Dear Maintainer,
I have found that if ~/.xscreensaver is a symbolic link to another file, running
xscreensaver-settings and changing one or more settings before exiting will
cause it to remove the symlink entirely and write a new, regular (non-symlink)
~/.xscreensaver file, rather than resolving the symlink and writing to its
target as one would expect.
Steps to reproduce:
1. Create any valid .xscreensaver file, as by invoking xscreensaver-settings
2. `mv .xscreensaver xscreensaver-target`
3. `ln -s xscreensaver-target .xscreensaver`
4. Run xscreensaver-settings, change a setting (e.g. increase the "Blank After"
time by 1 minute), and close the application
Expected behavior:
- .xscreensaver is still a symlink to xscreensaver-target
- xscreensaver-target has been updated with the new settings
Observed behavior:
- .xscreensaver is now a regular file with the new settings
- xscreensaver-target is unchanged
The strange thing is that the xscreensaver code purports to already handle this
case, via the chase_symlinks function in driver/prefsw.c, and none of the Debian
patches for this package affect that part of the code. Additionally, I have
locally built the Debian version of 6.06 with all patches applied, and running
that version of xscreensaver-settings works as expected -- the new settings are
written to the symlink target properly. Therefore I conclude this is not a bug
in the upstream code itself, but in the Debian-provided binary.
The portions of driver/prefsw.c that appear to be relevant here are:
/* don't use realpath() on fedora system */
#ifdef _FORTIFY_SOURCE
# undef HAVE_REALPATH
#endif
and
static char *
chase_symlinks (const char *file)
{
# ifdef HAVE_REALPATH
if (file)
{
# ifndef PATH_MAX
# ifdef MAXPATHLEN
# define PATH_MAX MAXPATHLEN
# else
# define PATH_MAX 2048
# endif
# endif
char buf[PATH_MAX];
if (realpath (file, buf))
return strdup (buf);
/* sprintf (buf, "%.100s: realpath %.200s", blurb(), file);
perror(buf);*/
}
# endif /* HAVE_REALPATH */
return 0;
}
I hazard a guess that HAVE_REALPATH is not being set by the configure script, or
possibly _FORTIFY_SOURCE is being set somewhere, as either of those cases would
cause the observed behavior.
Additional supporting observations:
- `objdump -T /usr/bin/xscreensaver-settings` does not mention realpath(3) at
all, nor does the 6.09+dfsg1-1 binary from testing, implying that it was
ifdef'd out at compile time. In contrast, my (working) locally-built binary
does reference it:
$ objdump -T driver/xscreensaver-settings | grep realpath
0000000000000000 DF *UND* 0000000000000000 (GLIBC_2.3) realpath
- `strace /usr/bin/xscreensaver-settings` shows no sign of readlink(2) being
called when performing the reproducer steps above, whereas my locally-built
binary does:
$ strace driver/xscreensaver-settings 2>&1 | grep readlink
readlink("/home", 0x7ffecdde1c90, 1023) = -1 EINVAL (Invalid argument)
readlink("/home/wisnij", 0x7ffecdde1c90, 1023) = -1 EINVAL (Invalid argument)
readlink("/home/wisnij/.xscreensaver", "code/dotfiles/linux/.xscreensave"..., 1023) = 33
[etc...]