X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/ffe832ea680b4820f5ff399191f7f2d41350ee2e..ff5dec5cd103f6a9b030d295b014f0ff81025def:/src/w32.c diff --git a/src/w32.c b/src/w32.c index 3107af8f4a..2ff344abd6 100644 --- a/src/w32.c +++ b/src/w32.c @@ -1,13 +1,13 @@ /* Utility and Unix shadow routines for GNU Emacs on the Microsoft W32 API. - Copyright (C) 1994, 1995, 2000, 2001, 2002, 2003, 2004, - 2005, 2006, 2007, 2008 Free Software Foundation, Inc. + Copyright (C) 1994, 1995, 2000, 2001, 2002, 2003, 2004, 2005, 2006, + 2007, 2008, 2009, 2010 Free Software Foundation, Inc. This file is part of GNU Emacs. -GNU Emacs is free software; you can redistribute it and/or modify +GNU Emacs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3, or (at your option) -any later version. +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. GNU Emacs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -15,15 +15,15 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with GNU Emacs; see the file COPYING. If not, write to -the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -Boston, MA 02110-1301, USA. +along with GNU Emacs. If not, see . */ +/* Geoff Voelker (voelker@cs.washington.edu) 7-29-94 */ #include /* for offsetof */ #include #include +#include /* for DBL_EPSILON */ #include #include #include @@ -33,6 +33,8 @@ Boston, MA 02110-1301, USA. #include #include #include /* for _mbspbrk */ +#include +#include /* must include CRT headers *before* config.h */ @@ -73,9 +75,41 @@ Boston, MA 02110-1301, USA. #define _ANONYMOUS_STRUCT #endif #include +/* Some versions of compiler define MEMORYSTATUSEX, some don't, so we + use a different name to avoid compilation problems. */ +typedef struct _MEMORY_STATUS_EX { + DWORD dwLength; + DWORD dwMemoryLoad; + DWORDLONG ullTotalPhys; + DWORDLONG ullAvailPhys; + DWORDLONG ullTotalPageFile; + DWORDLONG ullAvailPageFile; + DWORDLONG ullTotalVirtual; + DWORDLONG ullAvailVirtual; + DWORDLONG ullAvailExtendedVirtual; +} MEMORY_STATUS_EX,*LPMEMORY_STATUS_EX; + #include #include +#include +#include +/* This either is not in psapi.h or guarded by higher value of + _WIN32_WINNT than what we use. */ +typedef struct _PROCESS_MEMORY_COUNTERS_EX { + DWORD cb; + DWORD PageFaultCount; + DWORD PeakWorkingSetSize; + DWORD WorkingSetSize; + DWORD QuotaPeakPagedPoolUsage; + DWORD QuotaPagedPoolUsage; + DWORD QuotaPeakNonPagedPoolUsage; + DWORD QuotaNonPagedPoolUsage; + DWORD PagefileUsage; + DWORD PeakPagefileUsage; + DWORD PrivateUsage; +} PROCESS_MEMORY_COUNTERS_EX,*PPROCESS_MEMORY_COUNTERS_EX; + #ifdef HAVE_SOCKETS /* TCP connection support, if kernel can do it */ #include #undef socket @@ -101,15 +135,28 @@ Boston, MA 02110-1301, USA. #include "ndir.h" #include "w32heap.h" #include "systime.h" +#include "dispextern.h" /* for xstrcasecmp */ +#include "coding.h" /* for Vlocale_coding_system */ + +/* For serial_configure and serial_open. */ +#include "process.h" +/* From process.c */ +extern Lisp_Object QCport, QCspeed, QCprocess; +extern Lisp_Object QCbytesize, QCstopbits, QCparity, Qodd, Qeven; +extern Lisp_Object QCflowcontrol, Qhw, Qsw, QCsummary; typedef HRESULT (WINAPI * ShGetFolderPath_fn) (IN HWND, IN int, IN HANDLE, IN DWORD, OUT char *); void globals_of_w32 (); +static DWORD get_rid (PSID); extern Lisp_Object Vw32_downcase_file_names; extern Lisp_Object Vw32_generate_fake_inodes; extern Lisp_Object Vw32_get_true_file_attributes; +/* Defined in process.c for its own purpose. */ +extern Lisp_Object Qlocal; + extern int w32_num_mouse_buttons; @@ -129,6 +176,25 @@ static BOOL g_b_init_lookup_account_sid; static BOOL g_b_init_get_sid_identifier_authority; static BOOL g_b_init_get_sid_sub_authority; static BOOL g_b_init_get_sid_sub_authority_count; +static BOOL g_b_init_get_file_security; +static BOOL g_b_init_get_security_descriptor_owner; +static BOOL g_b_init_get_security_descriptor_group; +static BOOL g_b_init_is_valid_sid; +static BOOL g_b_init_create_toolhelp32_snapshot; +static BOOL g_b_init_process32_first; +static BOOL g_b_init_process32_next; +static BOOL g_b_init_open_thread_token; +static BOOL g_b_init_impersonate_self; +static BOOL g_b_init_revert_to_self; +static BOOL g_b_init_get_process_memory_info; +static BOOL g_b_init_get_process_working_set_size; +static BOOL g_b_init_global_memory_status; +static BOOL g_b_init_global_memory_status_ex; +static BOOL g_b_init_get_length_sid; +static BOOL g_b_init_equal_sid; +static BOOL g_b_init_copy_sid; +static BOOL g_b_init_get_native_system_info; +static BOOL g_b_init_get_system_times; /* BEGIN: Wrapper functions around OpenProcessToken @@ -157,8 +223,10 @@ GetProcessTimes_Proc get_process_times_fn = NULL; #ifdef _UNICODE const char * const LookupAccountSid_Name = "LookupAccountSidW"; +const char * const GetFileSecurity_Name = "GetFileSecurityW"; #else const char * const LookupAccountSid_Name = "LookupAccountSidA"; +const char * const GetFileSecurity_Name = "GetFileSecurityA"; #endif typedef BOOL (WINAPI * LookupAccountSid_Proc) ( LPCTSTR lpSystemName, @@ -175,6 +243,67 @@ typedef PDWORD (WINAPI * GetSidSubAuthority_Proc) ( DWORD n); typedef PUCHAR (WINAPI * GetSidSubAuthorityCount_Proc) ( PSID pSid); +typedef BOOL (WINAPI * GetFileSecurity_Proc) ( + LPCTSTR lpFileName, + SECURITY_INFORMATION RequestedInformation, + PSECURITY_DESCRIPTOR pSecurityDescriptor, + DWORD nLength, + LPDWORD lpnLengthNeeded); +typedef BOOL (WINAPI * GetSecurityDescriptorOwner_Proc) ( + PSECURITY_DESCRIPTOR pSecurityDescriptor, + PSID *pOwner, + LPBOOL lpbOwnerDefaulted); +typedef BOOL (WINAPI * GetSecurityDescriptorGroup_Proc) ( + PSECURITY_DESCRIPTOR pSecurityDescriptor, + PSID *pGroup, + LPBOOL lpbGroupDefaulted); +typedef BOOL (WINAPI * IsValidSid_Proc) ( + PSID sid); +typedef HANDLE (WINAPI * CreateToolhelp32Snapshot_Proc) ( + DWORD dwFlags, + DWORD th32ProcessID); +typedef BOOL (WINAPI * Process32First_Proc) ( + HANDLE hSnapshot, + LPPROCESSENTRY32 lppe); +typedef BOOL (WINAPI * Process32Next_Proc) ( + HANDLE hSnapshot, + LPPROCESSENTRY32 lppe); +typedef BOOL (WINAPI * OpenThreadToken_Proc) ( + HANDLE ThreadHandle, + DWORD DesiredAccess, + BOOL OpenAsSelf, + PHANDLE TokenHandle); +typedef BOOL (WINAPI * ImpersonateSelf_Proc) ( + SECURITY_IMPERSONATION_LEVEL ImpersonationLevel); +typedef BOOL (WINAPI * RevertToSelf_Proc) (void); +typedef BOOL (WINAPI * GetProcessMemoryInfo_Proc) ( + HANDLE Process, + PPROCESS_MEMORY_COUNTERS ppsmemCounters, + DWORD cb); +typedef BOOL (WINAPI * GetProcessWorkingSetSize_Proc) ( + HANDLE hProcess, + DWORD * lpMinimumWorkingSetSize, + DWORD * lpMaximumWorkingSetSize); +typedef BOOL (WINAPI * GlobalMemoryStatus_Proc) ( + LPMEMORYSTATUS lpBuffer); +typedef BOOL (WINAPI * GlobalMemoryStatusEx_Proc) ( + LPMEMORY_STATUS_EX lpBuffer); +typedef BOOL (WINAPI * CopySid_Proc) ( + DWORD nDestinationSidLength, + PSID pDestinationSid, + PSID pSourceSid); +typedef BOOL (WINAPI * EqualSid_Proc) ( + PSID pSid1, + PSID pSid2); +typedef DWORD (WINAPI * GetLengthSid_Proc) ( + PSID pSid); +typedef void (WINAPI * GetNativeSystemInfo_Proc) ( + LPSYSTEM_INFO lpSystemInfo); +typedef BOOL (WINAPI * GetSystemTimes_Proc) ( + LPFILETIME lpIdleTime, + LPFILETIME lpKernelTime, + LPFILETIME lpUserTime); + /* ** A utility function ** */ @@ -415,12 +544,236 @@ PUCHAR WINAPI get_sid_sub_authority_count ( return (s_pfn_Get_Sid_Sub_Authority_Count (pSid)); } +BOOL WINAPI get_file_security ( + LPCTSTR lpFileName, + SECURITY_INFORMATION RequestedInformation, + PSECURITY_DESCRIPTOR pSecurityDescriptor, + DWORD nLength, + LPDWORD lpnLengthNeeded) +{ + static GetFileSecurity_Proc s_pfn_Get_File_Security = NULL; + HMODULE hm_advapi32 = NULL; + if (is_windows_9x () == TRUE) + { + return FALSE; + } + if (g_b_init_get_file_security == 0) + { + g_b_init_get_file_security = 1; + hm_advapi32 = LoadLibrary ("Advapi32.dll"); + s_pfn_Get_File_Security = + (GetFileSecurity_Proc) GetProcAddress ( + hm_advapi32, GetFileSecurity_Name); + } + if (s_pfn_Get_File_Security == NULL) + { + return FALSE; + } + return (s_pfn_Get_File_Security (lpFileName, RequestedInformation, + pSecurityDescriptor, nLength, + lpnLengthNeeded)); +} + +BOOL WINAPI get_security_descriptor_owner ( + PSECURITY_DESCRIPTOR pSecurityDescriptor, + PSID *pOwner, + LPBOOL lpbOwnerDefaulted) +{ + static GetSecurityDescriptorOwner_Proc s_pfn_Get_Security_Descriptor_Owner = NULL; + HMODULE hm_advapi32 = NULL; + if (is_windows_9x () == TRUE) + { + return FALSE; + } + if (g_b_init_get_security_descriptor_owner == 0) + { + g_b_init_get_security_descriptor_owner = 1; + hm_advapi32 = LoadLibrary ("Advapi32.dll"); + s_pfn_Get_Security_Descriptor_Owner = + (GetSecurityDescriptorOwner_Proc) GetProcAddress ( + hm_advapi32, "GetSecurityDescriptorOwner"); + } + if (s_pfn_Get_Security_Descriptor_Owner == NULL) + { + return FALSE; + } + return (s_pfn_Get_Security_Descriptor_Owner (pSecurityDescriptor, pOwner, + lpbOwnerDefaulted)); +} + +BOOL WINAPI get_security_descriptor_group ( + PSECURITY_DESCRIPTOR pSecurityDescriptor, + PSID *pGroup, + LPBOOL lpbGroupDefaulted) +{ + static GetSecurityDescriptorGroup_Proc s_pfn_Get_Security_Descriptor_Group = NULL; + HMODULE hm_advapi32 = NULL; + if (is_windows_9x () == TRUE) + { + return FALSE; + } + if (g_b_init_get_security_descriptor_group == 0) + { + g_b_init_get_security_descriptor_group = 1; + hm_advapi32 = LoadLibrary ("Advapi32.dll"); + s_pfn_Get_Security_Descriptor_Group = + (GetSecurityDescriptorGroup_Proc) GetProcAddress ( + hm_advapi32, "GetSecurityDescriptorGroup"); + } + if (s_pfn_Get_Security_Descriptor_Group == NULL) + { + return FALSE; + } + return (s_pfn_Get_Security_Descriptor_Group (pSecurityDescriptor, pGroup, + lpbGroupDefaulted)); +} + +BOOL WINAPI is_valid_sid ( + PSID sid) +{ + static IsValidSid_Proc s_pfn_Is_Valid_Sid = NULL; + HMODULE hm_advapi32 = NULL; + if (is_windows_9x () == TRUE) + { + return FALSE; + } + if (g_b_init_is_valid_sid == 0) + { + g_b_init_is_valid_sid = 1; + hm_advapi32 = LoadLibrary ("Advapi32.dll"); + s_pfn_Is_Valid_Sid = + (IsValidSid_Proc) GetProcAddress ( + hm_advapi32, "IsValidSid"); + } + if (s_pfn_Is_Valid_Sid == NULL) + { + return FALSE; + } + return (s_pfn_Is_Valid_Sid (sid)); +} + +BOOL WINAPI equal_sid ( + PSID sid1, + PSID sid2) +{ + static EqualSid_Proc s_pfn_Equal_Sid = NULL; + HMODULE hm_advapi32 = NULL; + if (is_windows_9x () == TRUE) + { + return FALSE; + } + if (g_b_init_equal_sid == 0) + { + g_b_init_equal_sid = 1; + hm_advapi32 = LoadLibrary ("Advapi32.dll"); + s_pfn_Equal_Sid = + (EqualSid_Proc) GetProcAddress ( + hm_advapi32, "EqualSid"); + } + if (s_pfn_Equal_Sid == NULL) + { + return FALSE; + } + return (s_pfn_Equal_Sid (sid1, sid2)); +} + +DWORD WINAPI get_length_sid ( + PSID sid) +{ + static GetLengthSid_Proc s_pfn_Get_Length_Sid = NULL; + HMODULE hm_advapi32 = NULL; + if (is_windows_9x () == TRUE) + { + return 0; + } + if (g_b_init_get_length_sid == 0) + { + g_b_init_get_length_sid = 1; + hm_advapi32 = LoadLibrary ("Advapi32.dll"); + s_pfn_Get_Length_Sid = + (GetLengthSid_Proc) GetProcAddress ( + hm_advapi32, "GetLengthSid"); + } + if (s_pfn_Get_Length_Sid == NULL) + { + return 0; + } + return (s_pfn_Get_Length_Sid (sid)); +} + +BOOL WINAPI copy_sid ( + DWORD destlen, + PSID dest, + PSID src) +{ + static CopySid_Proc s_pfn_Copy_Sid = NULL; + HMODULE hm_advapi32 = NULL; + if (is_windows_9x () == TRUE) + { + return FALSE; + } + if (g_b_init_copy_sid == 0) + { + g_b_init_copy_sid = 1; + hm_advapi32 = LoadLibrary ("Advapi32.dll"); + s_pfn_Copy_Sid = + (CopySid_Proc) GetProcAddress ( + hm_advapi32, "CopySid"); + } + if (s_pfn_Copy_Sid == NULL) + { + return FALSE; + } + return (s_pfn_Copy_Sid (destlen, dest, src)); +} + /* END: Wrapper functions around OpenProcessToken and other functions in advapi32.dll that are only supported in Windows NT / 2k / XP */ +void WINAPI get_native_system_info ( + LPSYSTEM_INFO lpSystemInfo) +{ + static GetNativeSystemInfo_Proc s_pfn_Get_Native_System_Info = NULL; + if (is_windows_9x () != TRUE) + { + if (g_b_init_get_native_system_info == 0) + { + g_b_init_get_native_system_info = 1; + s_pfn_Get_Native_System_Info = + (GetNativeSystemInfo_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"), + "GetNativeSystemInfo"); + } + if (s_pfn_Get_Native_System_Info != NULL) + s_pfn_Get_Native_System_Info (lpSystemInfo); + } + else + lpSystemInfo->dwNumberOfProcessors = -1; +} + +BOOL WINAPI get_system_times( + LPFILETIME lpIdleTime, + LPFILETIME lpKernelTime, + LPFILETIME lpUserTime) +{ + static GetSystemTimes_Proc s_pfn_Get_System_times = NULL; + if (is_windows_9x () == TRUE) + { + return FALSE; + } + if (g_b_init_get_system_times == 0) + { + g_b_init_get_system_times = 1; + s_pfn_Get_System_times = + (GetSystemTimes_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"), + "GetSystemTimes"); + } + if (s_pfn_Get_System_times == NULL) + return FALSE; + return (s_pfn_Get_System_times (lpIdleTime, lpKernelTime, lpUserTime)); +} /* Equivalent of strerror for W32 error codes. */ char * @@ -493,55 +846,201 @@ gethostname (char *buffer, int size) #endif /* HAVE_SOCKETS */ /* Emulate getloadavg. */ + +struct load_sample { + time_t sample_time; + ULONGLONG idle; + ULONGLONG kernel; + ULONGLONG user; +}; + +/* Number of processors on this machine. */ +static unsigned num_of_processors; + +/* We maintain 1-sec samples for the last 16 minutes in a circular buffer. */ +static struct load_sample samples[16*60]; +static int first_idx = -1, last_idx = -1; +static int max_idx = sizeof (samples) / sizeof (samples[0]); + +static int +buf_next (int from) +{ + int next_idx = from + 1; + + if (next_idx >= max_idx) + next_idx = 0; + + return next_idx; +} + +static int +buf_prev (int from) +{ + int prev_idx = from - 1; + + if (prev_idx < 0) + prev_idx = max_idx - 1; + + return prev_idx; +} + +static void +sample_system_load (ULONGLONG *idle, ULONGLONG *kernel, ULONGLONG *user) +{ + SYSTEM_INFO sysinfo; + FILETIME ft_idle, ft_user, ft_kernel; + + /* Initialize the number of processors on this machine. */ + if (num_of_processors <= 0) + { + get_native_system_info (&sysinfo); + num_of_processors = sysinfo.dwNumberOfProcessors; + if (num_of_processors <= 0) + { + GetSystemInfo (&sysinfo); + num_of_processors = sysinfo.dwNumberOfProcessors; + } + if (num_of_processors <= 0) + num_of_processors = 1; + } + + /* TODO: Take into account threads that are ready to run, by + sampling the "\System\Processor Queue Length" performance + counter. The code below accounts only for threads that are + actually running. */ + + if (get_system_times (&ft_idle, &ft_kernel, &ft_user)) + { + ULARGE_INTEGER uidle, ukernel, uuser; + + memcpy (&uidle, &ft_idle, sizeof (ft_idle)); + memcpy (&ukernel, &ft_kernel, sizeof (ft_kernel)); + memcpy (&uuser, &ft_user, sizeof (ft_user)); + *idle = uidle.QuadPart; + *kernel = ukernel.QuadPart; + *user = uuser.QuadPart; + } + else + { + *idle = 0; + *kernel = 0; + *user = 0; + } +} + +/* Produce the load average for a given time interval, using the + samples in the samples[] array. WHICH can be 0, 1, or 2, meaning + 1-minute, 5-minute, or 15-minute average, respectively. */ +static double +getavg (int which) +{ + double retval = -1.0; + double tdiff; + int idx; + double span = (which == 0 ? 1.0 : (which == 1 ? 5.0 : 15.0)) * 60; + time_t now = samples[last_idx].sample_time; + + if (first_idx != last_idx) + { + for (idx = buf_prev (last_idx); ; idx = buf_prev (idx)) + { + tdiff = difftime (now, samples[idx].sample_time); + if (tdiff >= span - 2*DBL_EPSILON*now) + { + long double sys = + samples[last_idx].kernel + samples[last_idx].user + - (samples[idx].kernel + samples[idx].user); + long double idl = samples[last_idx].idle - samples[idx].idle; + + retval = (1.0 - idl / sys) * num_of_processors; + break; + } + if (idx == first_idx) + break; + } + } + + return retval; +} + int getloadavg (double loadavg[], int nelem) { - int i; + int elem; + ULONGLONG idle, kernel, user; + time_t now = time (NULL); + + /* Store another sample. We ignore samples that are less than 1 sec + apart. */ + if (difftime (now, samples[last_idx].sample_time) >= 1.0 - 2*DBL_EPSILON*now) + { + sample_system_load (&idle, &kernel, &user); + last_idx = buf_next (last_idx); + samples[last_idx].sample_time = now; + samples[last_idx].idle = idle; + samples[last_idx].kernel = kernel; + samples[last_idx].user = user; + /* If the buffer has more that 15 min worth of samples, discard + the old ones. */ + if (first_idx == -1) + first_idx = last_idx; + while (first_idx != last_idx + && (difftime (now, samples[first_idx].sample_time) + >= 15.0*60 + 2*DBL_EPSILON*now)) + first_idx = buf_next (first_idx); + } - /* A faithful emulation is going to have to be saved for a rainy day. */ - for (i = 0; i < nelem; i++) + for (elem = 0; elem < nelem; elem++) { - loadavg[i] = 0.0; + double avg = getavg (elem); + + if (avg < 0) + break; + loadavg[elem] = avg; } - return i; + + return elem; } /* Emulate getpwuid, getpwnam and others. */ #define PASSWD_FIELD_SIZE 256 -static char the_passwd_name[PASSWD_FIELD_SIZE]; -static char the_passwd_passwd[PASSWD_FIELD_SIZE]; -static char the_passwd_gecos[PASSWD_FIELD_SIZE]; -static char the_passwd_dir[PASSWD_FIELD_SIZE]; -static char the_passwd_shell[PASSWD_FIELD_SIZE]; +static char dflt_passwd_name[PASSWD_FIELD_SIZE]; +static char dflt_passwd_passwd[PASSWD_FIELD_SIZE]; +static char dflt_passwd_gecos[PASSWD_FIELD_SIZE]; +static char dflt_passwd_dir[PASSWD_FIELD_SIZE]; +static char dflt_passwd_shell[PASSWD_FIELD_SIZE]; -static struct passwd the_passwd = +static struct passwd dflt_passwd = { - the_passwd_name, - the_passwd_passwd, + dflt_passwd_name, + dflt_passwd_passwd, 0, 0, 0, - the_passwd_gecos, - the_passwd_dir, - the_passwd_shell, + dflt_passwd_gecos, + dflt_passwd_dir, + dflt_passwd_shell, }; -static struct group the_group = +static char dflt_group_name[GNLEN+1]; + +static struct group dflt_group = { - /* There are no groups on NT, so we just return "root" as the - group name. */ - "root", + /* When group information is not available, we return this as the + group for all files. */ + dflt_group_name, + 0, }; -int +unsigned getuid () { - return the_passwd.pw_uid; + return dflt_passwd.pw_uid; } -int +unsigned geteuid () { /* I could imagine arguing for checking to see whether the user is @@ -550,30 +1049,30 @@ geteuid () return getuid (); } -int +unsigned getgid () { - return the_passwd.pw_gid; + return dflt_passwd.pw_gid; } -int +unsigned getegid () { return getgid (); } struct passwd * -getpwuid (int uid) +getpwuid (unsigned uid) { - if (uid == the_passwd.pw_uid) - return &the_passwd; + if (uid == dflt_passwd.pw_uid) + return &dflt_passwd; return NULL; } struct group * getgrgid (gid_t gid) { - return &the_group; + return &dflt_group; } struct passwd * @@ -585,7 +1084,7 @@ getpwnam (char *name) if (!pw) return pw; - if (stricmp (name, pw->pw_name)) + if (xstrcasecmp (name, pw->pw_name)) return NULL; return pw; @@ -601,86 +1100,96 @@ init_user_info () the user-sid as the user id value (same for group id using the primary group sid from the process token). */ - char name[UNLEN+1], domain[1025]; - DWORD length = sizeof (name), dlength = sizeof (domain), trash; + char uname[UNLEN+1], gname[GNLEN+1], domain[1025]; + DWORD ulength = sizeof (uname), dlength = sizeof (domain), needed; + DWORD glength = sizeof (gname); HANDLE token = NULL; SID_NAME_USE user_type; - unsigned char buf[1024]; + unsigned char *buf = NULL; + DWORD blen = 0; TOKEN_USER user_token; TOKEN_PRIMARY_GROUP group_token; + BOOL result; - if (open_process_token (GetCurrentProcess (), TOKEN_QUERY, &token) - && get_token_information (token, TokenUser, - (PVOID)buf, sizeof (buf), &trash) - && (memcpy (&user_token, buf, sizeof (user_token)), - lookup_account_sid (NULL, user_token.User.Sid, name, &length, - domain, &dlength, &user_type))) + result = open_process_token (GetCurrentProcess (), TOKEN_QUERY, &token); + if (result) + { + result = get_token_information (token, TokenUser, NULL, 0, &blen); + if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER) + { + buf = xmalloc (blen); + result = get_token_information (token, TokenUser, + (LPVOID)buf, blen, &needed); + if (result) + { + memcpy (&user_token, buf, sizeof (user_token)); + result = lookup_account_sid (NULL, user_token.User.Sid, + uname, &ulength, + domain, &dlength, &user_type); + } + } + else + result = FALSE; + } + if (result) { - strcpy (the_passwd.pw_name, name); + strcpy (dflt_passwd.pw_name, uname); /* Determine a reasonable uid value. */ - if (stricmp ("administrator", name) == 0) + if (xstrcasecmp ("administrator", uname) == 0) { - the_passwd.pw_uid = 500; /* well-known Administrator uid */ - the_passwd.pw_gid = 513; /* well-known None gid */ + dflt_passwd.pw_uid = 500; /* well-known Administrator uid */ + dflt_passwd.pw_gid = 513; /* well-known None gid */ } else { /* Use the last sub-authority value of the RID, the relative portion of the SID, as user/group ID. */ - DWORD n_subauthorities = - *get_sid_sub_authority_count (user_token.User.Sid); + dflt_passwd.pw_uid = get_rid (user_token.User.Sid); - if (n_subauthorities < 1) - the_passwd.pw_uid = 0; /* the "World" RID */ - else + /* Get group id and name. */ + result = get_token_information (token, TokenPrimaryGroup, + (LPVOID)buf, blen, &needed); + if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER) { - the_passwd.pw_uid = - *get_sid_sub_authority (user_token.User.Sid, - n_subauthorities - 1); - /* Restrict to conventional uid range for normal users. */ - the_passwd.pw_uid %= 60001; + buf = xrealloc (buf, blen = needed); + result = get_token_information (token, TokenPrimaryGroup, + (LPVOID)buf, blen, &needed); } - - /* Get group id */ - if (get_token_information (token, TokenPrimaryGroup, - (PVOID)buf, sizeof (buf), &trash)) + if (result) { memcpy (&group_token, buf, sizeof (group_token)); - n_subauthorities = - *get_sid_sub_authority_count (group_token.PrimaryGroup); - - if (n_subauthorities < 1) - the_passwd.pw_gid = 0; /* the "World" RID */ - else - { - the_passwd.pw_gid = - *get_sid_sub_authority (group_token.PrimaryGroup, - n_subauthorities - 1); - /* I don't know if this is necessary, but for safety... */ - the_passwd.pw_gid %= 60001; - } + dflt_passwd.pw_gid = get_rid (group_token.PrimaryGroup); + dlength = sizeof (domain); + /* If we can get at the real Primary Group name, use that. + Otherwise, the default group name was already set to + "None" in globals_of_w32. */ + if (lookup_account_sid (NULL, group_token.PrimaryGroup, + gname, &glength, NULL, &dlength, + &user_type)) + strcpy (dflt_group_name, gname); } else - the_passwd.pw_gid = the_passwd.pw_uid; + dflt_passwd.pw_gid = dflt_passwd.pw_uid; } } /* If security calls are not supported (presumably because we - are running under Windows 95), fallback to this. */ - else if (GetUserName (name, &length)) + are running under Windows 9X), fallback to this: */ + else if (GetUserName (uname, &ulength)) { - strcpy (the_passwd.pw_name, name); - if (stricmp ("administrator", name) == 0) - the_passwd.pw_uid = 0; + strcpy (dflt_passwd.pw_name, uname); + if (xstrcasecmp ("administrator", uname) == 0) + dflt_passwd.pw_uid = 0; else - the_passwd.pw_uid = 123; - the_passwd.pw_gid = the_passwd.pw_uid; + dflt_passwd.pw_uid = 123; + dflt_passwd.pw_gid = dflt_passwd.pw_uid; } else { - strcpy (the_passwd.pw_name, "unknown"); - the_passwd.pw_uid = 123; - the_passwd.pw_gid = 123; + strcpy (dflt_passwd.pw_name, "unknown"); + dflt_passwd.pw_uid = 123; + dflt_passwd.pw_gid = 123; } + dflt_group.gr_gid = dflt_passwd.pw_gid; /* Ensure HOME and SHELL are defined. */ if (getenv ("HOME") == NULL) @@ -689,9 +1198,10 @@ init_user_info () abort (); /* Set dir and shell from environment variables. */ - strcpy (the_passwd.pw_dir, getenv ("HOME")); - strcpy (the_passwd.pw_shell, getenv ("SHELL")); + strcpy (dflt_passwd.pw_dir, getenv ("HOME")); + strcpy (dflt_passwd.pw_shell, getenv ("SHELL")); + xfree (buf); if (token) CloseHandle (token); } @@ -1009,7 +1519,7 @@ w32_get_resource (key, lpdwtype) return (lpvalue); } - if (lpvalue) xfree (lpvalue); + xfree (lpvalue); RegCloseKey (hrootkey); } @@ -1026,7 +1536,7 @@ w32_get_resource (key, lpdwtype) return (lpvalue); } - if (lpvalue) xfree (lpvalue); + xfree (lpvalue); RegCloseKey (hrootkey); } @@ -1127,25 +1637,20 @@ init_environment (char ** argv) HRESULT profile_result; /* Dynamically load ShGetFolderPath, as it won't exist on versions of Windows 95 and NT4 that have not been updated to include - MSIE 5. Also we don't link with shell32.dll by default. */ - HMODULE shell32_dll; + MSIE 5. */ ShGetFolderPath_fn get_folder_path; - shell32_dll = GetModuleHandle ("shell32.dll"); get_folder_path = (ShGetFolderPath_fn) - GetProcAddress (shell32_dll, "SHGetFolderPathA"); + GetProcAddress (GetModuleHandle ("shell32.dll"), "SHGetFolderPathA"); if (get_folder_path != NULL) { profile_result = get_folder_path (NULL, CSIDL_APPDATA, NULL, 0, default_home); - /* If we can't get the appdata dir, revert to old behaviour. */ + /* If we can't get the appdata dir, revert to old behavior. */ if (profile_result == S_OK) env_vars[0].def_value = default_home; } - - /* Unload shell32.dll, it is not needed anymore. */ - FreeLibrary (shell32_dll); } /* Get default locale info and use it for LANG. */ @@ -1178,7 +1683,7 @@ init_environment (char ** argv) abort (); *p = 0; - if ((p = strrchr (modname, '\\')) && stricmp (p, "\\bin") == 0) + if ((p = strrchr (modname, '\\')) && xstrcasecmp (p, "\\bin") == 0) { char buf[SET_ENV_BUF_SIZE]; @@ -1194,7 +1699,7 @@ init_environment (char ** argv) /* FIXME: should use substring of get_emacs_configuration (). But I don't think the Windows build supports alpha, mips etc anymore, so have taken the easy option for now. */ - else if (p && stricmp (p, "\\i386") == 0) + else if (p && xstrcasecmp (p, "\\i386") == 0) { *p = 0; p = strrchr (modname, '\\'); @@ -1202,7 +1707,7 @@ init_environment (char ** argv) { *p = 0; p = strrchr (modname, '\\'); - if (p && stricmp (p, "\\src") == 0) + if (p && xstrcasecmp (p, "\\src") == 0) { char buf[SET_ENV_BUF_SIZE]; @@ -1227,7 +1732,7 @@ init_environment (char ** argv) /* Also ignore empty environment variables. */ || *lpval == 0) { - if (lpval) xfree (lpval); + xfree (lpval); lpval = env_vars[i].def_value; dwType = REG_EXPAND_SZ; dont_free = 1; @@ -1534,7 +2039,7 @@ lookup_volume_info (char * root_dir) volume_info_data * info; for (info = volume_cache; info; info = info->next) - if (stricmp (info->root_dir, root_dir) == 0) + if (xstrcasecmp (info->root_dir, root_dir) == 0) break; return info; } @@ -1812,10 +2317,10 @@ is_exec (const char * name) char * p = strrchr (name, '.'); return (p != NULL - && (stricmp (p, ".exe") == 0 || - stricmp (p, ".com") == 0 || - stricmp (p, ".bat") == 0 || - stricmp (p, ".cmd") == 0)); + && (xstrcasecmp (p, ".exe") == 0 || + xstrcasecmp (p, ".com") == 0 || + xstrcasecmp (p, ".bat") == 0 || + xstrcasecmp (p, ".cmd") == 0)); } /* Emulate the Unix directory procedures opendir, closedir, @@ -1889,6 +2394,8 @@ closedir (DIR *dirp) struct direct * readdir (DIR *dirp) { + int downcase = !NILP (Vw32_downcase_file_names); + if (wnet_enum_handle != INVALID_HANDLE_VALUE) { if (!read_unc_volume (wnet_enum_handle, @@ -1923,14 +2430,38 @@ readdir (DIR *dirp) value returned by stat(). */ dir_static.d_ino = 1; + strcpy (dir_static.d_name, dir_find_data.cFileName); + + /* If the file name in cFileName[] includes `?' characters, it means + the original file name used characters that cannot be represented + by the current ANSI codepage. To avoid total lossage, retrieve + the short 8+3 alias of the long file name. */ + if (_mbspbrk (dir_static.d_name, "?")) + { + strcpy (dir_static.d_name, dir_find_data.cAlternateFileName); + downcase = 1; /* 8+3 aliases are returned in all caps */ + } + dir_static.d_namlen = strlen (dir_static.d_name); dir_static.d_reclen = sizeof (struct direct) - MAXNAMLEN + 3 + dir_static.d_namlen - dir_static.d_namlen % 4; - dir_static.d_namlen = strlen (dir_find_data.cFileName); - strcpy (dir_static.d_name, dir_find_data.cFileName); + /* If the file name in cFileName[] includes `?' characters, it means + the original file name used characters that cannot be represented + by the current ANSI codepage. To avoid total lossage, retrieve + the short 8+3 alias of the long file name. */ + if (_mbspbrk (dir_find_data.cFileName, "?")) + { + strcpy (dir_static.d_name, dir_find_data.cAlternateFileName); + /* 8+3 aliases are returned in all caps, which could break + various alists that look at filenames' extensions. */ + downcase = 1; + } + else + strcpy (dir_static.d_name, dir_find_data.cFileName); + dir_static.d_namlen = strlen (dir_static.d_name); if (dir_is_fat) _strlwr (dir_static.d_name); - else if (!NILP (Vw32_downcase_file_names)) + else if (downcase) { register char *p; for (p = dir_static.d_name; *p; p++) @@ -2024,10 +2555,26 @@ logon_network_drive (const char *path) NETRESOURCE resource; char share[MAX_PATH]; int i, n_slashes; + char drive[4]; + UINT drvtype; + + if (IS_DIRECTORY_SEP (path[0]) && IS_DIRECTORY_SEP (path[1])) + drvtype = DRIVE_REMOTE; + else if (path[0] == '\0' || path[1] != ':') + drvtype = GetDriveType (NULL); + else + { + drive[0] = path[0]; + drive[1] = ':'; + drive[2] = '\\'; + drive[3] = '\0'; + drvtype = GetDriveType (drive); + } /* Only logon to networked drives. */ - if (!IS_DIRECTORY_SEP (path[0]) || !IS_DIRECTORY_SEP (path[1])) + if (drvtype != DRIVE_REMOTE) return; + n_slashes = 2; strncpy (share, path, MAX_PATH); /* Truncate to just server and share name. */ @@ -2108,7 +2655,7 @@ sys_chmod (const char * path, int mode) int sys_chown (const char *path, uid_t owner, gid_t group) { - if (sys_chmod (path, _S_IREAD) == -1) /* check if file exists */ + if (sys_chmod (path, S_IREAD) == -1) /* check if file exists */ return -1; return 0; } @@ -2357,7 +2904,7 @@ sys_rename (const char * oldname, const char * newname) return -1; } - /* Emulate Unix behaviour - newname is deleted if it already exists + /* Emulate Unix behavior - newname is deleted if it already exists (at least if it is a file; don't do this for directories). Since we mustn't do this if we are just changing the case of the @@ -2397,70 +2944,69 @@ sys_unlink (const char * path) } static FILETIME utc_base_ft; -static long double utc_base; +static ULONGLONG utc_base; /* In 100ns units */ static int init = 0; +#define FILETIME_TO_U64(result, ft) \ + do { \ + ULARGE_INTEGER uiTemp; \ + uiTemp.LowPart = (ft).dwLowDateTime; \ + uiTemp.HighPart = (ft).dwHighDateTime; \ + result = uiTemp.QuadPart; \ + } while (0) + +static void +initialize_utc_base () +{ + /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */ + SYSTEMTIME st; + + st.wYear = 1970; + st.wMonth = 1; + st.wDay = 1; + st.wHour = 0; + st.wMinute = 0; + st.wSecond = 0; + st.wMilliseconds = 0; + + SystemTimeToFileTime (&st, &utc_base_ft); + FILETIME_TO_U64 (utc_base, utc_base_ft); +} + static time_t convert_time (FILETIME ft) { - long double ret; + ULONGLONG tmp; if (!init) { - /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */ - SYSTEMTIME st; - - st.wYear = 1970; - st.wMonth = 1; - st.wDay = 1; - st.wHour = 0; - st.wMinute = 0; - st.wSecond = 0; - st.wMilliseconds = 0; - - SystemTimeToFileTime (&st, &utc_base_ft); - utc_base = (long double) utc_base_ft.dwHighDateTime - * 4096.0L * 1024.0L * 1024.0L + utc_base_ft.dwLowDateTime; + initialize_utc_base(); init = 1; } if (CompareFileTime (&ft, &utc_base_ft) < 0) return 0; - ret = (long double) ft.dwHighDateTime - * 4096.0L * 1024.0L * 1024.0L + ft.dwLowDateTime; - ret -= utc_base; - return (time_t) (ret * 1e-7L); + FILETIME_TO_U64 (tmp, ft); + return (time_t) ((tmp - utc_base) / 10000000L); } + void convert_from_time_t (time_t time, FILETIME * pft) { - long double tmp; + ULARGE_INTEGER tmp; if (!init) { - /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */ - SYSTEMTIME st; - - st.wYear = 1970; - st.wMonth = 1; - st.wDay = 1; - st.wHour = 0; - st.wMinute = 0; - st.wSecond = 0; - st.wMilliseconds = 0; - - SystemTimeToFileTime (&st, &utc_base_ft); - utc_base = (long double) utc_base_ft.dwHighDateTime - * 4096 * 1024 * 1024 + utc_base_ft.dwLowDateTime; + initialize_utc_base (); init = 1; } /* time in 100ns units since 1-Jan-1601 */ - tmp = (long double) time * 1e7 + utc_base; - pft->dwHighDateTime = (DWORD) (tmp / (4096.0 * 1024 * 1024)); - pft->dwLowDateTime = (DWORD) (tmp - (4096.0 * 1024 * 1024) * pft->dwHighDateTime); + tmp.QuadPart = (ULONGLONG) time * 10000000L + utc_base; + pft->dwHighDateTime = tmp.HighPart; + pft->dwLowDateTime = tmp.LowPart; } #if 0 @@ -2506,49 +3052,268 @@ generate_inode_val (const char * name) #endif -/* MSVC stat function can't cope with UNC names and has other bugs, so - replace it with our own. This also allows us to calculate consistent - inode values without hacks in the main Emacs code. */ -int -stat (const char * path, struct stat * buf) +static PSECURITY_DESCRIPTOR +get_file_security_desc (const char *fname) { - char *name, *r; - WIN32_FIND_DATA wfd; - HANDLE fh; - DWORD fake_inode; - int permission; - int len; - int rootdir = FALSE; + PSECURITY_DESCRIPTOR psd = NULL; + DWORD sd_len, err; + SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION + | GROUP_SECURITY_INFORMATION /* | DACL_SECURITY_INFORMATION */ ; - if (path == NULL || buf == NULL) + if (!get_file_security (fname, si, psd, 0, &sd_len)) { - errno = EFAULT; - return -1; + err = GetLastError (); + if (err != ERROR_INSUFFICIENT_BUFFER) + return NULL; } - name = (char *) map_w32_filename (path, &path); - /* Must be valid filename, no wild cards or other invalid - characters. We use _mbspbrk to support multibyte strings that - might look to strpbrk as if they included literal *, ?, and other - characters mentioned below that are disallowed by Windows - filesystems. */ - if (_mbspbrk (name, "*?|<>\"")) + psd = xmalloc (sd_len); + if (!get_file_security (fname, si, psd, sd_len, &sd_len)) { - errno = ENOENT; - return -1; + xfree (psd); + return NULL; } - /* If name is "c:/.." or "/.." then stat "c:/" or "/". */ - r = IS_DEVICE_SEP (name[1]) ? &name[2] : name; - if (IS_DIRECTORY_SEP (r[0]) && r[1] == '.' && r[2] == '.' && r[3] == '\0') - { - r[1] = r[2] = '\0'; - } + return psd; +} - /* Remove trailing directory separator, unless name is the root - directory of a drive or UNC volume in which case ensure there - is a trailing separator. */ - len = strlen (name); +static DWORD +get_rid (PSID sid) +{ + unsigned n_subauthorities; + + /* Use the last sub-authority value of the RID, the relative + portion of the SID, as user/group ID. */ + n_subauthorities = *get_sid_sub_authority_count (sid); + if (n_subauthorities < 1) + return 0; /* the "World" RID */ + return *get_sid_sub_authority (sid, n_subauthorities - 1); +} + +/* Caching SID and account values for faster lokup. */ + +#ifdef __GNUC__ +# define FLEXIBLE_ARRAY_MEMBER +#else +# define FLEXIBLE_ARRAY_MEMBER 1 +#endif + +struct w32_id { + unsigned rid; + struct w32_id *next; + char name[GNLEN+1]; + unsigned char sid[FLEXIBLE_ARRAY_MEMBER]; +}; + +static struct w32_id *w32_idlist; + +static int +w32_cached_id (PSID sid, unsigned *id, char *name) +{ + struct w32_id *tail, *found; + + for (found = NULL, tail = w32_idlist; tail; tail = tail->next) + { + if (equal_sid ((PSID)tail->sid, sid)) + { + found = tail; + break; + } + } + if (found) + { + *id = found->rid; + strcpy (name, found->name); + return 1; + } + else + return 0; +} + +static void +w32_add_to_cache (PSID sid, unsigned id, char *name) +{ + DWORD sid_len; + struct w32_id *new_entry; + + /* We don't want to leave behind stale cache from when Emacs was + dumped. */ + if (initialized) + { + sid_len = get_length_sid (sid); + new_entry = xmalloc (offsetof (struct w32_id, sid) + sid_len); + if (new_entry) + { + new_entry->rid = id; + strcpy (new_entry->name, name); + copy_sid (sid_len, (PSID)new_entry->sid, sid); + new_entry->next = w32_idlist; + w32_idlist = new_entry; + } + } +} + +#define UID 1 +#define GID 2 + +static int +get_name_and_id (PSECURITY_DESCRIPTOR psd, const char *fname, + unsigned *id, char *nm, int what) +{ + PSID sid = NULL; + char machine[MAX_COMPUTERNAME_LENGTH+1]; + BOOL dflt; + SID_NAME_USE ignore; + char name[UNLEN+1]; + DWORD name_len = sizeof (name); + char domain[1024]; + DWORD domain_len = sizeof(domain); + char *mp = NULL; + int use_dflt = 0; + int result; + + if (what == UID) + result = get_security_descriptor_owner (psd, &sid, &dflt); + else if (what == GID) + result = get_security_descriptor_group (psd, &sid, &dflt); + else + result = 0; + + if (!result || !is_valid_sid (sid)) + use_dflt = 1; + else if (!w32_cached_id (sid, id, nm)) + { + /* If FNAME is a UNC, we need to lookup account on the + specified machine. */ + if (IS_DIRECTORY_SEP (fname[0]) && IS_DIRECTORY_SEP (fname[1]) + && fname[2] != '\0') + { + const char *s; + char *p; + + for (s = fname + 2, p = machine; + *s && !IS_DIRECTORY_SEP (*s); s++, p++) + *p = *s; + *p = '\0'; + mp = machine; + } + + if (!lookup_account_sid (mp, sid, name, &name_len, + domain, &domain_len, &ignore) + || name_len > UNLEN+1) + use_dflt = 1; + else + { + *id = get_rid (sid); + strcpy (nm, name); + w32_add_to_cache (sid, *id, name); + } + } + return use_dflt; +} + +static void +get_file_owner_and_group ( + PSECURITY_DESCRIPTOR psd, + const char *fname, + struct stat *st) +{ + int dflt_usr = 0, dflt_grp = 0; + + if (!psd) + { + dflt_usr = 1; + dflt_grp = 1; + } + else + { + if (get_name_and_id (psd, fname, &st->st_uid, st->st_uname, UID)) + dflt_usr = 1; + if (get_name_and_id (psd, fname, &st->st_gid, st->st_gname, GID)) + dflt_grp = 1; + } + /* Consider files to belong to current user/group, if we cannot get + more accurate information. */ + if (dflt_usr) + { + st->st_uid = dflt_passwd.pw_uid; + strcpy (st->st_uname, dflt_passwd.pw_name); + } + if (dflt_grp) + { + st->st_gid = dflt_passwd.pw_gid; + strcpy (st->st_gname, dflt_group.gr_name); + } +} + +/* Return non-zero if NAME is a potentially slow filesystem. */ +int +is_slow_fs (const char *name) +{ + char drive_root[4]; + UINT devtype; + + if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1])) + devtype = DRIVE_REMOTE; /* assume UNC name is remote */ + else if (!(strlen (name) >= 2 && IS_DEVICE_SEP (name[1]))) + devtype = GetDriveType (NULL); /* use root of current drive */ + else + { + /* GetDriveType needs the root directory of the drive. */ + strncpy (drive_root, name, 2); + drive_root[2] = '\\'; + drive_root[3] = '\0'; + devtype = GetDriveType (drive_root); + } + return !(devtype == DRIVE_FIXED || devtype == DRIVE_RAMDISK); +} + +/* MSVC stat function can't cope with UNC names and has other bugs, so + replace it with our own. This also allows us to calculate consistent + inode values without hacks in the main Emacs code. */ +int +stat (const char * path, struct stat * buf) +{ + char *name, *r; + char drive_root[4]; + UINT devtype; + WIN32_FIND_DATA wfd; + HANDLE fh; + unsigned __int64 fake_inode; + int permission; + int len; + int rootdir = FALSE; + PSECURITY_DESCRIPTOR psd = NULL; + + if (path == NULL || buf == NULL) + { + errno = EFAULT; + return -1; + } + + name = (char *) map_w32_filename (path, &path); + /* Must be valid filename, no wild cards or other invalid + characters. We use _mbspbrk to support multibyte strings that + might look to strpbrk as if they included literal *, ?, and other + characters mentioned below that are disallowed by Windows + filesystems. */ + if (_mbspbrk (name, "*?|<>\"")) + { + errno = ENOENT; + return -1; + } + + /* If name is "c:/.." or "/.." then stat "c:/" or "/". */ + r = IS_DEVICE_SEP (name[1]) ? &name[2] : name; + if (IS_DIRECTORY_SEP (r[0]) && r[1] == '.' && r[2] == '.' && r[3] == '\0') + { + r[1] = r[2] = '\0'; + } + + /* Remove trailing directory separator, unless name is the root + directory of a drive or UNC volume in which case ensure there + is a trailing separator. */ + len = strlen (name); rootdir = (path >= name + len - 1 && (IS_DIRECTORY_SEP (*path) || *path == 0)); name = strcpy (alloca (len + 2), name); @@ -2597,7 +3362,7 @@ stat (const char * path, struct stat * buf) if (dir_find_handle != INVALID_HANDLE_VALUE && strnicmp (name, dir_pathname, len) == 0 && IS_DIRECTORY_SEP (name[len]) - && stricmp (name + len + 1, dir_static.d_name) == 0) + && xstrcasecmp (name + len + 1, dir_static.d_name) == 0) { /* This was the last entry returned by readdir. */ wfd = dir_find_data; @@ -2616,7 +3381,8 @@ stat (const char * path, struct stat * buf) } } - if (!NILP (Vw32_get_true_file_attributes) + if (!(NILP (Vw32_get_true_file_attributes) + || (EQ (Vw32_get_true_file_attributes, Qlocal) && is_slow_fs (name))) /* No access rights required to get info. */ && (fh = CreateFile (name, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL)) @@ -2635,7 +3401,9 @@ stat (const char * path, struct stat * buf) all the time (even then there are situations where it is not unique). Reputedly, there are at most 48 bits of info (on NTFS, presumably less on FAT). */ - fake_inode = info.nFileIndexLow ^ info.nFileIndexHigh; + fake_inode = info.nFileIndexHigh; + fake_inode <<= 32; + fake_inode += info.nFileIndexLow; } else { @@ -2645,34 +3413,39 @@ stat (const char * path, struct stat * buf) if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - buf->st_mode = _S_IFDIR; + buf->st_mode = S_IFDIR; } else { switch (GetFileType (fh)) { case FILE_TYPE_DISK: - buf->st_mode = _S_IFREG; + buf->st_mode = S_IFREG; break; case FILE_TYPE_PIPE: - buf->st_mode = _S_IFIFO; + buf->st_mode = S_IFIFO; break; case FILE_TYPE_CHAR: case FILE_TYPE_UNKNOWN: default: - buf->st_mode = _S_IFCHR; + buf->st_mode = S_IFCHR; } } CloseHandle (fh); + psd = get_file_security_desc (name); + get_file_owner_and_group (psd, name, buf); } else { /* Don't bother to make this information more accurate. */ buf->st_mode = (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? - _S_IFDIR : _S_IFREG; + S_IFDIR : S_IFREG; buf->st_nlink = 1; fake_inode = 0; + + get_file_owner_and_group (NULL, name, buf); } + xfree (psd); #if 0 /* Not sure if there is any point in this. */ @@ -2692,16 +3465,14 @@ stat (const char * path, struct stat * buf) else buf->st_ino = fake_inode; - /* consider files to belong to current user */ - buf->st_uid = the_passwd.pw_uid; - buf->st_gid = the_passwd.pw_gid; - /* volume_info is set indirectly by map_w32_filename */ buf->st_dev = volume_info.serialnum; buf->st_rdev = volume_info.serialnum; - buf->st_size = wfd.nFileSizeLow; + buf->st_size = wfd.nFileSizeHigh; + buf->st_size <<= 32; + buf->st_size += wfd.nFileSizeLow; /* Convert timestamps to Unix format. */ buf->st_mtime = convert_time (wfd.ftLastWriteTime); @@ -2712,14 +3483,14 @@ stat (const char * path, struct stat * buf) /* determine rwx permissions */ if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) - permission = _S_IREAD; + permission = S_IREAD; else - permission = _S_IREAD | _S_IWRITE; + permission = S_IREAD | S_IWRITE; if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - permission |= _S_IEXEC; + permission |= S_IEXEC; else if (is_exec (name)) - permission |= _S_IEXEC; + permission |= S_IEXEC; buf->st_mode |= permission | (permission >> 3) | (permission >> 6); @@ -2733,13 +3504,13 @@ fstat (int desc, struct stat * buf) { HANDLE fh = (HANDLE) _get_osfhandle (desc); BY_HANDLE_FILE_INFORMATION info; - DWORD fake_inode; + unsigned __int64 fake_inode; int permission; switch (GetFileType (fh) & ~FILE_TYPE_REMOTE) { case FILE_TYPE_DISK: - buf->st_mode = _S_IFREG; + buf->st_mode = S_IFREG; if (!GetFileInformationByHandle (fh, &info)) { errno = EACCES; @@ -2747,12 +3518,12 @@ fstat (int desc, struct stat * buf) } break; case FILE_TYPE_PIPE: - buf->st_mode = _S_IFIFO; + buf->st_mode = S_IFIFO; goto non_disk; case FILE_TYPE_CHAR: case FILE_TYPE_UNKNOWN: default: - buf->st_mode = _S_IFCHR; + buf->st_mode = S_IFCHR; non_disk: memset (&info, 0, sizeof (info)); info.dwFileAttributes = 0; @@ -2762,7 +3533,7 @@ fstat (int desc, struct stat * buf) } if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - buf->st_mode = _S_IFDIR; + buf->st_mode = S_IFDIR; buf->st_nlink = info.nNumberOfLinks; /* Might as well use file index to fake inode values, but this @@ -2770,7 +3541,9 @@ fstat (int desc, struct stat * buf) all the time (even then there are situations where it is not unique). Reputedly, there are at most 48 bits of info (on NTFS, presumably less on FAT). */ - fake_inode = info.nFileIndexLow ^ info.nFileIndexHigh; + fake_inode = info.nFileIndexHigh; + fake_inode <<= 32; + fake_inode += info.nFileIndexLow; /* MSVC defines _ino_t to be short; other libc's might not. */ if (sizeof (buf->st_ino) == 2) @@ -2778,14 +3551,20 @@ fstat (int desc, struct stat * buf) else buf->st_ino = fake_inode; - /* consider files to belong to current user */ - buf->st_uid = the_passwd.pw_uid; - buf->st_gid = the_passwd.pw_gid; + /* Consider files to belong to current user. + FIXME: this should use GetSecurityInfo API, but it is only + available for _WIN32_WINNT >= 0x501. */ + buf->st_uid = dflt_passwd.pw_uid; + buf->st_gid = dflt_passwd.pw_gid; + strcpy (buf->st_uname, dflt_passwd.pw_name); + strcpy (buf->st_gname, dflt_group.gr_name); buf->st_dev = info.dwVolumeSerialNumber; buf->st_rdev = info.dwVolumeSerialNumber; - buf->st_size = info.nFileSizeLow; + buf->st_size = info.nFileSizeHigh; + buf->st_size <<= 32; + buf->st_size += info.nFileSizeLow; /* Convert timestamps to Unix format. */ buf->st_mtime = convert_time (info.ftLastWriteTime); @@ -2796,22 +3575,22 @@ fstat (int desc, struct stat * buf) /* determine rwx permissions */ if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) - permission = _S_IREAD; + permission = S_IREAD; else - permission = _S_IREAD | _S_IWRITE; + permission = S_IREAD | S_IWRITE; if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - permission |= _S_IEXEC; + permission |= S_IEXEC; else { #if 0 /* no way of knowing the filename */ char * p = strrchr (name, '.'); if (p != NULL && - (stricmp (p, ".exe") == 0 || - stricmp (p, ".com") == 0 || - stricmp (p, ".bat") == 0 || - stricmp (p, ".cmd") == 0)) - permission |= _S_IEXEC; + (xstrcasecmp (p, ".exe") == 0 || + xstrcasecmp (p, ".com") == 0 || + xstrcasecmp (p, ".bat") == 0 || + xstrcasecmp (p, ".cmd") == 0)) + permission |= S_IEXEC; #endif } @@ -2857,6 +3636,709 @@ utime (const char *name, struct utimbuf *times) return 0; } + +/* Support for browsing other processes and their attributes. See + process.c for the Lisp bindings. */ + +/* Helper wrapper functions. */ + +HANDLE WINAPI create_toolhelp32_snapshot( + DWORD Flags, + DWORD Ignored) +{ + static CreateToolhelp32Snapshot_Proc s_pfn_Create_Toolhelp32_Snapshot = NULL; + + if (g_b_init_create_toolhelp32_snapshot == 0) + { + g_b_init_create_toolhelp32_snapshot = 1; + s_pfn_Create_Toolhelp32_Snapshot = (CreateToolhelp32Snapshot_Proc) + GetProcAddress (GetModuleHandle ("kernel32.dll"), + "CreateToolhelp32Snapshot"); + } + if (s_pfn_Create_Toolhelp32_Snapshot == NULL) + { + return INVALID_HANDLE_VALUE; + } + return (s_pfn_Create_Toolhelp32_Snapshot (Flags, Ignored)); +} + +BOOL WINAPI process32_first( + HANDLE hSnapshot, + LPPROCESSENTRY32 lppe) +{ + static Process32First_Proc s_pfn_Process32_First = NULL; + + if (g_b_init_process32_first == 0) + { + g_b_init_process32_first = 1; + s_pfn_Process32_First = (Process32First_Proc) + GetProcAddress (GetModuleHandle ("kernel32.dll"), + "Process32First"); + } + if (s_pfn_Process32_First == NULL) + { + return FALSE; + } + return (s_pfn_Process32_First (hSnapshot, lppe)); +} + +BOOL WINAPI process32_next( + HANDLE hSnapshot, + LPPROCESSENTRY32 lppe) +{ + static Process32Next_Proc s_pfn_Process32_Next = NULL; + + if (g_b_init_process32_next == 0) + { + g_b_init_process32_next = 1; + s_pfn_Process32_Next = (Process32Next_Proc) + GetProcAddress (GetModuleHandle ("kernel32.dll"), + "Process32Next"); + } + if (s_pfn_Process32_Next == NULL) + { + return FALSE; + } + return (s_pfn_Process32_Next (hSnapshot, lppe)); +} + +BOOL WINAPI open_thread_token ( + HANDLE ThreadHandle, + DWORD DesiredAccess, + BOOL OpenAsSelf, + PHANDLE TokenHandle) +{ + static OpenThreadToken_Proc s_pfn_Open_Thread_Token = NULL; + HMODULE hm_advapi32 = NULL; + if (is_windows_9x () == TRUE) + { + SetLastError (ERROR_NOT_SUPPORTED); + return FALSE; + } + if (g_b_init_open_thread_token == 0) + { + g_b_init_open_thread_token = 1; + hm_advapi32 = LoadLibrary ("Advapi32.dll"); + s_pfn_Open_Thread_Token = + (OpenThreadToken_Proc) GetProcAddress (hm_advapi32, "OpenThreadToken"); + } + if (s_pfn_Open_Thread_Token == NULL) + { + SetLastError (ERROR_NOT_SUPPORTED); + return FALSE; + } + return ( + s_pfn_Open_Thread_Token ( + ThreadHandle, + DesiredAccess, + OpenAsSelf, + TokenHandle) + ); +} + +BOOL WINAPI impersonate_self ( + SECURITY_IMPERSONATION_LEVEL ImpersonationLevel) +{ + static ImpersonateSelf_Proc s_pfn_Impersonate_Self = NULL; + HMODULE hm_advapi32 = NULL; + if (is_windows_9x () == TRUE) + { + return FALSE; + } + if (g_b_init_impersonate_self == 0) + { + g_b_init_impersonate_self = 1; + hm_advapi32 = LoadLibrary ("Advapi32.dll"); + s_pfn_Impersonate_Self = + (ImpersonateSelf_Proc) GetProcAddress (hm_advapi32, "ImpersonateSelf"); + } + if (s_pfn_Impersonate_Self == NULL) + { + return FALSE; + } + return s_pfn_Impersonate_Self (ImpersonationLevel); +} + +BOOL WINAPI revert_to_self (void) +{ + static RevertToSelf_Proc s_pfn_Revert_To_Self = NULL; + HMODULE hm_advapi32 = NULL; + if (is_windows_9x () == TRUE) + { + return FALSE; + } + if (g_b_init_revert_to_self == 0) + { + g_b_init_revert_to_self = 1; + hm_advapi32 = LoadLibrary ("Advapi32.dll"); + s_pfn_Revert_To_Self = + (RevertToSelf_Proc) GetProcAddress (hm_advapi32, "RevertToSelf"); + } + if (s_pfn_Revert_To_Self == NULL) + { + return FALSE; + } + return s_pfn_Revert_To_Self (); +} + +BOOL WINAPI get_process_memory_info ( + HANDLE h_proc, + PPROCESS_MEMORY_COUNTERS mem_counters, + DWORD bufsize) +{ + static GetProcessMemoryInfo_Proc s_pfn_Get_Process_Memory_Info = NULL; + HMODULE hm_psapi = NULL; + if (is_windows_9x () == TRUE) + { + return FALSE; + } + if (g_b_init_get_process_memory_info == 0) + { + g_b_init_get_process_memory_info = 1; + hm_psapi = LoadLibrary ("Psapi.dll"); + if (hm_psapi) + s_pfn_Get_Process_Memory_Info = (GetProcessMemoryInfo_Proc) + GetProcAddress (hm_psapi, "GetProcessMemoryInfo"); + } + if (s_pfn_Get_Process_Memory_Info == NULL) + { + return FALSE; + } + return s_pfn_Get_Process_Memory_Info (h_proc, mem_counters, bufsize); +} + +BOOL WINAPI get_process_working_set_size ( + HANDLE h_proc, + DWORD *minrss, + DWORD *maxrss) +{ + static GetProcessWorkingSetSize_Proc + s_pfn_Get_Process_Working_Set_Size = NULL; + + if (is_windows_9x () == TRUE) + { + return FALSE; + } + if (g_b_init_get_process_working_set_size == 0) + { + g_b_init_get_process_working_set_size = 1; + s_pfn_Get_Process_Working_Set_Size = (GetProcessWorkingSetSize_Proc) + GetProcAddress (GetModuleHandle ("kernel32.dll"), + "GetProcessWorkingSetSize"); + } + if (s_pfn_Get_Process_Working_Set_Size == NULL) + { + return FALSE; + } + return s_pfn_Get_Process_Working_Set_Size (h_proc, minrss, maxrss); +} + +BOOL WINAPI global_memory_status ( + MEMORYSTATUS *buf) +{ + static GlobalMemoryStatus_Proc s_pfn_Global_Memory_Status = NULL; + + if (is_windows_9x () == TRUE) + { + return FALSE; + } + if (g_b_init_global_memory_status == 0) + { + g_b_init_global_memory_status = 1; + s_pfn_Global_Memory_Status = (GlobalMemoryStatus_Proc) + GetProcAddress (GetModuleHandle ("kernel32.dll"), + "GlobalMemoryStatus"); + } + if (s_pfn_Global_Memory_Status == NULL) + { + return FALSE; + } + return s_pfn_Global_Memory_Status (buf); +} + +BOOL WINAPI global_memory_status_ex ( + MEMORY_STATUS_EX *buf) +{ + static GlobalMemoryStatusEx_Proc s_pfn_Global_Memory_Status_Ex = NULL; + + if (is_windows_9x () == TRUE) + { + return FALSE; + } + if (g_b_init_global_memory_status_ex == 0) + { + g_b_init_global_memory_status_ex = 1; + s_pfn_Global_Memory_Status_Ex = (GlobalMemoryStatusEx_Proc) + GetProcAddress (GetModuleHandle ("kernel32.dll"), + "GlobalMemoryStatusEx"); + } + if (s_pfn_Global_Memory_Status_Ex == NULL) + { + return FALSE; + } + return s_pfn_Global_Memory_Status_Ex (buf); +} + +Lisp_Object +list_system_processes () +{ + struct gcpro gcpro1; + Lisp_Object proclist = Qnil; + HANDLE h_snapshot; + + h_snapshot = create_toolhelp32_snapshot (TH32CS_SNAPPROCESS, 0); + + if (h_snapshot != INVALID_HANDLE_VALUE) + { + PROCESSENTRY32 proc_entry; + DWORD proc_id; + BOOL res; + + GCPRO1 (proclist); + + proc_entry.dwSize = sizeof (PROCESSENTRY32); + for (res = process32_first (h_snapshot, &proc_entry); res; + res = process32_next (h_snapshot, &proc_entry)) + { + proc_id = proc_entry.th32ProcessID; + proclist = Fcons (make_fixnum_or_float (proc_id), proclist); + } + + CloseHandle (h_snapshot); + UNGCPRO; + proclist = Fnreverse (proclist); + } + + return proclist; +} + +static int +enable_privilege (LPCTSTR priv_name, BOOL enable_p, TOKEN_PRIVILEGES *old_priv) +{ + TOKEN_PRIVILEGES priv; + DWORD priv_size = sizeof (priv); + DWORD opriv_size = sizeof (*old_priv); + HANDLE h_token = NULL; + HANDLE h_thread = GetCurrentThread (); + int ret_val = 0; + BOOL res; + + res = open_thread_token (h_thread, + TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, + FALSE, &h_token); + if (!res && GetLastError () == ERROR_NO_TOKEN) + { + if (impersonate_self (SecurityImpersonation)) + res = open_thread_token (h_thread, + TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, + FALSE, &h_token); + } + if (res) + { + priv.PrivilegeCount = 1; + priv.Privileges[0].Attributes = enable_p ? SE_PRIVILEGE_ENABLED : 0; + LookupPrivilegeValue (NULL, priv_name, &priv.Privileges[0].Luid); + if (AdjustTokenPrivileges (h_token, FALSE, &priv, priv_size, + old_priv, &opriv_size) + && GetLastError () != ERROR_NOT_ALL_ASSIGNED) + ret_val = 1; + } + if (h_token) + CloseHandle (h_token); + + return ret_val; +} + +static int +restore_privilege (TOKEN_PRIVILEGES *priv) +{ + DWORD priv_size = sizeof (*priv); + HANDLE h_token = NULL; + int ret_val = 0; + + if (open_thread_token (GetCurrentThread (), + TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, + FALSE, &h_token)) + { + if (AdjustTokenPrivileges (h_token, FALSE, priv, priv_size, NULL, NULL) + && GetLastError () != ERROR_NOT_ALL_ASSIGNED) + ret_val = 1; + } + if (h_token) + CloseHandle (h_token); + + return ret_val; +} + +static Lisp_Object +ltime (time_sec, time_usec) + long time_sec, time_usec; +{ + return list3 (make_number ((time_sec >> 16) & 0xffff), + make_number (time_sec & 0xffff), + make_number (time_usec)); +} + +#define U64_TO_LISP_TIME(time) ltime ((time) / 1000000L, (time) % 1000000L) + +static int +process_times (h_proc, ctime, etime, stime, utime, ttime, pcpu) + HANDLE h_proc; + Lisp_Object *ctime, *etime, *stime, *utime, *ttime; + double *pcpu; +{ + FILETIME ft_creation, ft_exit, ft_kernel, ft_user, ft_current; + ULONGLONG tem1, tem2, tem3, tem; + + if (!h_proc + || !get_process_times_fn + || !(*get_process_times_fn)(h_proc, &ft_creation, &ft_exit, + &ft_kernel, &ft_user)) + return 0; + + GetSystemTimeAsFileTime (&ft_current); + + FILETIME_TO_U64 (tem1, ft_kernel); + tem1 /= 10L; + *stime = U64_TO_LISP_TIME (tem1); + + FILETIME_TO_U64 (tem2, ft_user); + tem2 /= 10L; + *utime = U64_TO_LISP_TIME (tem2); + + tem3 = tem1 + tem2; + *ttime = U64_TO_LISP_TIME (tem3); + + FILETIME_TO_U64 (tem, ft_creation); + /* Process no 4 (System) returns zero creation time. */ + if (tem) + tem = (tem - utc_base) / 10L; + *ctime = U64_TO_LISP_TIME (tem); + + if (tem) + { + FILETIME_TO_U64 (tem3, ft_current); + tem = (tem3 - utc_base) / 10L - tem; + } + *etime = U64_TO_LISP_TIME (tem); + + if (tem) + { + *pcpu = 100.0 * (tem1 + tem2) / tem; + if (*pcpu > 100) + *pcpu = 100.0; + } + else + *pcpu = 0; + + return 1; +} + +Lisp_Object +system_process_attributes (pid) + Lisp_Object pid; +{ + struct gcpro gcpro1, gcpro2, gcpro3; + Lisp_Object attrs = Qnil; + Lisp_Object cmd_str, decoded_cmd, tem; + HANDLE h_snapshot, h_proc; + DWORD proc_id; + int found_proc = 0; + char uname[UNLEN+1], gname[GNLEN+1], domain[1025]; + DWORD ulength = sizeof (uname), dlength = sizeof (domain), needed; + DWORD glength = sizeof (gname); + HANDLE token = NULL; + SID_NAME_USE user_type; + unsigned char *buf = NULL; + DWORD blen = 0; + TOKEN_USER user_token; + TOKEN_PRIMARY_GROUP group_token; + unsigned euid; + unsigned egid; + DWORD sess; + PROCESS_MEMORY_COUNTERS mem; + PROCESS_MEMORY_COUNTERS_EX mem_ex; + DWORD minrss, maxrss; + MEMORYSTATUS memst; + MEMORY_STATUS_EX memstex; + double totphys = 0.0; + Lisp_Object ctime, stime, utime, etime, ttime; + double pcpu; + BOOL result = FALSE; + + CHECK_NUMBER_OR_FLOAT (pid); + proc_id = FLOATP (pid) ? XFLOAT_DATA (pid) : XINT (pid); + + h_snapshot = create_toolhelp32_snapshot (TH32CS_SNAPPROCESS, 0); + + GCPRO3 (attrs, decoded_cmd, tem); + + if (h_snapshot != INVALID_HANDLE_VALUE) + { + PROCESSENTRY32 pe; + BOOL res; + + pe.dwSize = sizeof (PROCESSENTRY32); + for (res = process32_first (h_snapshot, &pe); res; + res = process32_next (h_snapshot, &pe)) + { + if (proc_id == pe.th32ProcessID) + { + if (proc_id == 0) + decoded_cmd = build_string ("Idle"); + else + { + /* Decode the command name from locale-specific + encoding. */ + cmd_str = make_unibyte_string (pe.szExeFile, + strlen (pe.szExeFile)); + decoded_cmd = + code_convert_string_norecord (cmd_str, + Vlocale_coding_system, 0); + } + attrs = Fcons (Fcons (Qcomm, decoded_cmd), attrs); + attrs = Fcons (Fcons (Qppid, + make_fixnum_or_float (pe.th32ParentProcessID)), + attrs); + attrs = Fcons (Fcons (Qpri, make_number (pe.pcPriClassBase)), + attrs); + attrs = Fcons (Fcons (Qthcount, + make_fixnum_or_float (pe.cntThreads)), + attrs); + found_proc = 1; + break; + } + } + + CloseHandle (h_snapshot); + } + + if (!found_proc) + { + UNGCPRO; + return Qnil; + } + + h_proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + FALSE, proc_id); + /* If we were denied a handle to the process, try again after + enabling the SeDebugPrivilege in our process. */ + if (!h_proc) + { + TOKEN_PRIVILEGES priv_current; + + if (enable_privilege (SE_DEBUG_NAME, TRUE, &priv_current)) + { + h_proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + FALSE, proc_id); + restore_privilege (&priv_current); + revert_to_self (); + } + } + if (h_proc) + { + result = open_process_token (h_proc, TOKEN_QUERY, &token); + if (result) + { + result = get_token_information (token, TokenUser, NULL, 0, &blen); + if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER) + { + buf = xmalloc (blen); + result = get_token_information (token, TokenUser, + (LPVOID)buf, blen, &needed); + if (result) + { + memcpy (&user_token, buf, sizeof (user_token)); + if (!w32_cached_id (user_token.User.Sid, &euid, uname)) + { + euid = get_rid (user_token.User.Sid); + result = lookup_account_sid (NULL, user_token.User.Sid, + uname, &ulength, + domain, &dlength, + &user_type); + if (result) + w32_add_to_cache (user_token.User.Sid, euid, uname); + else + { + strcpy (uname, "unknown"); + result = TRUE; + } + } + ulength = strlen (uname); + } + } + } + if (result) + { + /* Determine a reasonable euid and gid values. */ + if (xstrcasecmp ("administrator", uname) == 0) + { + euid = 500; /* well-known Administrator uid */ + egid = 513; /* well-known None gid */ + } + else + { + /* Get group id and name. */ + result = get_token_information (token, TokenPrimaryGroup, + (LPVOID)buf, blen, &needed); + if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER) + { + buf = xrealloc (buf, blen = needed); + result = get_token_information (token, TokenPrimaryGroup, + (LPVOID)buf, blen, &needed); + } + if (result) + { + memcpy (&group_token, buf, sizeof (group_token)); + if (!w32_cached_id (group_token.PrimaryGroup, &egid, gname)) + { + egid = get_rid (group_token.PrimaryGroup); + dlength = sizeof (domain); + result = + lookup_account_sid (NULL, group_token.PrimaryGroup, + gname, &glength, NULL, &dlength, + &user_type); + if (result) + w32_add_to_cache (group_token.PrimaryGroup, + egid, gname); + else + { + strcpy (gname, "None"); + result = TRUE; + } + } + glength = strlen (gname); + } + } + } + xfree (buf); + } + if (!result) + { + if (!is_windows_9x ()) + { + /* We couldn't open the process token, presumably because of + insufficient access rights. Assume this process is run + by the system. */ + strcpy (uname, "SYSTEM"); + strcpy (gname, "None"); + euid = 18; /* SYSTEM */ + egid = 513; /* None */ + glength = strlen (gname); + ulength = strlen (uname); + } + /* If we are running under Windows 9X, where security calls are + not supported, we assume all processes are run by the current + user. */ + else if (GetUserName (uname, &ulength)) + { + if (xstrcasecmp ("administrator", uname) == 0) + euid = 0; + else + euid = 123; + egid = euid; + strcpy (gname, "None"); + glength = strlen (gname); + ulength = strlen (uname); + } + else + { + euid = 123; + egid = 123; + strcpy (uname, "administrator"); + ulength = strlen (uname); + strcpy (gname, "None"); + glength = strlen (gname); + } + if (token) + CloseHandle (token); + } + + attrs = Fcons (Fcons (Qeuid, make_fixnum_or_float (euid)), attrs); + tem = make_unibyte_string (uname, ulength); + attrs = Fcons (Fcons (Quser, + code_convert_string_norecord (tem, Vlocale_coding_system, 0)), + attrs); + attrs = Fcons (Fcons (Qegid, make_fixnum_or_float (egid)), attrs); + tem = make_unibyte_string (gname, glength); + attrs = Fcons (Fcons (Qgroup, + code_convert_string_norecord (tem, Vlocale_coding_system, 0)), + attrs); + + if (global_memory_status_ex (&memstex)) +#if __GNUC__ || (defined (_MSC_VER) && _MSC_VER >= 1300) + totphys = memstex.ullTotalPhys / 1024.0; +#else + /* Visual Studio 6 cannot convert an unsigned __int64 type to + double, so we need to do this for it... */ + { + DWORD tot_hi = memstex.ullTotalPhys >> 32; + DWORD tot_md = (memstex.ullTotalPhys & 0x00000000ffffffff) >> 10; + DWORD tot_lo = memstex.ullTotalPhys % 1024; + + totphys = tot_hi * 4194304.0 + tot_md + tot_lo / 1024.0; + } +#endif /* __GNUC__ || _MSC_VER >= 1300 */ + else if (global_memory_status (&memst)) + totphys = memst.dwTotalPhys / 1024.0; + + if (h_proc + && get_process_memory_info (h_proc, (PROCESS_MEMORY_COUNTERS *)&mem_ex, + sizeof (mem_ex))) + { + DWORD rss = mem_ex.WorkingSetSize / 1024; + + attrs = Fcons (Fcons (Qmajflt, + make_fixnum_or_float (mem_ex.PageFaultCount)), + attrs); + attrs = Fcons (Fcons (Qvsize, + make_fixnum_or_float (mem_ex.PrivateUsage / 1024)), + attrs); + attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (rss)), attrs); + if (totphys) + attrs = Fcons (Fcons (Qpmem, make_float (100. * rss / totphys)), attrs); + } + else if (h_proc + && get_process_memory_info (h_proc, &mem, sizeof (mem))) + { + DWORD rss = mem_ex.WorkingSetSize / 1024; + + attrs = Fcons (Fcons (Qmajflt, + make_fixnum_or_float (mem.PageFaultCount)), + attrs); + attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (rss)), attrs); + if (totphys) + attrs = Fcons (Fcons (Qpmem, make_float (100. * rss / totphys)), attrs); + } + else if (h_proc + && get_process_working_set_size (h_proc, &minrss, &maxrss)) + { + DWORD rss = maxrss / 1024; + + attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (maxrss / 1024)), attrs); + if (totphys) + attrs = Fcons (Fcons (Qpmem, make_float (100. * rss / totphys)), attrs); + } + + if (process_times (h_proc, &ctime, &etime, &stime, &utime, &ttime, &pcpu)) + { + attrs = Fcons (Fcons (Qutime, utime), attrs); + attrs = Fcons (Fcons (Qstime, stime), attrs); + attrs = Fcons (Fcons (Qtime, ttime), attrs); + attrs = Fcons (Fcons (Qstart, ctime), attrs); + attrs = Fcons (Fcons (Qetime, etime), attrs); + attrs = Fcons (Fcons (Qpcpu, make_float (pcpu)), attrs); + } + + /* FIXME: Retrieve command line by walking the PEB of the process. */ + + if (h_proc) + CloseHandle (h_proc); + UNGCPRO; + return attrs; +} + + #ifdef HAVE_SOCKETS /* Wrappers for winsock functions to map between our file descriptors @@ -3016,7 +4498,7 @@ init_winsock (int load_now) int h_errno = 0; -/* function to set h_errno for compatability; map winsock error codes to +/* function to set h_errno for compatibility; map winsock error codes to normal system codes where they overlap (non-overlapping definitions are already in */ static void @@ -3767,10 +5249,10 @@ _sys_read_ahead (int fd) if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY) return STATUS_READ_ERROR; - if ((fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET)) == 0 + if ((fd_info[fd].flags & (FILE_PIPE | FILE_SERIAL | FILE_SOCKET)) == 0 || (fd_info[fd].flags & FILE_READ) == 0) { - DebPrint (("_sys_read_ahead: internal error: fd %d is not a pipe or socket!\n", fd)); + DebPrint (("_sys_read_ahead: internal error: fd %d is not a pipe, serial port, or socket!\n", fd)); abort (); } @@ -3784,7 +5266,7 @@ _sys_read_ahead (int fd) reporting that input is available; we need this because Windows 95 connects DOS programs to pipes by making the pipe appear to be the normal console stdout - as a result most DOS programs will - write to stdout without buffering, ie. one character at a + write to stdout without buffering, ie. one character at a time. Even some W32 programs do this - "dir" in a command shell on NT is very slow if we don't do this. */ if (rc > 0) @@ -3800,6 +5282,29 @@ _sys_read_ahead (int fd) Sleep (0); } } + else if (fd_info[fd].flags & FILE_SERIAL) + { + HANDLE hnd = fd_info[fd].hnd; + OVERLAPPED *ovl = &fd_info[fd].cp->ovl_read; + COMMTIMEOUTS ct; + + /* Configure timeouts for blocking read. */ + if (!GetCommTimeouts (hnd, &ct)) + return STATUS_READ_ERROR; + ct.ReadIntervalTimeout = 0; + ct.ReadTotalTimeoutMultiplier = 0; + ct.ReadTotalTimeoutConstant = 0; + if (!SetCommTimeouts (hnd, &ct)) + return STATUS_READ_ERROR; + + if (!ReadFile (hnd, &cp->chr, sizeof (char), (DWORD*) &rc, ovl)) + { + if (GetLastError () != ERROR_IO_PENDING) + return STATUS_READ_ERROR; + if (!GetOverlappedResult (hnd, ovl, (DWORD*) &rc, TRUE)) + return STATUS_READ_ERROR; + } + } #ifdef HAVE_SOCKETS else if (fd_info[fd].flags & FILE_SOCKET) { @@ -3871,7 +5376,7 @@ sys_read (int fd, char * buffer, unsigned int count) return -1; } - if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET)) + if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET | FILE_SERIAL)) { child_process *cp = fd_info[fd].cp; @@ -3942,6 +5447,52 @@ sys_read (int fd, char * buffer, unsigned int count) if (to_read > 0) nchars += _read (fd, buffer, to_read); } + else if (fd_info[fd].flags & FILE_SERIAL) + { + HANDLE hnd = fd_info[fd].hnd; + OVERLAPPED *ovl = &fd_info[fd].cp->ovl_read; + DWORD err = 0; + int rc = 0; + COMMTIMEOUTS ct; + + if (count > 0) + { + /* Configure timeouts for non-blocking read. */ + if (!GetCommTimeouts (hnd, &ct)) + { + errno = EIO; + return -1; + } + ct.ReadIntervalTimeout = MAXDWORD; + ct.ReadTotalTimeoutMultiplier = 0; + ct.ReadTotalTimeoutConstant = 0; + if (!SetCommTimeouts (hnd, &ct)) + { + errno = EIO; + return -1; + } + + if (!ResetEvent (ovl->hEvent)) + { + errno = EIO; + return -1; + } + if (!ReadFile (hnd, buffer, count, (DWORD*) &rc, ovl)) + { + if (GetLastError () != ERROR_IO_PENDING) + { + errno = EIO; + return -1; + } + if (!GetOverlappedResult (hnd, ovl, (DWORD*) &rc, TRUE)) + { + errno = EIO; + return -1; + } + } + nchars += rc; + } + } #ifdef HAVE_SOCKETS else /* FILE_SOCKET */ { @@ -4003,6 +5554,9 @@ sys_read (int fd, char * buffer, unsigned int count) return nchars; } +/* From w32xfns.c */ +extern HANDLE interrupt_handle; + /* For now, don't bother with a non-blocking mode */ int sys_write (int fd, const void * buffer, unsigned int count) @@ -4015,7 +5569,7 @@ sys_write (int fd, const void * buffer, unsigned int count) return -1; } - if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET)) + if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET | FILE_SERIAL)) { if ((fd_info[fd].flags & FILE_WRITE) == 0) { @@ -4056,6 +5610,42 @@ sys_write (int fd, const void * buffer, unsigned int count) } } + if (fd < MAXDESC && fd_info[fd].flags & FILE_SERIAL) + { + HANDLE hnd = (HANDLE) _get_osfhandle (fd); + OVERLAPPED *ovl = &fd_info[fd].cp->ovl_write; + HANDLE wait_hnd[2] = { interrupt_handle, ovl->hEvent }; + DWORD active = 0; + + if (!WriteFile (hnd, buffer, count, (DWORD*) &nchars, ovl)) + { + if (GetLastError () != ERROR_IO_PENDING) + { + errno = EIO; + return -1; + } + if (detect_input_pending ()) + active = MsgWaitForMultipleObjects (2, wait_hnd, FALSE, INFINITE, + QS_ALLINPUT); + else + active = WaitForMultipleObjects (2, wait_hnd, FALSE, INFINITE); + if (active == WAIT_OBJECT_0) + { /* User pressed C-g, cancel write, then leave. Don't bother + cleaning up as we may only get stuck in buggy drivers. */ + PurgeComm (hnd, PURGE_TXABORT | PURGE_TXCLEAR); + CancelIo (hnd); + errno = EIO; + return -1; + } + if (active == WAIT_OBJECT_0 + 1 + && !GetOverlappedResult (hnd, ovl, (DWORD*) &nchars, TRUE)) + { + errno = EIO; + return -1; + } + } + } + else #ifdef HAVE_SOCKETS if (fd < MAXDESC && fd_info[fd].flags & FILE_SOCKET) { @@ -4302,11 +5892,224 @@ globals_of_w32 () g_b_init_get_sid_identifier_authority = 0; g_b_init_get_sid_sub_authority = 0; g_b_init_get_sid_sub_authority_count = 0; + g_b_init_get_file_security = 0; + g_b_init_get_security_descriptor_owner = 0; + g_b_init_get_security_descriptor_group = 0; + g_b_init_is_valid_sid = 0; + g_b_init_create_toolhelp32_snapshot = 0; + g_b_init_process32_first = 0; + g_b_init_process32_next = 0; + g_b_init_open_thread_token = 0; + g_b_init_impersonate_self = 0; + g_b_init_revert_to_self = 0; + g_b_init_get_process_memory_info = 0; + g_b_init_get_process_working_set_size = 0; + g_b_init_global_memory_status = 0; + g_b_init_global_memory_status_ex = 0; + g_b_init_equal_sid = 0; + g_b_init_copy_sid = 0; + g_b_init_get_length_sid = 0; + g_b_init_get_native_system_info = 0; + g_b_init_get_system_times = 0; + num_of_processors = 0; /* The following sets a handler for shutdown notifications for console apps. This actually applies to Emacs in both console and GUI modes, since we had to fool windows into thinking emacs is a console application to get console mode to work. */ SetConsoleCtrlHandler(shutdown_handler, TRUE); + + /* "None" is the default group name on standalone workstations. */ + strcpy (dflt_group_name, "None"); +} + +/* For make-serial-process */ +int serial_open (char *port) +{ + HANDLE hnd; + child_process *cp; + int fd = -1; + + hnd = CreateFile (port, GENERIC_READ | GENERIC_WRITE, 0, 0, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); + if (hnd == INVALID_HANDLE_VALUE) + error ("Could not open %s", port); + fd = (int) _open_osfhandle ((int) hnd, 0); + if (fd == -1) + error ("Could not open %s", port); + + cp = new_child (); + if (!cp) + error ("Could not create child process"); + cp->fd = fd; + cp->status = STATUS_READ_ACKNOWLEDGED; + fd_info[ fd ].hnd = hnd; + fd_info[ fd ].flags |= + FILE_READ | FILE_WRITE | FILE_BINARY | FILE_SERIAL; + if (fd_info[ fd ].cp != NULL) + { + error ("fd_info[fd = %d] is already in use", fd); + } + fd_info[ fd ].cp = cp; + cp->ovl_read.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); + if (cp->ovl_read.hEvent == NULL) + error ("Could not create read event"); + cp->ovl_write.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); + if (cp->ovl_write.hEvent == NULL) + error ("Could not create write event"); + + return fd; +} + +/* For serial-process-configure */ +void +serial_configure (struct Lisp_Process *p, + Lisp_Object contact) +{ + Lisp_Object childp2 = Qnil; + Lisp_Object tem = Qnil; + HANDLE hnd; + DCB dcb; + COMMTIMEOUTS ct; + char summary[4] = "???"; /* This usually becomes "8N1". */ + + if ((fd_info[ p->outfd ].flags & FILE_SERIAL) == 0) + error ("Not a serial process"); + hnd = fd_info[ p->outfd ].hnd; + + childp2 = Fcopy_sequence (p->childp); + + /* Initialize timeouts for blocking read and blocking write. */ + if (!GetCommTimeouts (hnd, &ct)) + error ("GetCommTimeouts() failed"); + ct.ReadIntervalTimeout = 0; + ct.ReadTotalTimeoutMultiplier = 0; + ct.ReadTotalTimeoutConstant = 0; + ct.WriteTotalTimeoutMultiplier = 0; + ct.WriteTotalTimeoutConstant = 0; + if (!SetCommTimeouts (hnd, &ct)) + error ("SetCommTimeouts() failed"); + /* Read port attributes and prepare default configuration. */ + memset (&dcb, 0, sizeof (dcb)); + dcb.DCBlength = sizeof (DCB); + if (!GetCommState (hnd, &dcb)) + error ("GetCommState() failed"); + dcb.fBinary = TRUE; + dcb.fNull = FALSE; + dcb.fAbortOnError = FALSE; + /* dcb.XonLim and dcb.XoffLim are set by GetCommState() */ + dcb.ErrorChar = 0; + dcb.EofChar = 0; + dcb.EvtChar = 0; + + /* Configure speed. */ + if (!NILP (Fplist_member (contact, QCspeed))) + tem = Fplist_get (contact, QCspeed); + else + tem = Fplist_get (p->childp, QCspeed); + CHECK_NUMBER (tem); + dcb.BaudRate = XINT (tem); + childp2 = Fplist_put (childp2, QCspeed, tem); + + /* Configure bytesize. */ + if (!NILP (Fplist_member (contact, QCbytesize))) + tem = Fplist_get (contact, QCbytesize); + else + tem = Fplist_get (p->childp, QCbytesize); + if (NILP (tem)) + tem = make_number (8); + CHECK_NUMBER (tem); + if (XINT (tem) != 7 && XINT (tem) != 8) + error (":bytesize must be nil (8), 7, or 8"); + dcb.ByteSize = XINT (tem); + summary[0] = XINT (tem) + '0'; + childp2 = Fplist_put (childp2, QCbytesize, tem); + + /* Configure parity. */ + if (!NILP (Fplist_member (contact, QCparity))) + tem = Fplist_get (contact, QCparity); + else + tem = Fplist_get (p->childp, QCparity); + if (!NILP (tem) && !EQ (tem, Qeven) && !EQ (tem, Qodd)) + error (":parity must be nil (no parity), `even', or `odd'"); + dcb.fParity = FALSE; + dcb.Parity = NOPARITY; + dcb.fErrorChar = FALSE; + if (NILP (tem)) + { + summary[1] = 'N'; + } + else if (EQ (tem, Qeven)) + { + summary[1] = 'E'; + dcb.fParity = TRUE; + dcb.Parity = EVENPARITY; + dcb.fErrorChar = TRUE; + } + else if (EQ (tem, Qodd)) + { + summary[1] = 'O'; + dcb.fParity = TRUE; + dcb.Parity = ODDPARITY; + dcb.fErrorChar = TRUE; + } + childp2 = Fplist_put (childp2, QCparity, tem); + + /* Configure stopbits. */ + if (!NILP (Fplist_member (contact, QCstopbits))) + tem = Fplist_get (contact, QCstopbits); + else + tem = Fplist_get (p->childp, QCstopbits); + if (NILP (tem)) + tem = make_number (1); + CHECK_NUMBER (tem); + if (XINT (tem) != 1 && XINT (tem) != 2) + error (":stopbits must be nil (1 stopbit), 1, or 2"); + summary[2] = XINT (tem) + '0'; + if (XINT (tem) == 1) + dcb.StopBits = ONESTOPBIT; + else if (XINT (tem) == 2) + dcb.StopBits = TWOSTOPBITS; + childp2 = Fplist_put (childp2, QCstopbits, tem); + + /* Configure flowcontrol. */ + if (!NILP (Fplist_member (contact, QCflowcontrol))) + tem = Fplist_get (contact, QCflowcontrol); + else + tem = Fplist_get (p->childp, QCflowcontrol); + if (!NILP (tem) && !EQ (tem, Qhw) && !EQ (tem, Qsw)) + error (":flowcontrol must be nil (no flowcontrol), `hw', or `sw'"); + dcb.fOutxCtsFlow = FALSE; + dcb.fOutxDsrFlow = FALSE; + dcb.fDtrControl = DTR_CONTROL_DISABLE; + dcb.fDsrSensitivity = FALSE; + dcb.fTXContinueOnXoff = FALSE; + dcb.fOutX = FALSE; + dcb.fInX = FALSE; + dcb.fRtsControl = RTS_CONTROL_DISABLE; + dcb.XonChar = 17; /* Control-Q */ + dcb.XoffChar = 19; /* Control-S */ + if (NILP (tem)) + { + /* Already configured. */ + } + else if (EQ (tem, Qhw)) + { + dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; + dcb.fOutxCtsFlow = TRUE; + } + else if (EQ (tem, Qsw)) + { + dcb.fOutX = TRUE; + dcb.fInX = TRUE; + } + childp2 = Fplist_put (childp2, QCflowcontrol, tem); + + /* Activate configuration. */ + if (!SetCommState (hnd, &dcb)) + error ("SetCommState() failed"); + + childp2 = Fplist_put (childp2, QCsummary, build_string (summary)); + p->childp = childp2; } /* end of w32.c */