I got a problem report from Mark Willson:
"I recently installed mg (via the Debian package) under WSL on Windows
10.
I found that the 'backup-to-home-directory' option didn't work.
The cause of this appears to be that getlogin under WSL returns NULL,
probably due to my use of wsltty to invoke bash. The patch below fixes
the issue for me:"
[snip]
- if ((un = getlogin()) != NULL)
+ if ((un = getenv("LOGNAME")) != NULL)
[snip]
Which put me onto the track of what was going on. I found the
following in the Linux manpage:
BUGS
Unfortunately, it is often rather easy to fool getlogin().
Sometimes
it does not work at all, because some program messed up the utmp
file.
Often, it gives only the first 8 characters of the login name.
The
user currently logged in on the controlling terminal of our
program
need not be the user who started it. Avoid getlogin() for
security-
related purposes.
Note that glibc does not follow the POSIX specification and uses
stdin
instead of /dev/tty. A bug. (Other recent systems, like SunOS 5.8
and
HP-UX 11.11 and FreeBSD 4.8 all return the login name also when
stdin
is redirected.)
Nobody knows precisely what cuserid() does; avoid it in portable
pro‐
grams. Or avoid it altogether: use getpwuid(geteuid()) instead,
if
that is what you meant. Do not use cuserid().
So I started looking at the code and rewrote it a bit, which I think
makes it more portable and removes a syscall in the process. I do
suspect this can be written even more elegantly, but didn't want to
rework the code too much.
I also took the liberty to remove some whitespace.
Index: fileio.c
===================================================================
RCS file: /cvs/src/usr.bin/mg/fileio.c,v
retrieving revision 1.104
@@ -703,7 +706,7 @@ expandtilde(const char *fn)
struct stat statbuf;
const char *cp;
char user[LOGIN_NAME_MAX], path[NFILEN];
- char *un, *ret;
+ char *ret;
size_t ulen, plen;
path[0] = '\0';
@@ -722,21 +725,21 @@ expandtilde(const char *fn)
return (NULL);
return(ret);
}
+ pw = getpwuid(geteuid());
if (ulen == 0) { /* ~/ or ~ */
- if ((un = getlogin()) != NULL)
- (void)strlcpy(user, un, sizeof(user));
+ if (pw != NULL)
+ (void)strlcpy(user, pw->pw_name, sizeof(user));
else
user[0] = '\0';
} else { /* ~user/ or ~user */
memcpy(user, &fn[1], ulen);
user[ulen] = '\0';
}
- pw = getpwnam(user);
if (pw != NULL) {
plen = strlcpy(path, pw->pw_dir, sizeof(path));
if (plen == 0 || path[plen - 1] != '/') {
if (strlcat(path, "/", sizeof(path)) >=
sizeof(path)) {
- dobeep();
+ dobeep();
ewprintf("Path too long");
return (NULL);
}