From e6c1d4b8bd9610444d9fb2d145610f46c881c210 Mon Sep 17 00:00:00 2001 From: David Seifert Date: Wed, 9 Oct 2024 19:06:05 +0200 Subject: [PATCH 1/2] ${pcfiledir} should point to parent dir of actual file, not symlink * In situations where we have a real /foo.pc that uses ${pcfiledir} and a symlink /foo.pc that points to /foo.pc, then ${pcfiledir} should resolve to and not . --- libpkgconf/pkg.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/libpkgconf/pkg.c b/libpkgconf/pkg.c index feb8d9a..c05be2c 100644 --- a/libpkgconf/pkg.c +++ b/libpkgconf/pkg.c @@ -17,6 +17,15 @@ #include #include +#ifndef _WIN32 +#include // open +#include // basename/dirname +#include // lstat, S_ISLNK +#include // close, readlinkat + +#include +#endif + /* * !doc * @@ -63,6 +72,65 @@ pkg_get_parent_dir(pkgconf_pkg_t *pkg) char buf[PKGCONF_ITEM_SIZE], *pathbuf; pkgconf_strlcpy(buf, pkg->filename, sizeof buf); +#ifndef _WIN32 + /* + * We want to resolve symlinks, since ${pcfiledir} should point to the + * parent of the file symlinked to. + */ + struct stat path_stat; + while (!lstat(buf, &path_stat) && S_ISLNK(path_stat.st_mode)) + { + /* + * Have to split the path into the dir + file components, + * in order to extract the directory file descriptor. + * + * The nomenclature here uses the + * + * ln + * + * model. + */ + char basenamebuf[PKGCONF_ITEM_SIZE]; + pkgconf_strlcpy(basenamebuf, buf, sizeof(basenamebuf)); + const char* targetfilename = basename(basenamebuf); + + char dirnamebuf[PKGCONF_ITEM_SIZE]; + pkgconf_strlcpy(dirnamebuf, buf, sizeof(dirnamebuf)); + const char* targetdir = dirname(dirnamebuf); + + const int dirfd = open(targetdir, O_DIRECTORY); + if (dirfd == -1) + break; + + char sourcebuf[PKGCONF_ITEM_SIZE]; + ssize_t len = readlinkat(dirfd, targetfilename, sourcebuf, sizeof(sourcebuf) - 1); + close(dirfd); + + if (len == -1) + break; + sourcebuf[len] = '\0'; + + memset(buf, '\0', sizeof buf); + /* + * The logic here can be a bit tricky, so here's a table: + * + * | | result + * ----------------------------------------------------------------------- + * /bar (absolute) | foo/link (relative) | /bar (absolute) + * ../bar (relative) | foo/link (relative) | foo/../bar (relative) + * /bar (absolute) | /foo/link (absolute) | /bar (absolute) + * ../bar (relative) | /foo/link (absolute) | /foo/../bar (relative) + */ + if ((sourcebuf[0] != '/') /* absolute path in wins */ + && (strcmp(targetdir, "."))) /* do not prepend "." */ + { + pkgconf_strlcat(buf, targetdir, sizeof buf); + pkgconf_strlcat(buf, "/", sizeof buf); + } + pkgconf_strlcat(buf, sourcebuf, sizeof buf); + } +#endif + pathbuf = strrchr(buf, PKG_DIR_SEP_S); if (pathbuf == NULL) pathbuf = strrchr(buf, '/'); From ff51052e1cb8a53f27f119ba26d6353ef94fb5e0 Mon Sep 17 00:00:00 2001 From: David Seifert Date: Wed, 9 Oct 2024 19:06:06 +0200 Subject: [PATCH 2/2] add test for symlinked ${pcfiledir} --- .gitignore | 1 + Makefile.am | 2 + tests/Kyuafile.in | 1 + tests/lib1/pcfiledir.pc | 8 +++ tests/meson.build | 3 +- tests/symlink.sh | 122 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 tests/lib1/pcfiledir.pc create mode 100755 tests/symlink.sh diff --git a/.gitignore b/.gitignore index 558e11a..e848fb8 100644 --- a/.gitignore +++ b/.gitignore @@ -73,6 +73,7 @@ core /tests/provides /tests/regress /tests/requires +/tests/symlink /tests/sysroot /tests/test_env.sh /tests/version diff --git a/Makefile.am b/Makefile.am index 0d96500..661aaa6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -107,6 +107,7 @@ EXTRA_DIST = pkg.m4 \ tests/lib1/tuple-quoting.pc \ tests/lib1/empty-tuple.pc \ tests/lib1/orphaned-requires-private.pc \ + tests/lib1/pcfiledir.pc \ tests/lib1/sysroot-dir-2.pc \ tests/lib1/sysroot-dir-3.pc \ tests/lib1/sysroot-dir-4.pc \ @@ -137,6 +138,7 @@ test_scripts= tests/meson.build \ tests/provides.sh \ tests/regress.sh \ tests/requires.sh \ + tests/symlink.sh \ tests/sysroot.sh \ tests/version.sh diff --git a/tests/Kyuafile.in b/tests/Kyuafile.in index 0b53da7..f50b749 100644 --- a/tests/Kyuafile.in +++ b/tests/Kyuafile.in @@ -11,3 +11,4 @@ atf_test_program{name='conflicts'} atf_test_program{name='version'} atf_test_program{name='framework'} atf_test_program{name='provides'} +atf_test_program{name='symlink'} diff --git a/tests/lib1/pcfiledir.pc b/tests/lib1/pcfiledir.pc new file mode 100644 index 0000000..fa6ada9 --- /dev/null +++ b/tests/lib1/pcfiledir.pc @@ -0,0 +1,8 @@ +prefix=${pcfiledir} +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: foo +Description: A testing pkg-config file with a ${pcfiledir} +Version: 1.2.3 diff --git a/tests/meson.build b/tests/meson.build index 39ceeeb..56406c3 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -11,8 +11,9 @@ tests = [ 'provides', 'regress', 'requires', + 'symlink', 'sysroot', - 'version' + 'version', ] diff --git a/tests/symlink.sh b/tests/symlink.sh new file mode 100755 index 0000000..7f49655 --- /dev/null +++ b/tests/symlink.sh @@ -0,0 +1,122 @@ +#!/usr/bin/env atf-sh + +. $(atf_get_srcdir)/test_env.sh + +tests_init \ + pcfiledir_symlink_absolute \ + pcfiledir_symlink_relative + +# - We need to create a temporary subtree, since symlinks are not preserved +# in "make dist". +# - ${srcdir} is relative and since we need to compare paths, we would have +# to portably canonicalize it again, which is hard. Instead, just keep +# the whole thing nested. +pcfiledir_symlink_absolute_body() +{ + mkdir -p tmp/child + cp -f "${selfdir}/lib1/pcfiledir.pc" tmp/child/ + ln -f -s "${PWD}/tmp/child/pcfiledir.pc" tmp/pcfiledir.pc # absolute + ln -f -s tmp/pcfiledir.pc pcfiledir.pc + + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix pcfiledir.pc + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix tmp/pcfiledir.pc + atf_check \ + -o inline:"tmp/child\n" \ + pkgconf --variable=prefix tmp/child/pcfiledir.pc + + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix "${PWD}/pcfiledir.pc" + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix "${PWD}/tmp/pcfiledir.pc" + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix "${PWD}/tmp/child/pcfiledir.pc" + + export PKG_CONFIG_PATH="." + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix pcfiledir + export PKG_CONFIG_PATH="${PWD}" + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix pcfiledir + + export PKG_CONFIG_PATH="tmp" + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix pcfiledir + export PKG_CONFIG_PATH="${PWD}/tmp" + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix pcfiledir + + export PKG_CONFIG_PATH="tmp/child" + atf_check \ + -o inline:"tmp/child\n" \ + pkgconf --variable=prefix pcfiledir + export PKG_CONFIG_PATH="${PWD}/tmp/child" + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix pcfiledir +} + +pcfiledir_symlink_relative_body() +{ + mkdir -p tmp/child + cp -f "${selfdir}/lib1/pcfiledir.pc" tmp/child/ + ln -f -s child/pcfiledir.pc tmp/pcfiledir.pc # relative + ln -f -s tmp/pcfiledir.pc pcfiledir.pc + + atf_check \ + -o inline:"tmp/child\n" \ + pkgconf --variable=prefix pcfiledir.pc + atf_check \ + -o inline:"tmp/child\n" \ + pkgconf --variable=prefix tmp/pcfiledir.pc + atf_check \ + -o inline:"tmp/child\n" \ + pkgconf --variable=prefix tmp/child/pcfiledir.pc + + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix "${PWD}/pcfiledir.pc" + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix "${PWD}/tmp/pcfiledir.pc" + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix "${PWD}/tmp/child/pcfiledir.pc" + + export PKG_CONFIG_PATH="." + atf_check \ + -o inline:"tmp/child\n" \ + pkgconf --variable=prefix pcfiledir + export PKG_CONFIG_PATH="${PWD}" + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix pcfiledir + + export PKG_CONFIG_PATH="tmp" + atf_check \ + -o inline:"tmp/child\n" \ + pkgconf --variable=prefix pcfiledir + export PKG_CONFIG_PATH="${PWD}/tmp" + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix pcfiledir + + export PKG_CONFIG_PATH="tmp/child" + atf_check \ + -o inline:"tmp/child\n" \ + pkgconf --variable=prefix pcfiledir + export PKG_CONFIG_PATH="${PWD}/tmp/child" + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix pcfiledir +}