qfile: Detect matches with canonicalised target directory
On certain systems, e.g. merged-usr (/{,s}bin and /usr/sbin are symlinks to /usr/bin), files that are installed to symlinked directories (/sbin etc.) actually reside in /usr/bin. Previously, qfile would only resolve (canonicalise -- i.e. call realpath(3) on argument's directory) the argument's directory, but not the target's.
This resulted in qfile's inability to detect that /sbin/init in
sysvinit's vdb CONTENTS file refers to the same file /usr/bin/init,
since /sbin is never canonicalised to /usr/bin. As such, the
following line would not work on merged-usr systems, although
/sbin/init and /usr/bin/init point to the exact same files:
$ qfile /usr/bin/init
## No output ##
whereas
$ qfile /sbin/init
sys-apps/sysvinit: /sbin/init
(To be precise, qfile both resolved and did not resolve the argument's dirname, producing a match if either the resolved or non-resolved dirname matched).
Tests do pass. Additionally, this fixes a bug when ROOT is set. Previously, as far as I understand, it worked purely by accident:
https://github.com/gentoo/portage-utils/blob/1833294774ebfb4bcc1464e6ae28e71da0f6babf/qfile.c#L257-L278
This line
https://github.com/gentoo/portage-utils/blob/1833294774ebfb4bcc1464e6ae28e71da0f6babf/qfile.c#L272
is meant to compare ROOT-stripped target and source dirname. What it actually does, though, is comparing the full path (including the basename), which, obviously, yields false. The reason why qfile worked successfully with ROOT set is because this branch wasn't even taken. Due to the fact that dir_names contain unprefixed paths, a match is yielded a bit up, in these branches:
https://github.com/gentoo/portage-utils/blob/1833294774ebfb4bcc1464e6ae28e71da0f6babf/qfile.c#L236-L248
e->name holds the path that is being checked and dirname_len is the length of its directory component.
so what does your qfile do if you call it on /usr/sbin/init?
Outputs nothing. I'm not on my laptopt right now, I will add you some example queries in a few minutes.
Here is a sample.
$ ls -la /bin /sbin /usr/sbin
lrwxrwxrwx 1 root root 7 Sep 1 2024 /bin -> usr/bin
lrwxrwxrwx 1 root root 7 Sep 1 2024 /sbin -> usr/bin
lrwxrwxrwx 1 root root 3 Sep 1 2024 /usr/sbin -> bin
$ eselect profile show
Current /etc/portage/make.profile symlink:
default/linux/amd64/23.0/desktop
$ which init
/usr/bin/init
$ qfile /sbin/init
sys-apps/sysvinit: /sbin/init
$ qfile /usr/bin/init
$ qfile /usr/sbin/init
$ qfile /bin/init
$ which bash
/usr/bin/bash
$ qfile /bin/bash
app-shells/bash: /bin/bash
$ qfile /usr/bin/bash
$ qfile /sbin/bash
$ qfile /usr/sbin/bash
I have merged-usr layout.
$ equery --quiet uses --forced-mask baselayout
-build
(-split-usr)
qfile not working well with symlinks breaks the needrestart tool I use on my server. The tool restarts services, when the respective running binaries are updated. needrestart uses qfile to map binary to service in /etc/init.d. Since qfile fails on symlinks, sometimes needrestart is unable to find the respective service and restart the daemon.
Please, merge this PR, since the described use case is a very important part of keeping my server (and not only my, I suspect) up to date and secure.
what this requires is an option -L to qfile that tells it to follow symlinks, so needrestart can use that
The problem is not that symlinks are not resolved, but that the source directory (in case of sysvinit it is /sbin) is never canonicalised.
When qfile /usr/bin/init is executed (/usr/bin is not a symlink, but /sbin is) strcmp("/sbin", "/usr/bin") obviously fails. This commit canonicalises the source directory before calling strcmp, turning the source directory /sbin into /usr/bin.
/bin is a symlink to usr/bin (on my systems).
The package manager records for bash:
% grep bin /var/db/pkg/app-shells/bash-5.2_p37/CONTENTS
dir /bin
sym /bin/rbash -> bash 1728503901
obj /bin/bash 5ae743380531ae9ad35c990a262a1349 1728503904
dir /usr/bin
obj /usr/bin/bashbug 0f5183015431921d4bca5c0cde23848c 1728503900
That is, /bin/bash.
In order to match /usr/bin/bash onto app-shells/bash-5.2_p37, we'd have to expand all entries in CONTENTS files, as opposed to the source.
This is kind of awkward, and thus we should only do this on demand.
Do I have understood the problem correctly now?