]> code.delx.au - gnu-emacs/blobdiff - src/w32.c
Merge from emacs--rel--22
[gnu-emacs] / src / w32.c
index 894160a275d55a59e08e656a9902fa1196189b96..25756087a636ed55fd5b0853d1244ea42c5da7c6 100644 (file)
--- a/src/w32.c
+++ b/src/w32.c
@@ -73,6 +73,7 @@ Boston, MA 02110-1301, USA.
 #define _ANONYMOUS_STRUCT
 #endif
 #include <windows.h>
+#include <lmcons.h>
 #include <shlobj.h>
 
 #ifdef HAVE_SOCKETS    /* TCP connection support, if kernel can do it */
@@ -115,14 +116,22 @@ extern Lisp_Object Qlocal;
 extern int w32_num_mouse_buttons;
 
 \f
-/*
-  Initialization states
- */
+/* Initialization states.
+
+   WARNING: If you add any more such variables for additional APIs,
+            you MUST add initialization for them to globals_of_w32
+            below.  This is because these variables might get set
+            to non-NULL values during dumping, but the dumped Emacs
+            cannot reuse those values, because it could be run on a
+            different version of the OS, where API addresses are
+            different.  */
 static BOOL g_b_init_is_windows_9x;
 static BOOL g_b_init_open_process_token;
 static BOOL g_b_init_get_token_information;
 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;
 
 /*
   BEGIN: Wrapper functions around OpenProcessToken
@@ -140,6 +149,15 @@ typedef BOOL (WINAPI * GetTokenInformation_Proc) (
     LPVOID TokenInformation,
     DWORD TokenInformationLength,
     PDWORD ReturnLength);
+typedef BOOL (WINAPI * GetProcessTimes_Proc) (
+    HANDLE process_handle,
+    LPFILETIME creation_time,
+    LPFILETIME exit_time,
+    LPFILETIME kernel_time,
+    LPFILETIME user_time);
+
+GetProcessTimes_Proc get_process_times_fn = NULL;
+
 #ifdef _UNICODE
 const char * const LookupAccountSid_Name = "LookupAccountSidW";
 #else
@@ -155,6 +173,12 @@ typedef BOOL (WINAPI * LookupAccountSid_Proc) (
     PSID_NAME_USE peUse);
 typedef PSID_IDENTIFIER_AUTHORITY (WINAPI * GetSidIdentifierAuthority_Proc) (
     PSID pSid);
+typedef PDWORD (WINAPI * GetSidSubAuthority_Proc) (
+    PSID pSid,
+    DWORD n);
+typedef PUCHAR (WINAPI * GetSidSubAuthorityCount_Proc) (
+    PSID pSid);
+
 
   /* ** A utility function ** */
 static BOOL
@@ -175,6 +199,46 @@ is_windows_9x ()
   return s_b_ret;
 }
 
+/* Get total user and system times for get-internal-run-time.
+   Returns a list of three integers if the times are provided by the OS
+   (NT derivatives), otherwise it returns the result of current-time. */
+Lisp_Object
+w32_get_internal_run_time ()
+{
+  if (get_process_times_fn)
+    {
+      FILETIME create, exit, kernel, user;
+      HANDLE proc = GetCurrentProcess();
+      if ((*get_process_times_fn) (proc, &create, &exit, &kernel, &user))
+        {
+          LARGE_INTEGER user_int, kernel_int, total;
+          int microseconds;
+          user_int.LowPart = user.dwLowDateTime;
+          user_int.HighPart = user.dwHighDateTime;
+          kernel_int.LowPart = kernel.dwLowDateTime;
+          kernel_int.HighPart = kernel.dwHighDateTime;
+          total.QuadPart = user_int.QuadPart + kernel_int.QuadPart;
+          /* FILETIME is 100 nanosecond increments, Emacs only wants
+             microsecond resolution.  */
+          total.QuadPart /= 10;
+          microseconds = total.QuadPart % 1000000;
+          total.QuadPart /= 1000000;
+
+          /* Sanity check to make sure we can represent the result.  */
+          if (total.HighPart == 0)
+            {
+              int secs = total.LowPart;
+
+              return list3 (make_number ((secs >> 16) & 0xffff),
+                            make_number (secs & 0xffff),
+                            make_number (microseconds));
+            }
+        }
+    }
+
+  return Fcurrent_time ();
+}
+
   /* ** The wrapper functions ** */
 
 BOOL WINAPI open_process_token (
@@ -303,6 +367,57 @@ PSID_IDENTIFIER_AUTHORITY WINAPI get_sid_identifier_authority (
   return (s_pfn_Get_Sid_Identifier_Authority (pSid));
 }
 
+PDWORD WINAPI get_sid_sub_authority (
+    PSID pSid,
+    DWORD n)
+{
+  static GetSidSubAuthority_Proc s_pfn_Get_Sid_Sub_Authority = NULL;
+  static DWORD zero = 0U;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return &zero;
+    }
+  if (g_b_init_get_sid_sub_authority == 0)
+    {
+      g_b_init_get_sid_sub_authority = 1;
+      hm_advapi32 = LoadLibrary ("Advapi32.dll");
+      s_pfn_Get_Sid_Sub_Authority =
+        (GetSidSubAuthority_Proc) GetProcAddress (
+            hm_advapi32, "GetSidSubAuthority");
+    }
+  if (s_pfn_Get_Sid_Sub_Authority == NULL)
+    {
+      return &zero;
+    }
+  return (s_pfn_Get_Sid_Sub_Authority (pSid, n));
+}
+
+PUCHAR WINAPI get_sid_sub_authority_count (
+    PSID pSid)
+{
+  static GetSidSubAuthorityCount_Proc s_pfn_Get_Sid_Sub_Authority_Count = NULL;
+  static UCHAR zero = 0U;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return &zero;
+    }
+  if (g_b_init_get_sid_sub_authority_count == 0)
+    {
+      g_b_init_get_sid_sub_authority_count = 1;
+      hm_advapi32 = LoadLibrary ("Advapi32.dll");
+      s_pfn_Get_Sid_Sub_Authority_Count =
+        (GetSidSubAuthorityCount_Proc) GetProcAddress (
+            hm_advapi32, "GetSidSubAuthorityCount");
+    }
+  if (s_pfn_Get_Sid_Sub_Authority_Count == NULL)
+    {
+      return &zero;
+    }
+  return (s_pfn_Get_Sid_Sub_Authority_Count (pSid));
+}
+
 /*
   END: Wrapper functions around OpenProcessToken
   and other functions in advapi32.dll that are only
@@ -489,51 +604,60 @@ 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         user_sid[256], name[256], domain[256];
+  char         name[UNLEN+1], domain[1025];
   DWORD        length = sizeof (name), dlength = sizeof (domain), trash;
   HANDLE       token = NULL;
   SID_NAME_USE user_type;
+  unsigned char buf[1024];
+  TOKEN_USER   user_token;
+  TOKEN_PRIMARY_GROUP group_token;
 
   if (open_process_token (GetCurrentProcess (), TOKEN_QUERY, &token)
       && get_token_information (token, TokenUser,
-                               (PVOID) user_sid, sizeof (user_sid), &trash)
-      && lookup_account_sid (NULL, *((PSID *) user_sid), name, &length,
-                            domain, &dlength, &user_type))
+                               (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)))
     {
       strcpy (the_passwd.pw_name, name);
-      /* Determine a reasonable uid value. */
+      /* Determine a reasonable uid value.  */
       if (stricmp ("administrator", name) == 0)
        {
-         the_passwd.pw_uid = 0;
-         the_passwd.pw_gid = 0;
+         the_passwd.pw_uid = 500; /* well-known Administrator uid */
+         the_passwd.pw_gid = 513; /* well-known None gid */
        }
       else
        {
-         SID_IDENTIFIER_AUTHORITY * pSIA;
-
-         pSIA = get_sid_identifier_authority (*((PSID *) user_sid));
-         /* I believe the relative portion is the last 4 bytes (of 6)
-            with msb first. */
-         the_passwd.pw_uid = ((pSIA->Value[2] << 24) +
-                              (pSIA->Value[3] << 16) +
-                              (pSIA->Value[4] << 8)  +
-                              (pSIA->Value[5] << 0));
-         /* restrict to conventional uid range for normal users */
-         the_passwd.pw_uid = the_passwd.pw_uid % 60001;
+         /* 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);
+
+         if (n_subauthorities < 1)
+           the_passwd.pw_uid = 0;      /* the "World" RID */
+         else
+           {
+             the_passwd.pw_uid =
+               *get_sid_sub_authority (user_token.User.Sid,
+                                       n_subauthorities - 1);
+           }
 
          /* Get group id */
          if (get_token_information (token, TokenPrimaryGroup,
-                                    (PVOID) user_sid, sizeof (user_sid), &trash))
+                                    (PVOID)buf, sizeof (buf), &trash))
            {
-             SID_IDENTIFIER_AUTHORITY * pSIA;
-
-             pSIA = get_sid_identifier_authority (*((PSID *) user_sid));
-             the_passwd.pw_gid = ((pSIA->Value[2] << 24) +
-                                  (pSIA->Value[3] << 16) +
-                                  (pSIA->Value[4] << 8)  +
-                                  (pSIA->Value[5] << 0));
-             /* I don't know if this is necessary, but for safety... */
-             the_passwd.pw_gid = the_passwd.pw_gid % 60001;
+             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);
+               }
            }
          else
            the_passwd.pw_gid = the_passwd.pw_uid;
@@ -858,18 +982,6 @@ alarm (int seconds)
   return 0;
 }
 
-void
-unrequest_sigio (void)
-{
-  return;
-}
-
-void
-request_sigio (void)
-{
-  return;
-}
-
 #define REG_ROOT "SOFTWARE\\GNU\\Emacs"
 
 LPBYTE
@@ -1235,12 +1347,6 @@ get_emacs_configuration (void)
       break;
 #endif
 
-#ifdef PROCESSOR_INTEL_860
-    case PROCESSOR_INTEL_860:
-      arch = "i860";
-      break;
-#endif
-
 #ifdef PROCESSOR_MIPS_R2000
     case PROCESSOR_MIPS_R2000:
     case PROCESSOR_MIPS_R3000:
@@ -1818,6 +1924,18 @@ 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;
 
@@ -1924,6 +2042,41 @@ unc_volume_file_attributes (const char *path)
   return attrs;
 }
 
+/* Ensure a network connection is authenticated.  */
+static void
+logon_network_drive (const char *path)
+{
+  NETRESOURCE resource;
+  char share[MAX_PATH];
+  int i, n_slashes;
+  char drive[4];
+
+  sprintf (drive, "%c:\\", path[0]);
+
+  /* Only logon to networked drives.  */
+  if ((!IS_DIRECTORY_SEP (path[0]) || !IS_DIRECTORY_SEP (path[1]))
+      && GetDriveType (drive) != DRIVE_REMOTE)
+    return;
+
+  n_slashes = 2;
+  strncpy (share, path, MAX_PATH);
+  /* Truncate to just server and share name.  */
+  for (i = 2; i < MAX_PATH; i++)
+    {
+      if (IS_DIRECTORY_SEP (share[i]) && ++n_slashes > 3)
+        {
+          share[i] = '\0';
+          break;
+        }
+    }
+
+  resource.dwType = RESOURCETYPE_DISK;
+  resource.lpLocalName = NULL;
+  resource.lpRemoteName = share;
+  resource.lpProvider = NULL;
+
+  WNetAddConnection2 (&resource, NULL, NULL, CONNECT_INTERACTIVE);
+}
 
 /* Shadow some MSVC runtime functions to map requests for long filenames
    to reasonable short names if necessary.  This was originally added to
@@ -1985,7 +2138,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;
 }
@@ -2392,7 +2545,7 @@ stat (const char * path, struct stat * buf)
   char *name, *r;
   WIN32_FIND_DATA wfd;
   HANDLE fh;
-  DWORD fake_inode;
+  unsigned __int64 fake_inode;
   int permission;
   int len;
   int rootdir = FALSE;
@@ -2481,6 +2634,8 @@ stat (const char * path, struct stat * buf)
        }
       else
        {
+          logon_network_drive (name);
+
          fh = FindFirstFile (name, &wfd);
          if (fh == INVALID_HANDLE_VALUE)
            {
@@ -2512,7 +2667,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
        {
@@ -2522,22 +2679,22 @@ 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);
@@ -2546,7 +2703,7 @@ stat (const char * path, struct stat * buf)
     {
       /* 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;
     }
@@ -2589,14 +2746,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);
 
@@ -2610,13 +2767,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;
@@ -2624,12 +2781,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;
@@ -2639,7 +2796,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
@@ -2647,7 +2804,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)
@@ -2656,8 +2815,8 @@ fstat (int desc, struct stat * buf)
     buf->st_ino = fake_inode;
 
   /* consider files to belong to current user */
-  buf->st_uid = 0;
-  buf->st_gid = 0;
+  buf->st_uid = the_passwd.pw_uid;
+  buf->st_gid = the_passwd.pw_gid;
 
   buf->st_dev = info.dwVolumeSerialNumber;
   buf->st_rdev = info.dwVolumeSerialNumber;
@@ -2673,12 +2832,12 @@ 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 */
@@ -2688,7 +2847,7 @@ fstat (int desc, struct stat * buf)
           stricmp (p, ".com") == 0 ||
           stricmp (p, ".bat") == 0 ||
           stricmp (p, ".cmd") == 0))
-       permission |= _S_IEXEC;
+       permission |= S_IEXEC;
 #endif
     }
 
@@ -4167,11 +4326,18 @@ BOOL WINAPI shutdown_handler(DWORD type)
 void
 globals_of_w32 ()
 {
+  HMODULE kernel32 = GetModuleHandle ("kernel32.dll");
+
+  get_process_times_fn = (GetProcessTimes_Proc)
+    GetProcAddress (kernel32, "GetProcessTimes");
+
   g_b_init_is_windows_9x = 0;
   g_b_init_open_process_token = 0;
   g_b_init_get_token_information = 0;
   g_b_init_lookup_account_sid = 0;
   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;
   /* 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