From 8304de2e34e6977d3314920717ae5de8db8aff02 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Sat, 29 Mar 2003 12:44:01 +0000 Subject: [PATCH] * syscalls.cc: Slightly cleanup all utmp functions. (login): Use mutex to secure against concurrent access to wtmp file. (logout): Rewrite using POSIX calls. (utmp_fd): Initialized to -1 now. Any value < 0 is treated as closed in subsequent functions. (utmp_readonly): New variable, indicating utmp file open for reading only. (internal_setutent): New function implementing setutent(). (setutent): Call internal_setutent now. (endutent): Reset utmp_readonly. (getutent): Return immediately if utmp file can't be opened. (getutid): Ditto. (getutline): Ditto. (pututline): Ditto. Use mutex to secure against concurrent access to utmp file. --- winsup/cygwin/ChangeLog | 18 ++++ winsup/cygwin/syscalls.cc | 182 +++++++++++++++++++++++--------------- 2 files changed, 131 insertions(+), 69 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 3fe17e0fb..2ee102abf 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,21 @@ +2003-03-29 Corinna Vinschen + + * syscalls.cc: Slightly cleanup all utmp functions. + (login): Use mutex to secure against concurrent access to wtmp file. + (logout): Rewrite using POSIX calls. + (utmp_fd): Initialized to -1 now. Any value < 0 is treated as closed + in subsequent functions. + (utmp_readonly): New variable, indicating utmp file open for reading + only. + (internal_setutent): New function implementing setutent(). + (setutent): Call internal_setutent now. + (endutent): Reset utmp_readonly. + (getutent): Return immediately if utmp file can't be opened. + (getutid): Ditto. + (getutline): Ditto. + (pututline): Ditto. Use mutex to secure against concurrent access to + utmp file. + 2003-03-28 Christopher Faylor * Makefile.in: Remove EXE_LDFLAGS. Fix fhandler_CFLAGS typo. Recognize .s suffix. diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index b3ae5a0dc..230028017 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -2501,98 +2501,113 @@ login (struct utmp *ut) pututline (ut); endutent (); + /* Read/write to utmp must be atomic to prevent overriding data + by concurrent processes. */ + HANDLE mutex = CreateMutex (NULL, FALSE, shared_name ("wtmp_mutex", 0)); + if (mutex) + while (WaitForSingleObject (mutex, INFINITE) == WAIT_ABANDONED) + ; if ((fd = open (_PATH_WTMP, O_WRONLY | O_APPEND | O_BINARY, 0)) >= 0) { - (void) write (fd, (char *) ut, sizeof (struct utmp)); - (void) close (fd); + write (fd, ut, sizeof *ut); + close (fd); + } + if (mutex) + { + ReleaseMutex (mutex); + CloseHandle (mutex); } } -/* It isn't possible to use unix-style I/O function in logout code because -cygwin's I/O subsystem may be inaccessible at logout () call time. -FIXME (cgf): huh? -*/ extern "C" int logout (char *line) { sigframe thisframe (mainthread); - int res = 0; - HANDLE ut_fd; - static const char path_utmp[] = _PATH_UTMP; + struct utmp ut_buf, *ut; - path_conv win32_path (path_utmp); - if (win32_path.error) - return 0; - - ut_fd = CreateFile (win32_path.get_win32 (), - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - &sec_none_nih, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, NULL); - if (ut_fd != INVALID_HANDLE_VALUE) + memset (&ut_buf, 0, sizeof ut_buf); + strncpy (ut_buf.ut_line, line, sizeof ut_buf.ut_line); + setutent (); + ut = getutline (&ut_buf); + if (ut) { - struct utmp *ut; - struct utmp ut_buf[100]; - /* FIXME: utmp file access is not 64 bit clean for now. */ - __off32_t pos = 0; /* Position in file */ - DWORD rd; + int fd; - while (!res && ReadFile (ut_fd, ut_buf, sizeof ut_buf, &rd, NULL) - && rd != 0) + /* We can't use ut further since it's a pointer to the static utmp_data + area (see below) and would get overwritten in pututline(). So we + copy it back to the local ut_buf. */ + memcpy (&ut_buf, ut, sizeof ut_buf); + ut_buf.ut_type = DEAD_PROCESS; + memset (ut_buf.ut_user, 0, sizeof ut_buf.ut_user); + time (&ut_buf.ut_time); + /* Read/write to utmp must be atomic to prevent overriding data + by concurrent processes. */ + HANDLE mutex = CreateMutex (NULL, FALSE, shared_name ("wtmp_mutex", 0)); + if (mutex) + while (WaitForSingleObject (mutex, INFINITE) == WAIT_ABANDONED) + ; + if ((fd = open (_PATH_WTMP, O_WRONLY | O_APPEND | O_BINARY, 0)) >= 0) { - struct utmp *ut_end = (struct utmp *) ((char *) ut_buf + rd); - - for (ut = ut_buf; ut < ut_end; ut++, pos += sizeof (*ut)) - if (ut->ut_name[0] - && strncmp (ut->ut_line, line, sizeof (ut->ut_line)) == 0) - /* Found the entry for LINE; mark it as logged out. */ - { - /* Zero out entries describing who's logged in. */ - memset (ut->ut_name, 0, sizeof (ut->ut_name)); - memset (ut->ut_host, 0, sizeof (ut->ut_host)); - time (&ut->ut_time); - - /* Now seek back to the position in utmp at which UT occured, - and write the new version of UT there. */ - if ((SetFilePointer (ut_fd, pos, 0, FILE_BEGIN) != 0xFFFFFFFF) - && (WriteFile (ut_fd, (char *) ut, sizeof (*ut), - &rd, NULL))) - { - res = 1; - break; - } - } + write (fd, &ut_buf, sizeof ut_buf); + close (fd); } - - CloseHandle (ut_fd); + if (mutex) + { + ReleaseMutex (mutex); + CloseHandle (mutex); + } + memset (ut_buf.ut_line, 0, sizeof ut_buf.ut_line); + ut_buf.ut_time = 0; + pututline (&ut_buf); + endutent (); } - - return res; + return 1; } -static int utmp_fd = -2; +static int utmp_fd = -1; +static bool utmp_readonly = false; static char *utmp_file = (char *) _PATH_UTMP; static struct utmp utmp_data; +static void +internal_setutent (bool force_readwrite) +{ + sigframe thisframe (mainthread); + if (force_readwrite && utmp_readonly) + endutent (); + if (utmp_fd < 0) + { + utmp_fd = open (utmp_file, O_RDWR | O_BINARY); + /* If open fails, we assume an unprivileged process (who?). In this + case we try again for reading only unless the process calls + pututline() (==force_readwrite) in which case opening just fails. */ + if (utmp_fd < 0 && !force_readwrite) + { + utmp_fd = open (utmp_file, O_RDONLY | O_BINARY); + if (utmp_fd >= 0) + utmp_readonly = true; + } + } + else + lseek (utmp_fd, 0, SEEK_SET); +} + extern "C" void setutent () { - sigframe thisframe (mainthread); - if (utmp_fd == -2) - utmp_fd = open (utmp_file, O_RDWR); - else - lseek (utmp_fd, 0, SEEK_SET); + internal_setutent (false); } extern "C" void endutent () { sigframe thisframe (mainthread); - if (utmp_fd != -2) + if (utmp_fd >= 0) { close (utmp_fd); - utmp_fd = -2; + utmp_fd = -1; + utmp_readonly = false; } } @@ -2614,9 +2629,13 @@ extern "C" struct utmp * getutent () { sigframe thisframe (mainthread); - if (utmp_fd == -2) - setutent (); - if (read (utmp_fd, &utmp_data, sizeof (utmp_data)) != sizeof (utmp_data)) + if (utmp_fd < 0) + { + internal_setutent (false); + if (utmp_fd < 0) + return NULL; + } + if (read (utmp_fd, &utmp_data, sizeof utmp_data) != sizeof utmp_data) return NULL; return &utmp_data; } @@ -2627,7 +2646,13 @@ getutid (struct utmp *id) sigframe thisframe (mainthread); if (check_null_invalid_struct_errno (id)) return NULL; - while (read (utmp_fd, &utmp_data, sizeof (utmp_data)) == sizeof (utmp_data)) + if (utmp_fd < 0) + { + internal_setutent (false); + if (utmp_fd < 0) + return NULL; + } + while (read (utmp_fd, &utmp_data, sizeof utmp_data) == sizeof utmp_data) { switch (id->ut_type) { @@ -2658,12 +2683,18 @@ getutline (struct utmp *line) sigframe thisframe (mainthread); if (check_null_invalid_struct_errno (line)) return NULL; - while (read (utmp_fd, &utmp_data, sizeof (utmp_data)) == sizeof (utmp_data)) + if (utmp_fd < 0) + { + internal_setutent (false); + if (utmp_fd < 0) + return NULL; + } + while (read (utmp_fd, &utmp_data, sizeof utmp_data) == sizeof utmp_data) { if ((utmp_data.ut_type == LOGIN_PROCESS || utmp_data.ut_type == USER_PROCESS) && !strncmp (utmp_data.ut_line, line->ut_line, - sizeof (utmp_data.ut_line))) + sizeof utmp_data.ut_line)) return &utmp_data; } return NULL; @@ -2675,11 +2706,24 @@ pututline (struct utmp *ut) sigframe thisframe (mainthread); if (check_null_invalid_struct (ut)) return; - setutent (); + internal_setutent (true); + if (utmp_fd < 0) + return; + /* Read/write to utmp must be atomic to prevent overriding data + by concurrent processes. */ + HANDLE mutex = CreateMutex (NULL, FALSE, shared_name ("utmp_mutex", 0)); + if (mutex) + while (WaitForSingleObject (mutex, INFINITE) == WAIT_ABANDONED) + ; struct utmp *u; if ((u = getutid (ut))) - lseek (utmp_fd, -sizeof(struct utmp), SEEK_CUR); + lseek (utmp_fd, -sizeof *ut, SEEK_CUR); else lseek (utmp_fd, 0, SEEK_END); - (void) write (utmp_fd, (char *) ut, sizeof (struct utmp)); + write (utmp_fd, ut, sizeof *ut); + if (mutex) + { + ReleaseMutex (mutex); + CloseHandle (mutex); + } }