Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ include(CheckVariableExists)
set(OPTFUNCS
stpcpy stpncpy putenv mempcpy fdatasync lutimes mergesort
getauxval setprogname __progname syncfs sched_getaffinity unshare
secure_getenv __secure_getenv mremap strchrnul
secure_getenv __secure_getenv mremap strchrnul dlmopen dlinfo
)
set(REQFUNCS
mkstemp getcwd basename dirname realpath setenv unsetenv regcomp
Expand Down
101 changes: 99 additions & 2 deletions build/files.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#ifdef WITH_CAP
#include <sys/capability.h>
#endif
#include <sys/xattr.h>

#ifdef HAVE_LIBDW
#include <libelf.h>
Expand Down Expand Up @@ -55,6 +56,8 @@
#define DEBUG_ID_DIR "/usr/lib/debug/.build-id"
#define DEBUG_DWZ_DIR "/usr/lib/debug/.dwz"

#define XATTR_NAME_SOVERS "user.rpm_elf_so_version"

/**
*/
enum specfFlags_e {
Expand Down Expand Up @@ -105,6 +108,7 @@ struct FileListRec_s {
rpmVerifyFlags verifyFlags;
char *langs; /* XXX locales separated with | */
char *caps;
char *xattrs;

bool operator < (const FileListRec_s & other) const
{
Expand Down Expand Up @@ -136,6 +140,7 @@ typedef struct FileEntry_s {

ARGV_t langs;
char *caps;
char *xattrs;

/* these are only ever relevant for current entry */
unsigned devtype;
Expand Down Expand Up @@ -170,6 +175,7 @@ typedef struct FileList_s {
size_t buildRootLen;
int processingFailed;
int haveCaps;
int haveXattrs;
int largeFiles;
ARGV_t docDirs;
rpmBuildPkgFlags pkgFlags;
Expand Down Expand Up @@ -220,12 +226,16 @@ static void copyFileEntry(FileEntry src, FileEntry dest)
if (src->caps != NULL) {
dest->caps = xstrdup(src->caps);
}
if (src->xattrs != NULL) {
dest->xattrs = xstrdup(src->xattrs);
}
}

static void FileEntryFree(FileEntry entry)
{
argvFree(entry->langs);
free(entry->caps);
free(entry->xattrs);
memset(entry, 0, sizeof(*entry));
}

Expand Down Expand Up @@ -303,6 +313,7 @@ static VFA_t const verifyAttrs[] = {
{ "mode", RPMVERIFY_MODE },
{ "rdev", RPMVERIFY_RDEV },
{ "caps", RPMVERIFY_CAPS },
{ "xattrs", RPMVERIFY_XATTRS },
{ NULL, 0 }
};

Expand Down Expand Up @@ -1231,6 +1242,9 @@ static void genCpioListAndHeader(FileList fl, rpmSpec spec, Package pkg, int isS
if (fl->haveCaps) {
headerPutString(h, RPMTAG_FILECAPS, flp->caps);
}
if (fl->haveXattrs) {
headerPutString(h, RPMTAG_FILEXATTRS, flp->xattrs);
}

buf[0] = '\0';
if (S_ISREG(flp->fl_mode) && !(flp->flags & RPMFILE_GHOST))
Expand Down Expand Up @@ -1298,6 +1312,10 @@ static void genCpioListAndHeader(FileList fl, rpmSpec spec, Package pkg, int isS
rpmlibNeedsFeature(pkg, "FileCaps", "4.6.1-1");
}

if (fl->haveXattrs) {
rpmlibNeedsFeature(pkg, "FileXattrs", "4.6.1-1");
}

if (!isSrc && !rpmExpandNumeric("%{_noPayloadPrefix}"))
(void) rpmlibNeedsFeature(pkg, "PayloadFilesHavePrefix", "4.0-1");

Expand Down Expand Up @@ -1325,6 +1343,7 @@ static void FileRecordsFree(FileRecords & files)
free(rec.cpioPath);
free(rec.langs);
free(rec.caps);
free(rec.xattrs);
}
files.clear();
}
Expand Down Expand Up @@ -1556,6 +1575,12 @@ static rpmRC addFile(FileList fl, const char * diskPath,
flp->caps = xstrdup("");
}

if (fl->cur.xattrs) {
flp->xattrs = xstrdup(fl->cur.xattrs);
} else {
flp->xattrs = xstrdup("");
}

flp->flags = fl->cur.attrFlags;
flp->specdFlags = fl->cur.specdFlags;
flp->verifyFlags = fl->cur.verifyFlags;
Expand Down Expand Up @@ -1708,6 +1733,64 @@ static void argvAddAttr(ARGV_t *filesp, rpmfileAttrs attrs, const char *path)
free(line);
}

static int generateElfSoVers(FileList fl)
{
int rc = 0;
int i;
FileListRec flp;

/* What ABI version will be recorded? */
char *elf_so_version_macro = rpmExpand("user.rpm_elf_so_version=%{?_elf_so_version}", NULL);
if (elf_so_version_macro[sizeof("user.rpm_elf_so_version=")-1] == '\0') {
rc = 1;
rpmlog(RPMLOG_WARNING,
_("_elf_so_version macro not set, skipping elf-version generation\n"));
}

if (rc != 0) {
free(elf_so_version_macro);
return rc;
}

/* Save _elf_so_version for ELF shared objects in this package. */
for (i = 0, flp = fl->files.data(); i < fl->files.size(); 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;

/* Only save elf_so_version executable files in the main package. */
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_DYN should have _elf_so_version. */
GElf_Ehdr ehdr;
#ifdef HAVE_DWELF_ELF_BEGIN
Elf *elf = dwelf_elf_begin (fd);
#else
Elf *elf = elf_begin (fd, ELF_C_READ, NULL);
#endif
if (elf != NULL && elf_kind (elf) == ELF_K_ELF
&& gelf_getehdr (elf, &ehdr) != NULL
&& (ehdr.e_type == ET_DYN)) {
flp->xattrs = xstrdup(elf_so_version_macro);
// fsetxattr(fd, XATTR_NAME_SOVERS,
// elf_so_version_macro, strlen(elf_so_version_macro), 0);
}
elf_end (elf);
close (fd);
}
}
}
free(elf_so_version_macro);
return rc;
}

#ifdef HAVE_LIBDW
/* How build id links are generated. See macros.in for description. */
#define BUILD_IDS_NONE 0
Expand Down Expand Up @@ -2540,6 +2623,8 @@ static void addPackageFileList (struct FileList_s *fl, Package pkg,

if (fl->cur.caps)
fl->haveCaps = 1;
if (fl->cur.xattrs)
fl->haveXattrs = 1;
}
argvFree(fileNames);
}
Expand Down Expand Up @@ -2582,13 +2667,25 @@ static rpmRC processPackageFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags,
if (fl.processingFailed)
goto exit;

#ifdef HAVE_LIBDW
{
/* Check build-ids and add build-ids links for files to package list. */
const char *arch = headerGetString(pkg->header, RPMTAG_ARCH);
if (rpmExpandNumeric("%{?__debug_package}") && !rstreq(arch, "noarch")) {
/* Go through the current package list and generate a files list. */
ARGV_t idFiles = NULL;
/* Go through the current package list and tag shared object files. */
if (generateElfSoVers (&fl) != 0) {
rpmlog(RPMLOG_ERR, _("Generating elf-so-version failed\n"));
fl.processingFailed = 1;
goto exit;
}

if (fl.processingFailed)
goto exit;

#ifdef HAVE_LIBDW
/* Check build-ids and add build-ids links for files to package list. */
/* Go through the current package list and generate a files list. */
if (generateBuildIDs (&fl, &idFiles) != 0) {
rpmlog(RPMLOG_ERR, _("Generating build-id links failed\n"));
fl.processingFailed = 1;
Expand All @@ -2605,8 +2702,8 @@ static rpmRC processPackageFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags,
if (fl.processingFailed)
goto exit;
}
}
#endif
}

/* Verify that file attributes scope over hardlinks correctly. */
if (checkHardLinks(fl.files))
Expand Down
2 changes: 2 additions & 0 deletions config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#cmakedefine HAVE_DIRENT_H @HAVE_DIRENT_H@
#cmakedefine HAVE_DIRNAME @HAVE_DIRNAME@
#cmakedefine HAVE_DLFCN_H @HAVE_DLFCN_H@
#cmakedefine HAVE_DLINFO @HAVE_DLINFO@
#cmakedefine HAVE_DLMOPEN @HAVE_DLMOPEN@
#cmakedefine HAVE_DSA_SET0_KEY @HAVE_DSA_SET0_KEY@
#cmakedefine HAVE_DSA_SET0_PQG @HAVE_DSA_SET0_PQG@
#cmakedefine HAVE_DSA_SIG_SET0 @HAVE_DSA_SIG_SET0@
Expand Down
8 changes: 6 additions & 2 deletions docs/manual/more_dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,16 @@ This has two "side-effects":
It's a fairly common mistake to replace legacy PreReq dependencies with Requires(pre), but this is not the same, due to the latter point above!

## Automatic Dependencies
To reduce the amount of work required by the package builder, RPM scans the file list of a package when it is being built. Any files in the file list which require shared libraries to work (as determined by ldd) cause that package to require the shared library.
To reduce the amount of work required by the package builder, RPM scans the file list of a package when it is being built. Any files in the file list which require shared libraries to work (as determined by elfdeps for ELF objects) cause that package to require the shared library.

For example, if your package contains /bin/vi, RPM will add dependencies for both libtermcap.so.2 and libc.so.5. These are treated as virtual packages, so no version numbers are used.
For example, if your package contains /bin/vi, RPM will add dependencies for both libtermcap.so.2 and libc.so.5. These are treated as virtual packages.

A similar process allows RPM to add Provides information automatically. Any shared library in the file list is examined for its soname (the part of the name which must match for two shared libraries to be considered equivalent) and that soname is automatically provided by the package. For example, the libc-5.3.12 package has provides information added for libm.so.5 and libc.so.5. We expect this automatic dependency generation to eliminate the need for most packages to use explicit Requires: lines.

As of rpm version (TBD), the internal dependency generator provides optional support for generating versioned ELF object dependencies on objects that do not provide versioned symbols. Distributions that wish to make use of this support should enable the \_elf\_provide\_fallback\_versions and \_elf\_require\_fallback\_versions macros. When these macros are enabled, the dependency generator will attempt to resolve shared object dependencies to a full path. If the shared object does not provide versioned symbols, and the soname is a symlink to a full name that differs from the soname, and the full name ends in ".so." followed by a sequence of numbers optionally separated by the '.' character, then that trailing sequence will be treated as a version and included in the automatically generated dependencies. While shared objects with versioned symbols are well supported, and are the preferred approach to ensuring that a dependency actually provides the ABI that another package requires, this fallback approach to versioning dependencies provides reasonably good coverage for the large body of ELF shared objects that do not yet maintain versioned symbols.

The most likely case where the fallback version system will break is a shared object being updated from a purely numeric version suffix to a version suffix with non-numeric characters (e.g. "libfoo.so.3.0.1" is updated to "libfoo.so.3.0.1a"). In that case, the dependency generator would not provide a version for that shared object, and the package maintainer would need to manually add a Provides: tag to the package in order to continue satisfying existing package dependencies.

## Custom Automatic Dependency
Customizing automatic dependency generation is covered in [dependency generator documentation]().

Expand Down
4 changes: 2 additions & 2 deletions fileattrs/elf.attr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
%__elf_provides %{_rpmconfigdir}/elfdeps --provides --multifile
%__elf_requires %{_rpmconfigdir}/elfdeps --requires --multifile
%__elf_provides %{_rpmconfigdir}/elfdeps %{?_elf_provide_fallback_versions} --provides --multifile
%__elf_requires %{_rpmconfigdir}/elfdeps %{?_elf_require_fallback_versions} --requires --multifile
%__elf_magic ^(setuid,? )?(setgid,? )?(sticky )?ELF (32|64)-bit.*$
%__elf_exclude_path ^/lib/modules/.*\\.ko?(\\.[[:alnum:]]*)$
%__elf_protocol multifile
1 change: 1 addition & 0 deletions include/rpm/rpmarchive.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ enum rpmfilesErrorCodes {
RPMERR_LSETFCON_FAILED = -32786,
RPMERR_SETCAP_FAILED = -32787,
RPMERR_CLOSE_FAILED = -32788,
RPMERR_SETXATTR_FAILED = -32789,
};

#ifdef __cplusplus
Expand Down
9 changes: 9 additions & 0 deletions include/rpm/rpmfi.h
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,15 @@ const char * rpmfiFGroup(rpmfi fi);
*/
const char * rpmfiFCaps(rpmfi fi);

/** \ingroup rpmfi
* Return textual representation of current file extended attributes
* from file info set iterator.
* @param fi file info set iterator
* @return file extended attribute description, "" for no attributes
* and NULL on invalid
*/
const char * rpmfiFXattrs(rpmfi fi);

/** \ingroup rpmfi
* Return current file language(s) from file info set iterator.
* @param fi file info set iterator
Expand Down
3 changes: 2 additions & 1 deletion include/rpm/rpmfiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ enum rpmVerifyAttrs_e {
RPMVERIFY_MODE = (1 << 6), /*!< from %verify(mode) */
RPMVERIFY_RDEV = (1 << 7), /*!< from %verify(rdev) */
RPMVERIFY_CAPS = (1 << 8), /*!< from %verify(caps) */
/* bits 9-14 unused, reserved for rpmVerifyAttrs */
RPMVERIFY_XATTRS = (1 << 9), /*!< from %verify(xattrs) */
/* bits 10-14 unused, reserved for rpmVerifyAttrs */
RPMVERIFY_CONTEXTS = (1 << 15), /*!< verify: from --nocontexts */
/* bits 16-22 used in rpmVerifyFlags */
/* bits 23-27 used in rpmQueryFlags */
Expand Down
1 change: 1 addition & 0 deletions include/rpm/rpmtag.h
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ typedef enum rpmTag_e {
RPMTAG_PACKAGEDIGESTS = 5118, /* s[] */
RPMTAG_PACKAGEDIGESTALGOS = 5119, /* i[] */
RPMTAG_SOURCENEVR = 5120, /* s */
RPMTAG_FILEXATTRS = 5121, /* s */

RPMTAG_FIRSTFREE_TAG /*!< internal */
} rpmTag;
Expand Down
49 changes: 49 additions & 0 deletions lib/fsm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#ifdef WITH_CAP
#include <sys/capability.h>
#endif
#include <sys/xattr.h>

#include <rpm/rpmte.h>
#include <rpm/rpmts.h>
Expand Down Expand Up @@ -108,6 +109,17 @@ static int cap_set_fileat(int dirfd, const char *path, cap_t fcaps)
}
#endif

static int xattr_set_fileat(int dirfd, const char *path, const char *name, const char *val, size_t size, int flags)
{
int fd = -1;
int rc = fsmOpenat(&fd, dirfd, path, O_RDONLY|O_NOFOLLOW, 0);
if (!rc) {
rc = fsetxattr(fd, name, val, size, flags);
fsmClose(&fd);
}
return rc;
}

static int fsmSetFCaps(int fd, int dirfd, const char *path, const char *captxt)
{
int rc = 0;
Expand All @@ -134,6 +146,39 @@ static int fsmSetFCaps(int fd, int dirfd, const char *path, const char *captxt)
return rc;
}

static int fsmSetFXattrs(int fd, int dirfd, const char *path, const char *xattrstxt)
{
int rc = 0;

if (xattrstxt && *xattrstxt != '\0') {
char *eqidx, *xattrname, *xattrval = NULL;

xattrname = xstrdup(xattrstxt);
eqidx = (char *) strchr(xattrstxt, '=');
if (eqidx == NULL)
rc = RPMERR_SETXATTR_FAILED;
else {
*eqidx = '\0';
xattrval = xstrdup(eqidx+1);

if (fd >= 0) {
if (xattrval == NULL || fsetxattr(fd, xattrname, xattrval, strlen(xattrval), 0))
rc = RPMERR_SETXATTR_FAILED;
} else {
if (xattrval == NULL || xattr_set_fileat(dirfd, path, xattrname, xattrval, strlen(xattrval), 0))
rc = RPMERR_SETXATTR_FAILED;
}
}
if (_fsm_debug) {
rpmlog(RPMLOG_DEBUG, " %8s (%d - %d %s, %s) %s\n", __func__,
fd, dirfd, path, xattrstxt, (rc < 0 ? strerror(errno) : ""));
}
free(xattrname);
free(xattrval);
}
return rc;
}

static int fsmClose(int *wfdp)
{
int rc = 0;
Expand Down Expand Up @@ -754,6 +799,10 @@ static int fsmSetmeta(int fd, int dirfd, const char *path,
if (!rc && !nofcaps && S_ISREG(st->st_mode) && !getuid()) {
rc = fsmSetFCaps(fd, dirfd, path, rpmfiFCaps(fi));
}
/* Set file extended attributes (if enabled) */
if (!rc && S_ISREG(st->st_mode)) {
rc = fsmSetFXattrs(fd, dirfd, path, rpmfiFXattrs(fi));
}
if (!rc) {
rc = fsmUtime(fd, dirfd, path, st->st_mode, rpmfiFMtime(fi));
}
Expand Down
3 changes: 3 additions & 0 deletions lib/rpmds.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1085,6 +1085,9 @@ static const struct rpmlibProvides_s rpmlibProvides[] = {
( RPMSENSE_EQUAL),
N_("support for POSIX.1e file capabilities") },
#endif
{ "rpmlib(FileXattrs)", "4.6.1-1",
( RPMSENSE_EQUAL),
N_("support for file extended attributes.") },
{ "rpmlib(ScriptletExpansion)", "4.9.0-1",
( RPMSENSE_EQUAL),
N_("package scriptlets can be expanded at install time.") },
Expand Down
Loading