From b3da913dfa6ffd1ef0f380363c9bf3791ddc7aad Mon Sep 17 00:00:00 2001 From: Lukas Nykryn Date: Sun, 9 Apr 2023 15:12:53 +0200 Subject: [PATCH 1/2] alternatives: isLink should return 0 in case of lstat error isLink returns non-zero value when a file is a symlink. However, the old code also returned -1 when lsat failed. --- alternatives.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/alternatives.c b/alternatives.c index 22ff2b41..13047e52 100644 --- a/alternatives.c +++ b/alternatives.c @@ -484,13 +484,10 @@ static int readConfig(struct alternativeSet *set, const char *title, static int isLink(char *path) { struct stat sbuf; - int rc = 0; - rc = lstat(path, &sbuf); - if (!rc) { - rc = S_ISLNK(sbuf.st_mode); - } - return rc; + if (lstat(path, &sbuf)) + return 0; + return !!S_ISLNK(sbuf.st_mode); } static int facilityBelongsToUs(char *facility, const char *altDir) { From a5f8e0b28c2e1376d91a612960bc6d0e876e9535 Mon Sep 17 00:00:00 2001 From: Lukas Nykryn Date: Sun, 9 Apr 2023 15:37:59 +0200 Subject: [PATCH 2/2] alternatives: --keep-foreign incorrectly handles non-existent files Normally alternatives will only refuse to create a link to /etc/alternatives/* if the file already exists and is not a symlink. But alternatives never checked if existing symlink goes to /etc/alternatives/. This is a weird behavior, but we did not want to change the default behavior so we added --keep-foreign option that is more strict and checks that the target if the symlink is in /etc/alternatives/. But we missed the corner-case where there is no file at all and incorrectly reported on error. Fixes #102 --- alternatives.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/alternatives.c b/alternatives.c index 13047e52..a2d3750d 100644 --- a/alternatives.c +++ b/alternatives.c @@ -490,6 +490,12 @@ static int isLink(char *path) { return !!S_ISLNK(sbuf.st_mode); } +static int fileExists(char *path) { + struct stat sbuf; + + return !stat(path, &sbuf); +} + static int facilityBelongsToUs(char *facility, const char *altDir) { char buf[PATH_MAX]; if (readlink(facility, buf, sizeof(buf)) <= 0) @@ -533,7 +539,17 @@ static int makeLinks(struct linkSet *l, const char *altDir, int flags) { sl = alloca(strlen(altDir) + strlen(l->title) + 2); sprintf(sl, "%s/%s", altDir, l->title); - if (isLink(l->facility) && (!FL_KEEP_FOREIGN(flags) || facilityBelongsToUs(l->facility, altDir)) ) { + if (fileExists(l->facility) && !isLink(l->facility)) { + fprintf( + stderr, + _("failed to link %s -> %s: %s exists and it is not a symlink\n"), + l->facility, sl, l->facility); + } else if (FL_KEEP_FOREIGN(flags) && isLink(l->facility) && !facilityBelongsToUs(l->facility, altDir)) { + fprintf( + stderr, + _("failed to link %s -> %s: --keep-foreign was set and link %s points outside %s\n"), + l->facility, sl, l->facility, altDir); + } else { if (FL_TEST(flags)) { printf(_("would link %s -> %s\n"), l->facility, sl); } else { @@ -550,11 +566,7 @@ static int makeLinks(struct linkSet *l, const char *altDir, int flags) { } } } - } else - fprintf( - stderr, - _("failed to link %s -> %s: %s exists and it is either not a symlink or --keep-foreign was set and link points outside %s\n"), - l->facility, sl, l->facility, altDir); + } if (FL_TEST(flags)) { printf(_("would link %s -> %s\n"), sl, l->target);