Fix incompatibilities with 64-bit Windows builds.
[bpt/emacs.git] / src / w32.c
index b0272c6..fc97b0b 100644 (file)
--- a/src/w32.c
+++ b/src/w32.c
@@ -1,4 +1,4 @@
-/* Utility and Unix shadow routines for GNU Emacs on the Microsoft W32 API.
+/* Utility and Unix shadow routines for GNU Emacs on the Microsoft Windows API.
    Copyright (C) 1994-1995, 2000-2012  Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
@@ -31,14 +31,13 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <sys/file.h>
 #include <sys/time.h>
 #include <sys/utime.h>
-#include <mbstring.h>  /* for _mbspbrk */
 #include <math.h>
-#include <setjmp.h>
 #include <time.h>
 
 /* must include CRT headers *before* config.h */
 
 #include <config.h>
+#include <mbstring.h>  /* for _mbspbrk */
 
 #undef access
 #undef chdir
@@ -102,20 +101,71 @@ typedef struct _MEMORY_STATUS_EX {
    _WIN32_WINNT than what we use.  w32api supplied with MinGW 3.15
    defines it in psapi.h  */
 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;
+  DWORD  cb;
+  DWORD  PageFaultCount;
+  SIZE_T PeakWorkingSetSize;
+  SIZE_T WorkingSetSize;
+  SIZE_T QuotaPeakPagedPoolUsage;
+  SIZE_T QuotaPagedPoolUsage;
+  SIZE_T QuotaPeakNonPagedPoolUsage;
+  SIZE_T QuotaNonPagedPoolUsage;
+  SIZE_T PagefileUsage;
+  SIZE_T PeakPagefileUsage;
+  SIZE_T PrivateUsage;
 } PROCESS_MEMORY_COUNTERS_EX,*PPROCESS_MEMORY_COUNTERS_EX;
 #endif
 
+#include <winioctl.h>
+#include <aclapi.h>
+
+#ifdef _MSC_VER
+/* MSVC doesn't provide the definition of REPARSE_DATA_BUFFER and the
+   associated macros, except on ntifs.h, which cannot be included
+   because it triggers conflicts with other Windows API headers.  So
+   we define it here by hand.  */
+
+typedef struct _REPARSE_DATA_BUFFER {
+    ULONG  ReparseTag;
+    USHORT ReparseDataLength;
+    USHORT Reserved;
+    union {
+        struct {
+            USHORT SubstituteNameOffset;
+            USHORT SubstituteNameLength;
+            USHORT PrintNameOffset;
+            USHORT PrintNameLength;
+            ULONG Flags;
+            WCHAR PathBuffer[1];
+        } SymbolicLinkReparseBuffer;
+        struct {
+            USHORT SubstituteNameOffset;
+            USHORT SubstituteNameLength;
+            USHORT PrintNameOffset;
+            USHORT PrintNameLength;
+            WCHAR PathBuffer[1];
+        } MountPointReparseBuffer;
+        struct {
+            UCHAR  DataBuffer[1];
+        } GenericReparseBuffer;
+    } DUMMYUNIONNAME;
+} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
+
+#ifndef FILE_DEVICE_FILE_SYSTEM
+#define FILE_DEVICE_FILE_SYSTEM        9
+#endif
+#ifndef METHOD_BUFFERED
+#define METHOD_BUFFERED                0
+#endif
+#ifndef FILE_ANY_ACCESS
+#define FILE_ANY_ACCESS                0x00000000
+#endif
+#ifndef CTL_CODE
+#define CTL_CODE(t,f,m,a)       (((t)<<16)|((a)<<14)|((f)<<2)|(m))
+#endif
+#define FSCTL_GET_REPARSE_POINT \
+  CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#endif
+
 /* TCP connection support.  */
 #include <sys/socket.h>
 #undef socket
@@ -138,7 +188,9 @@ typedef struct _PROCESS_MEMORY_COUNTERS_EX {
 
 #include "w32.h"
 #include "ndir.h"
+#include "w32common.h"
 #include "w32heap.h"
+#include "w32select.h"
 #include "systime.h"
 #include "dispextern.h"                /* for xstrcasecmp */
 #include "coding.h"            /* for Vlocale_coding_system */
@@ -156,6 +208,17 @@ Lisp_Object QCloaded_from;
 
 void globals_of_w32 (void);
 static DWORD get_rid (PSID);
+static int is_symlink (const char *);
+static char * chase_symlinks (const char *);
+static int enable_privilege (LPCTSTR, BOOL, TOKEN_PRIVILEGES *);
+static int restore_privilege (TOKEN_PRIVILEGES *);
+static BOOL WINAPI revert_to_self (void);
+
+extern int sys_access (const char *, int);
+extern void *e_malloc (size_t);
+extern int sys_select (int, SELECT_TYPE *, SELECT_TYPE *, SELECT_TYPE *,
+                      EMACS_TIME *, void *);
+
 
 \f
 /* Initialization states.
@@ -173,6 +236,7 @@ static BOOL g_b_init_get_token_information;
 static BOOL g_b_init_lookup_account_sid;
 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_security_info;
 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;
@@ -192,6 +256,7 @@ 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;
+static BOOL g_b_init_create_symbolic_link;
 
 /*
   BEGIN: Wrapper functions around OpenProcessToken
@@ -238,6 +303,15 @@ typedef PDWORD (WINAPI * GetSidSubAuthority_Proc) (
     DWORD n);
 typedef PUCHAR (WINAPI * GetSidSubAuthorityCount_Proc) (
     PSID pSid);
+typedef DWORD (WINAPI * GetSecurityInfo_Proc) (
+    HANDLE handle,
+    SE_OBJECT_TYPE ObjectType,
+    SECURITY_INFORMATION SecurityInfo,
+    PSID *ppsidOwner,
+    PSID *ppsidGroup,
+    PACL *ppDacl,
+    PACL *ppSacl,
+    PSECURITY_DESCRIPTOR *ppSecurityDescriptor);
 typedef BOOL (WINAPI * GetFileSecurity_Proc) (
     LPCTSTR lpFileName,
     SECURITY_INFORMATION RequestedInformation,
@@ -277,8 +351,8 @@ typedef BOOL (WINAPI * GetProcessMemoryInfo_Proc) (
     DWORD cb);
 typedef BOOL (WINAPI * GetProcessWorkingSetSize_Proc) (
     HANDLE hProcess,
-    DWORD * lpMinimumWorkingSetSize,
-    DWORD * lpMaximumWorkingSetSize);
+    PSIZE_T lpMinimumWorkingSetSize,
+    PSIZE_T lpMaximumWorkingSetSize);
 typedef BOOL (WINAPI * GlobalMemoryStatus_Proc) (
     LPMEMORYSTATUS lpBuffer);
 typedef BOOL (WINAPI * GlobalMemoryStatusEx_Proc) (
@@ -298,6 +372,10 @@ typedef BOOL (WINAPI * GetSystemTimes_Proc) (
     LPFILETIME lpIdleTime,
     LPFILETIME lpKernelTime,
     LPFILETIME lpUserTime);
+typedef BOOLEAN (WINAPI *CreateSymbolicLink_Proc) (
+    LPTSTR lpSymlinkFileName,
+    LPTSTR lpTargetFileName,
+    DWORD  dwFlags);
 
   /* ** A utility function ** */
 static BOOL
@@ -499,6 +577,39 @@ get_sid_sub_authority_count (PSID pSid)
   return (s_pfn_Get_Sid_Sub_Authority_Count (pSid));
 }
 
+static DWORD WINAPI
+get_security_info (HANDLE handle,
+                  SE_OBJECT_TYPE ObjectType,
+                  SECURITY_INFORMATION SecurityInfo,
+                  PSID *ppsidOwner,
+                  PSID *ppsidGroup,
+                  PACL *ppDacl,
+                  PACL *ppSacl,
+                  PSECURITY_DESCRIPTOR *ppSecurityDescriptor)
+{
+  static GetSecurityInfo_Proc s_pfn_Get_Security_Info = NULL;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return FALSE;
+    }
+  if (g_b_init_get_security_info == 0)
+    {
+      g_b_init_get_security_info = 1;
+      hm_advapi32 = LoadLibrary ("Advapi32.dll");
+      s_pfn_Get_Security_Info =
+        (GetSecurityInfo_Proc) GetProcAddress (
+            hm_advapi32, "GetSecurityInfo");
+    }
+  if (s_pfn_Get_Security_Info == NULL)
+    {
+      return FALSE;
+    }
+  return (s_pfn_Get_Security_Info (handle, ObjectType, SecurityInfo,
+                                  ppsidOwner, ppsidGroup, ppDacl, ppSacl,
+                                  ppSecurityDescriptor));
+}
+
 static BOOL WINAPI
 get_file_security (LPCTSTR lpFileName,
                   SECURITY_INFORMATION RequestedInformation,
@@ -726,24 +837,58 @@ get_system_times (LPFILETIME lpIdleTime,
     return FALSE;
   return (s_pfn_Get_System_times (lpIdleTime, lpKernelTime, lpUserTime));
 }
-\f
-/* Equivalent of strerror for W32 error codes.  */
-char *
-w32_strerror (int error_no)
+
+static BOOLEAN WINAPI
+create_symbolic_link (LPTSTR lpSymlinkFilename,
+                     LPTSTR lpTargetFileName,
+                     DWORD dwFlags)
 {
-  static char buf[500];
+  static CreateSymbolicLink_Proc s_pfn_Create_Symbolic_Link = NULL;
+  BOOLEAN retval;
+
+  if (is_windows_9x () == TRUE)
+    {
+      errno = ENOSYS;
+      return 0;
+    }
+  if (g_b_init_create_symbolic_link == 0)
+    {
+      g_b_init_create_symbolic_link = 1;
+#ifdef _UNICODE
+      s_pfn_Create_Symbolic_Link =
+       (CreateSymbolicLink_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
+                                                "CreateSymbolicLinkW");
+#else
+      s_pfn_Create_Symbolic_Link =
+       (CreateSymbolicLink_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
+                                                "CreateSymbolicLinkA");
+#endif
+    }
+  if (s_pfn_Create_Symbolic_Link == NULL)
+    {
+      errno = ENOSYS;
+      return 0;
+    }
 
-  if (error_no == 0)
-    error_no = GetLastError ();
+  retval = s_pfn_Create_Symbolic_Link (lpSymlinkFilename, lpTargetFileName,
+                                      dwFlags);
+  /* If we were denied creation of the symlink, try again after
+     enabling the SeCreateSymbolicLinkPrivilege for our process.  */
+  if (!retval)
+    {
+      TOKEN_PRIVILEGES priv_current;
 
-  buf[0] = '\0';
-  if (!FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL,
-                     error_no,
-                     0, /* choose most suitable language */
-                     buf, sizeof (buf), NULL))
-    sprintf (buf, "w32 error %u", error_no);
-  return buf;
+      if (enable_privilege (SE_CREATE_SYMBOLIC_LINK_NAME, TRUE, &priv_current))
+       {
+         retval = s_pfn_Create_Symbolic_Link (lpSymlinkFilename, lpTargetFileName,
+                                              dwFlags);
+         restore_privilege (&priv_current);
+         revert_to_self ();
+       }
+    }
+  return retval;
 }
+\f
 
 /* Return 1 if P is a valid pointer to an object of size SIZE.  Return
    0 if P is NOT a valid pointer.  Return -1 if we cannot validate P.
@@ -778,9 +923,8 @@ getwd (char *dir)
     return dir;
   return NULL;
 #else
-  /* Emacs doesn't actually change directory itself, and we want to
-     force our real wd to be where emacs.exe is to avoid unnecessary
-     conflicts when trying to rename or delete directories.  */
+  /* Emacs doesn't actually change directory itself, it stays in the
+     same directory where it was started.  */
   strcpy (dir, startup_dir);
   return dir;
 #endif
@@ -1134,9 +1278,9 @@ init_user_info (void)
 
   /* Ensure HOME and SHELL are defined. */
   if (getenv ("HOME") == NULL)
-    abort ();
+    emacs_abort ();
   if (getenv ("SHELL") == NULL)
-    abort ();
+    emacs_abort ();
 
   /* Set dir and shell from environment variables. */
   strcpy (dflt_passwd.pw_dir, getenv ("HOME"));
@@ -1390,76 +1534,6 @@ is_unc_volume (const char *filename)
   return 1;
 }
 
-/* Routines that are no-ops on NT but are defined to get Emacs to compile.  */
-
-int
-sigsetmask (int signal_mask)
-{
-  return 0;
-}
-
-int
-sigmask (int sig)
-{
-  return 0;
-}
-
-int
-sigblock (int sig)
-{
-  return 0;
-}
-
-int
-sigunblock (int sig)
-{
-  return 0;
-}
-
-int
-sigemptyset (sigset_t *set)
-{
-  return 0;
-}
-
-int
-sigaddset (sigset_t *set, int signo)
-{
-  return 0;
-}
-
-int
-sigfillset (sigset_t *set)
-{
-  return 0;
-}
-
-int
-sigprocmask (int how, const sigset_t *set, sigset_t *oset)
-{
-  return 0;
-}
-
-int
-pthread_sigmask (int how, const sigset_t *set, sigset_t *oset)
-{
-  if (sigprocmask (how, set, oset) == -1)
-    return EINVAL;
-  return 0;
-}
-
-int
-setpgrp (int pid, int gid)
-{
-  return 0;
-}
-
-int
-alarm (int seconds)
-{
-  return 0;
-}
-
 #define REG_ROOT "SOFTWARE\\GNU\\Emacs"
 
 LPBYTE
@@ -1477,7 +1551,7 @@ w32_get_resource (char *key, LPDWORD lpdwtype)
       lpvalue = NULL;
 
       if (RegQueryValueEx (hrootkey, key, NULL, NULL, NULL, &cbData) == ERROR_SUCCESS
-         && (lpvalue = (LPBYTE) xmalloc (cbData)) != NULL
+         && (lpvalue = xmalloc (cbData)) != NULL
          && RegQueryValueEx (hrootkey, key, NULL, lpdwtype, lpvalue, &cbData) == ERROR_SUCCESS)
        {
           RegCloseKey (hrootkey);
@@ -1494,7 +1568,7 @@ w32_get_resource (char *key, LPDWORD lpdwtype)
       lpvalue = NULL;
 
       if (RegQueryValueEx (hrootkey, key, NULL, NULL, NULL, &cbData) == ERROR_SUCCESS
-         && (lpvalue = (LPBYTE) xmalloc (cbData)) != NULL
+         && (lpvalue = xmalloc (cbData)) != NULL
          && RegQueryValueEx (hrootkey, key, NULL, lpdwtype, lpvalue, &cbData) == ERROR_SUCCESS)
        {
           RegCloseKey (hrootkey);
@@ -1536,12 +1610,9 @@ init_environment (char ** argv)
         read-only filesystem, like CD-ROM or a write-protected floppy.
         The only way to be really sure is to actually create a file and
         see if it succeeds.  But I think that's too much to ask.  */
-#ifdef _MSC_VER
-      /* MSVC's _access crashes with D_OK.  */
+
+      /* MSVCRT's _access crashes with D_OK.  */
       if (tmp && sys_access (tmp, D_OK) == 0)
-#else
-      if (tmp && _access (tmp, D_OK) == 0)
-#endif
        {
          char * var = alloca (strlen (tmp) + 8);
          sprintf (var, "TMPDIR=%s", tmp);
@@ -1563,7 +1634,6 @@ init_environment (char ** argv)
     LPBYTE lpval;
     DWORD dwType;
     char locale_name[32];
-    struct stat ignored;
     char default_home[MAX_PATH];
     int appdata = 0;
 
@@ -1573,17 +1643,19 @@ init_environment (char ** argv)
       char * def_value;
     } dflt_envvars[] =
     {
+      /* If the default value is NULL, we will use the value from the
+        outside environment or the Registry, but will not push the
+        variable into the Emacs environment if it is defined neither
+        in the Registry nor in the outside environment.  */
       {"HOME", "C:/"},
       {"PRELOAD_WINSOCK", NULL},
       {"emacs_dir", "C:/emacs"},
-      {"EMACSLOADPATH", "%emacs_dir%/site-lisp;%emacs_dir%/../site-lisp;%emacs_dir%/lisp;%emacs_dir%/leim"},
+      {"EMACSLOADPATH", NULL},
       {"SHELL", "%emacs_dir%/bin/cmdproxy.exe"},
-      {"EMACSDATA", "%emacs_dir%/etc"},
-      {"EMACSPATH", "%emacs_dir%/bin"},
-      /* We no longer set INFOPATH because Info-default-directory-list
-        is then ignored.  */
-      /*  {"INFOPATH", "%emacs_dir%/info"},  */
-      {"EMACSDOC", "%emacs_dir%/etc"},
+      {"EMACSDATA", NULL},
+      {"EMACSPATH", NULL},
+      {"INFOPATH", NULL},
+      {"EMACSDOC", NULL},
       {"TERM", "cmd"},
       {"LANG", NULL},
     };
@@ -1602,7 +1674,7 @@ init_environment (char ** argv)
     /* For backwards compatibility, check if a .emacs file exists in C:/
        If not, then we can try to default to the appdata directory under the
        user's profile, which is more likely to be writable.   */
-    if (stat ("C:/.emacs", &ignored) < 0)
+    if (!check_existing ("C:/.emacs"))
       {
        HRESULT profile_result;
        /* Dynamically load ShGetFolderPath, as it won't exist on versions
@@ -1641,37 +1713,18 @@ init_environment (char ** argv)
         }
     }
 
-  /* When Emacs is invoked with --no-site-lisp, we must remove the
-     site-lisp directories from the default value of EMACSLOADPATH.
-     This assumes that the site-lisp entries are at the front, and
-     that additional entries do exist.  */
-  if (no_site_lisp)
-    {
-      for (i = 0; i < N_ENV_VARS; i++)
-        {
-          if (strcmp (env_vars[i].name, "EMACSLOADPATH") == 0)
-            {
-              char *site;
-              while ((site = strstr (env_vars[i].def_value, "site-lisp")))
-                env_vars[i].def_value = strchr (site, ';') + 1;
-              break;
-            }
-        }
-    }
-
 #define SET_ENV_BUF_SIZE (4 * MAX_PATH)        /* to cover EMACSLOADPATH */
 
     /* Treat emacs_dir specially: set it unconditionally based on our
-       location, if it appears that we are running from the bin subdir
-       of a standard installation.  */
+       location.  */
     {
       char *p;
       char modname[MAX_PATH];
 
       if (!GetModuleFileName (NULL, modname, MAX_PATH))
-       abort ();
+       emacs_abort ();
       if ((p = strrchr (modname, '\\')) == NULL)
-       abort ();
+       emacs_abort ();
       *p = 0;
 
       if ((p = strrchr (modname, '\\')) && xstrcasecmp (p, "\\bin") == 0)
@@ -1690,7 +1743,8 @@ 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 && xstrcasecmp (p, "\\i386") == 0)
+      else if (p && (xstrcasecmp (p, "\\i386") == 0
+                     || xstrcasecmp (p, "\\AMD64") == 0))
        {
          *p = 0;
          p = strrchr (modname, '\\');
@@ -1728,13 +1782,11 @@ init_environment (char ** argv)
                dwType = REG_EXPAND_SZ;
                dont_free = 1;
                if (!strcmp (env_vars[i].name, "HOME") && !appdata)
-                 {
-                   Lisp_Object warning[2];
-                   warning[0] = intern ("initialization");
-                   warning[1] = build_string ("Setting HOME to C:\\ by default is deprecated");
-                   Vdelayed_warnings_list = Fcons (Flist (2, warning),
-                                                   Vdelayed_warnings_list);
-                 }
+                 Vdelayed_warnings_list
+                   = Fcons (listn (CONSTYPE_HEAP, 2,
+                                   intern ("initialization"),
+                                   build_string ("Setting HOME to C:\\ by default is deprecated")),
+                            Vdelayed_warnings_list);
              }
 
            if (lpval)
@@ -1781,27 +1833,17 @@ init_environment (char ** argv)
        memcpy (*envp, "COMSPEC=", 8);
   }
 
-  /* Remember the initial working directory for getwd, then make the
-     real wd be the location of emacs.exe to avoid conflicts when
-     renaming or deleting directories.  (We also don't call chdir when
-     running subprocesses for the same reason.)  */
+  /* Remember the initial working directory for getwd.  */
+  /* FIXME: Do we need to resolve possible symlinks in startup_dir?
+     Does it matter anywhere in Emacs?  */
   if (!GetCurrentDirectory (MAXPATHLEN, startup_dir))
-    abort ();
+    emacs_abort ();
 
   {
-    char *p;
     static char modname[MAX_PATH];
 
     if (!GetModuleFileName (NULL, modname, MAX_PATH))
-      abort ();
-    if ((p = strrchr (modname, '\\')) == NULL)
-      abort ();
-    *p = 0;
-
-    SetCurrentDirectory (modname);
-
-    /* Ensure argv[0] has the full path to Emacs.  */
-    *p = '\\';
+      emacs_abort ();
     argv[0] = modname;
   }
 
@@ -1813,6 +1855,8 @@ init_environment (char ** argv)
   init_user_info ();
 }
 
+/* Called from expand-file-name when default-directory is not a string.  */
+
 char *
 emacs_root_dir (void)
 {
@@ -1821,7 +1865,7 @@ emacs_root_dir (void)
 
   p = getenv ("emacs_dir");
   if (p == NULL)
-    abort ();
+    emacs_abort ();
   strcpy (root_dir, p);
   root_dir[parse_root (root_dir, NULL)] = '\0';
   dostounix_filename (root_dir);
@@ -1848,7 +1892,16 @@ get_emacs_configuration (void)
     case PROCESSOR_INTEL_386:
     case PROCESSOR_INTEL_486:
     case PROCESSOR_INTEL_PENTIUM:
+#ifdef _WIN64
+      arch = "amd64";
+#else
       arch = "i386";
+#endif
+      break;
+#endif
+#ifdef PROCESSOR_AMD_X8664
+    case PROCESSOR_AMD_X8664:
+      arch = "amd64";
       break;
 #endif
 
@@ -1987,7 +2040,7 @@ gettimeofday (struct timeval *tv, struct timezone *tz)
      changed.  We could fix that by using GetSystemTime and
      GetTimeZoneInformation, but that doesn't seem necessary, since
      Emacs always calls gettimeofday with the 2nd argument NULL (see
-     EMACS_GET_TIME).  */
+     current_emacs_time).  */
   if (tz)
     {
       tz->tz_minuteswest = tb.timezone;        /* minutes west of Greenwich  */
@@ -2031,7 +2084,7 @@ fdutimens (int fd, char const *file, struct timespec const timespec[2])
 
 
 /* ------------------------------------------------------------------------- */
-/* IO support and wrapper functions for W32 API. */
+/* IO support and wrapper functions for the Windows API. */
 /* ------------------------------------------------------------------------- */
 
 /* Place a wrapper around the MSVC version of ctime.  It returns NULL
@@ -2187,7 +2240,7 @@ GetCachedVolumeInformation (char * root_dir)
         entry if present.  */
       if (info == NULL)
        {
-         info = (volume_info_data *) xmalloc (sizeof (volume_info_data));
+         info = xmalloc (sizeof (volume_info_data));
          add_volume_info (root_dir, info);
        }
       else
@@ -2207,8 +2260,15 @@ GetCachedVolumeInformation (char * root_dir)
   return info;
 }
 
-/* Get information on the volume where name is held; set path pointer to
-   start of pathname in name (past UNC header\volume header if present).  */
+/* Get information on the volume where NAME is held; set path pointer to
+   start of pathname in NAME (past UNC header\volume header if present),
+   if pPath is non-NULL.
+
+   Note: if NAME includes symlinks, the information is for the volume
+   of the symlink, not of its target.  That's because, even though
+   GetVolumeInformation returns information about the symlink target
+   of its argument, we only pass the root directory to
+   GetVolumeInformation, not the full NAME.  */
 static int
 get_volume_info (const char * name, const char ** pPath)
 {
@@ -2219,7 +2279,7 @@ get_volume_info (const char * name, const char ** pPath)
   if (name == NULL)
     return FALSE;
 
-  /* find the root name of the volume if given */
+  /* Find the root name of the volume if given.  */
   if (isalpha (name[0]) && name[1] == ':')
     {
       rootname = temp;
@@ -2259,7 +2319,8 @@ get_volume_info (const char * name, const char ** pPath)
 }
 
 /* Determine if volume is FAT format (ie. only supports short 8.3
-   names); also set path pointer to start of pathname in name.  */
+   names); also set path pointer to start of pathname in name, if
+   pPath is non-NULL.  */
 static int
 is_fat_volume (const char * name, const char ** pPath)
 {
@@ -2268,7 +2329,8 @@ is_fat_volume (const char * name, const char ** pPath)
   return FALSE;
 }
 
-/* Map filename to a valid 8.3 name if necessary. */
+/* Map filename to a valid 8.3 name if necessary.
+   The result is a pointer to a static buffer, so CAVEAT EMPTOR!  */
 const char *
 map_w32_filename (const char * name, const char ** pPath)
 {
@@ -2298,15 +2360,10 @@ map_w32_filename (const char * name, const char ** pPath)
         {
          switch ( c )
            {
+           case ':':
            case '\\':
            case '/':
-             *str++ = '\\';
-             extn = 0;         /* reset extension flags */
-             dots = 2;         /* max 2 dots */
-             left = 8;         /* max length 8 for main part */
-             break;
-           case ':':
-             *str++ = ':';
+             *str++ = (c == ':' ? ':' : '\\');
              extn = 0;         /* reset extension flags */
              dots = 2;         /* max 2 dots */
              left = 8;         /* max length 8 for main part */
@@ -2415,6 +2472,9 @@ opendir (char *filename)
   if (wnet_enum_handle != INVALID_HANDLE_VALUE)
     return NULL;
 
+  /* Note: We don't support traversal of UNC volumes via symlinks.
+     Doing so would mean punishing 99.99% of use cases by resolving
+     all the possible symlinks in FILENAME, recursively. */
   if (is_unc_volume (filename))
     {
       wnet_enum_handle = open_unc_volume (filename);
@@ -2431,6 +2491,9 @@ opendir (char *filename)
 
   strncpy (dir_pathname, map_w32_filename (filename, NULL), MAXPATHLEN);
   dir_pathname[MAXPATHLEN] = '\0';
+  /* Note: We don't support symlinks to file names on FAT volumes.
+     Doing so would mean punishing 99.99% of use cases by resolving
+     all the possible symlinks in FILENAME, recursively.  */
   dir_is_fat = is_fat_volume (filename, NULL);
 
   return dirp;
@@ -2477,6 +2540,9 @@ readdir (DIR *dirp)
        strcat (filename, "\\");
       strcat (filename, "*");
 
+      /* Note: No need to resolve symlinks in FILENAME, because
+        FindFirst opens the directory that is the target of a
+        symlink.  */
       dir_find_handle = FindFirstFile (filename, &dir_find_data);
 
       if (dir_find_handle == INVALID_HANDLE_VALUE)
@@ -2667,21 +2733,43 @@ sys_access (const char * path, int mode)
 {
   DWORD attributes;
 
-  /* MSVC implementation doesn't recognize D_OK.  */
+  /* MSVCRT implementation of 'access' doesn't recognize D_OK, and its
+     newer versions blow up when passed D_OK.  */
   path = map_w32_filename (path, NULL);
-  if (is_unc_volume (path))
-    {
-      attributes = unc_volume_file_attributes (path);
-      if (attributes == -1) {
-       errno = EACCES;
-       return -1;
-      }
-    }
-  else if ((attributes = GetFileAttributes (path)) == -1)
+  /* If the last element of PATH is a symlink, we need to resolve it
+     to get the attributes of its target file.  Note: any symlinks in
+     PATH elements other than the last one are transparently resolved
+     by GetFileAttributes below.  */
+  if ((volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) != 0)
+    path = chase_symlinks (path);
+
+  if ((attributes = GetFileAttributes (path)) == -1)
     {
-      /* Should try mapping GetLastError to errno; for now just indicate
-        that path doesn't exist.  */
-      errno = EACCES;
+      DWORD w32err = GetLastError ();
+
+      switch (w32err)
+       {
+       case ERROR_INVALID_NAME:
+       case ERROR_BAD_PATHNAME:
+         if (is_unc_volume (path))
+           {
+             attributes = unc_volume_file_attributes (path);
+             if (attributes == -1)
+               {
+                 errno = EACCES;
+                 return -1;
+               }
+             break;
+           }
+         /* FALLTHROUGH */
+       case ERROR_FILE_NOT_FOUND:
+       case ERROR_BAD_NETPATH:
+         errno = ENOENT;
+         break;
+       default:
+         errno = EACCES;
+         break;
+       }
       return -1;
     }
   if ((mode & X_OK) != 0 && !is_exec (path))
@@ -2711,7 +2799,8 @@ sys_chdir (const char * path)
 int
 sys_chmod (const char * path, int mode)
 {
-  return _chmod (map_w32_filename (path, NULL), mode);
+  path = chase_symlinks (map_w32_filename (path, NULL));
+  return _chmod (path, mode);
 }
 
 int
@@ -2940,7 +3029,7 @@ sys_rename (const char * oldname, const char * newname)
   /* volume_info is set indirectly by map_w32_filename.  */
   oldname_dev = volume_info.serialnum;
 
-  if (os_subtype == OS_WIN95)
+  if (os_subtype == OS_9X)
     {
       char * o;
       char * p;
@@ -2991,6 +3080,7 @@ sys_rename (const char * oldname, const char * newname)
 
   if (result < 0)
     {
+      DWORD w32err = GetLastError ();
 
       if (errno == EACCES
          && newname_dev != oldname_dev)
@@ -3003,7 +3093,7 @@ sys_rename (const char * oldname, const char * newname)
          DWORD attributes;
 
          if ((attributes = GetFileAttributes (temp)) != -1
-             && attributes & FILE_ATTRIBUTE_DIRECTORY)
+             && (attributes & FILE_ATTRIBUTE_DIRECTORY))
            errno = EXDEV;
        }
       else if (errno == EEXIST)
@@ -3014,6 +3104,14 @@ sys_rename (const char * oldname, const char * newname)
            return result;
          result = rename (temp, newname);
        }
+      else if (w32err == ERROR_PRIVILEGE_NOT_HELD
+              && is_symlink (temp))
+       {
+         /* This is Windows prohibiting the user from creating a
+            symlink in another place, since that requires
+            privileges.  */
+         errno = EPERM;
+       }
     }
 
   return result;
@@ -3133,7 +3231,7 @@ generate_inode_val (const char * name)
      doesn't resolve aliasing due to subst commands, or recognize hard
      links.  */
   if (!w32_get_long_filename ((char *)name, fullname, MAX_PATH))
-    abort ();
+    emacs_abort ();
 
   parse_root (fullname, &p);
   /* Normal W32 filesystems are still case insensitive. */
@@ -3144,7 +3242,23 @@ generate_inode_val (const char * name)
 #endif
 
 static PSECURITY_DESCRIPTOR
-get_file_security_desc (const char *fname)
+get_file_security_desc_by_handle (HANDLE h)
+{
+  PSECURITY_DESCRIPTOR psd = NULL;
+  DWORD err;
+  SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION
+    | GROUP_SECURITY_INFORMATION  /* | DACL_SECURITY_INFORMATION */ ;
+
+  err = get_security_info (h, SE_FILE_OBJECT, si,
+                          NULL, NULL, NULL, NULL, &psd);
+  if (err != ERROR_SUCCESS)
+    return NULL;
+
+  return psd;
+}
+
+static PSECURITY_DESCRIPTOR
+get_file_security_desc_by_name (const char *fname)
 {
   PSECURITY_DESCRIPTOR psd = NULL;
   DWORD sd_len, err;
@@ -3360,18 +3474,24 @@ is_slow_fs (const char *name)
 
 /* 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)
+   inode values and owner/group without hacks in the main Emacs code. */
+
+static int
+stat_worker (const char * path, struct stat * buf, int follow_symlinks)
 {
-  char *name, *r;
+  char *name, *save_name, *r;
   WIN32_FIND_DATA wfd;
   HANDLE fh;
-  unsigned __int64 fake_inode;
+  unsigned __int64 fake_inode = 0;
   int permission;
   int len;
   int rootdir = FALSE;
   PSECURITY_DESCRIPTOR psd = NULL;
+  int is_a_symlink = 0;
+  DWORD file_flags = FILE_FLAG_BACKUP_SEMANTICS;
+  DWORD access_rights = 0;
+  DWORD fattrs = 0, serialnum = 0, fs_high = 0, fs_low = 0, nlinks = 1;
+  FILETIME ctime, atime, wtime;
 
   if (path == NULL || buf == NULL)
     {
@@ -3379,7 +3499,7 @@ stat (const char * path, struct stat * buf)
       return -1;
     }
 
-  name = (char *) map_w32_filename (path, &path);
+  save_name = 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
@@ -3391,99 +3511,67 @@ stat (const char * path, struct stat * buf)
       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);
 
-  if (is_unc_volume (name))
-    {
-      DWORD attrs = unc_volume_file_attributes (name);
-
-      if (attrs == -1)
-       return -1;
-
-      memset (&wfd, 0, sizeof (wfd));
-      wfd.dwFileAttributes = attrs;
-      wfd.ftCreationTime = utc_base_ft;
-      wfd.ftLastAccessTime = utc_base_ft;
-      wfd.ftLastWriteTime = utc_base_ft;
-      strcpy (wfd.cFileName, name);
-    }
-  else if (rootdir)
-    {
-      if (!IS_DIRECTORY_SEP (name[len-1]))
-       strcat (name, "\\");
-      if (GetDriveType (name) < 2)
-       {
-         errno = ENOENT;
-         return -1;
-       }
-      memset (&wfd, 0, sizeof (wfd));
-      wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
-      wfd.ftCreationTime = utc_base_ft;
-      wfd.ftLastAccessTime = utc_base_ft;
-      wfd.ftLastWriteTime = utc_base_ft;
-      strcpy (wfd.cFileName, name);
-    }
-  else
-    {
-      if (IS_DIRECTORY_SEP (name[len-1]))
-       name[len - 1] = 0;
-
-      /* (This is hacky, but helps when doing file completions on
-        network drives.)  Optimize by using information available from
-        active readdir if possible.  */
-      len = strlen (dir_pathname);
-      if (IS_DIRECTORY_SEP (dir_pathname[len-1]))
-       len--;
-      if (dir_find_handle != INVALID_HANDLE_VALUE
-         && strnicmp (name, dir_pathname, len) == 0
-         && IS_DIRECTORY_SEP (name[len])
-         && xstrcasecmp (name + len + 1, dir_static.d_name) == 0)
-       {
-         /* This was the last entry returned by readdir.  */
-         wfd = dir_find_data;
-       }
-      else
-       {
-          logon_network_drive (name);
-
-         fh = FindFirstFile (name, &wfd);
-         if (fh == INVALID_HANDLE_VALUE)
-           {
-             errno = ENOENT;
-             return -1;
-           }
-         FindClose (fh);
-       }
-    }
-
+  /* Avoid a somewhat costly call to is_symlink if the filesystem
+     doesn't support symlinks.  */
+  if ((volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) != 0)
+    is_a_symlink = is_symlink (name);
+
+  /* Plan A: Open the file and get all the necessary information via
+     the resulting handle.  This solves several issues in one blow:
+
+      . retrieves attributes for the target of a symlink, if needed
+      . gets attributes of root directories and symlinks pointing to
+        root directories, thus avoiding the need for special-casing
+        these and detecting them by examining the file-name format
+      . retrieves more accurate attributes (e.g., non-zero size for
+        some directories, esp. directories that are junction points)
+      . correctly resolves "c:/..", "/.." and similar file names
+      . avoids run-time penalties for 99% of use cases
+
+     Plan A is always tried first, unless the user asked not to (but
+     if the file is a symlink and we need to follow links, we try Plan
+     A even if the user asked not to).
+
+     If Plan A fails, we go to Plan B (below), where various
+     potentially expensive techniques must be used to handle "special"
+     files such as UNC volumes etc.  */
   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))
-         != INVALID_HANDLE_VALUE)
+      /* Following symlinks requires getting the info by handle.  */
+      || (is_a_symlink && follow_symlinks))
     {
+      BY_HANDLE_FILE_INFORMATION info;
+
+      if (is_a_symlink && !follow_symlinks)
+       file_flags |= FILE_FLAG_OPEN_REPARSE_POINT;
+      /* READ_CONTROL access rights are required to get security info
+        by handle.  But if the OS doesn't support security in the
+        first place, we don't need to try.  */
+      if (is_windows_9x () != TRUE)
+       access_rights |= READ_CONTROL;
+
+      fh = CreateFile (name, access_rights, 0, NULL, OPEN_EXISTING,
+                      file_flags, NULL);
+      /* If CreateFile fails with READ_CONTROL, try again with zero as
+        access rights.  */
+      if (fh == INVALID_HANDLE_VALUE && access_rights)
+       fh = CreateFile (name, 0, 0, NULL, OPEN_EXISTING,
+                        file_flags, NULL);
+      if (fh == INVALID_HANDLE_VALUE)
+       goto no_true_file_attributes;
+
       /* This is more accurate in terms of getting the correct number
         of links, but is quite slow (it is noticeable when Emacs is
         making a list of file name completions). */
-      BY_HANDLE_FILE_INFORMATION info;
-
       if (GetFileInformationByHandle (fh, &info))
        {
-         buf->st_nlink = info.nNumberOfLinks;
+         nlinks = info.nNumberOfLinks;
          /* Might as well use file index to fake inode values, but this
             is not guaranteed to be unique unless we keep a handle open
             all the time (even then there are situations where it is
@@ -3492,20 +3580,53 @@ stat (const char * path, struct stat * buf)
          fake_inode = info.nFileIndexHigh;
          fake_inode <<= 32;
          fake_inode += info.nFileIndexLow;
+         serialnum = info.dwVolumeSerialNumber;
+         fs_high = info.nFileSizeHigh;
+         fs_low  = info.nFileSizeLow;
+         ctime = info.ftCreationTime;
+         atime = info.ftLastAccessTime;
+         wtime = info.ftLastWriteTime;
+         fattrs = info.dwFileAttributes;
        }
       else
        {
-         buf->st_nlink = 1;
-         fake_inode = 0;
+         /* We don't go to Plan B here, because it's not clear that
+            it's a good idea.  The only known use case where
+            CreateFile succeeds, but GetFileInformationByHandle fails
+            (with ERROR_INVALID_FUNCTION) is for character devices
+            such as NUL, PRN, etc.  For these, switching to Plan B is
+            a net loss, because we lose the character device
+            attribute returned by GetFileType below (FindFirstFile
+            doesn't set that bit in the attributes), and the other
+            fields don't make sense for character devices anyway.
+            Emacs doesn't really care for non-file entities in the
+            context of l?stat, so neither do we.  */
+
+         /* w32err is assigned so one could put a breakpoint here and
+            examine its value, when GetFileInformationByHandle
+            fails. */
+         DWORD w32err = GetLastError ();
+
+         switch (w32err)
+           {
+           case ERROR_FILE_NOT_FOUND: /* can this ever happen? */
+             errno = ENOENT;
+             return -1;
+           }
        }
 
-      if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-       {
-         buf->st_mode = S_IFDIR;
-       }
+      /* Test for a symlink before testing for a directory, since
+        symlinks to directories have the directory bit set, but we
+        don't want them to appear as directories.  */
+      if (is_a_symlink && !follow_symlinks)
+       buf->st_mode = S_IFLNK;
+      else if (fattrs & FILE_ATTRIBUTE_DIRECTORY)
+       buf->st_mode = S_IFDIR;
       else
        {
-         switch (GetFileType (fh))
+         DWORD ftype = GetFileType (fh);
+
+         switch (ftype)
            {
            case FILE_TYPE_DISK:
              buf->st_mode = S_IFREG;
@@ -3519,21 +3640,143 @@ stat (const char * path, struct stat * buf)
              buf->st_mode = S_IFCHR;
            }
        }
+      /* We produce the fallback owner and group data, based on the
+        current user that runs Emacs, in the following cases:
+
+         . this is Windows 9X
+         . getting security by handle failed, and we need to produce
+           information for the target of a symlink (this is better
+           than producing a potentially misleading info about the
+           symlink itself)
+
+        If getting security by handle fails, and we don't need to
+        resolve symlinks, we try getting security by name.  */
+      if (is_windows_9x () != TRUE)
+       psd = get_file_security_desc_by_handle (fh);
+      if (psd)
+       {
+         get_file_owner_and_group (psd, name, buf);
+         LocalFree (psd);
+       }
+      else if (is_windows_9x () == TRUE)
+       get_file_owner_and_group (NULL, name, buf);
+      else if (!(is_a_symlink && follow_symlinks))
+       {
+         psd = get_file_security_desc_by_name (name);
+         get_file_owner_and_group (psd, name, buf);
+         xfree (psd);
+       }
+      else
+       get_file_owner_and_group (NULL, name, buf);
       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;
-      buf->st_nlink = 1;
-      fake_inode = 0;
+    no_true_file_attributes:
+      /* Plan B: Either getting a handle on the file failed, or the
+        caller explicitly asked us to not bother making this
+        information more accurate.
+
+        Implementation note: In Plan B, we never bother to resolve
+        symlinks, even if we got here because we tried Plan A and
+        failed.  That's because, even if the caller asked for extra
+        precision by setting Vw32_get_true_file_attributes to t,
+        resolving symlinks requires acquiring a file handle to the
+        symlink, which we already know will fail.  And if the user
+        did not ask for extra precision, resolving symlinks will fly
+        in the face of that request, since the user then wants the
+        lightweight version of the code.  */
+      rootdir = (path >= save_name + len - 1
+                && (IS_DIRECTORY_SEP (*path) || *path == 0));
+
+      /* 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';
+
+      /* Note: If NAME is a symlink to the root of a UNC volume
+        (i.e. "\\SERVER"), we will not detect that here, and we will
+        return data about the symlink as result of FindFirst below.
+        This is unfortunate, but that marginal use case does not
+        justify a call to chase_symlinks which would impose a penalty
+        on all the other use cases.  (We get here for symlinks to
+        roots of UNC volumes because CreateFile above fails for them,
+        unlike with symlinks to root directories X:\ of drives.)  */
+      if (is_unc_volume (name))
+       {
+         fattrs = unc_volume_file_attributes (name);
+         if (fattrs == -1)
+           return -1;
+
+         ctime = atime = wtime = utc_base_ft;
+       }
+      else if (rootdir)
+       {
+         if (!IS_DIRECTORY_SEP (name[len-1]))
+           strcat (name, "\\");
+         if (GetDriveType (name) < 2)
+           {
+             errno = ENOENT;
+             return -1;
+           }
+
+         fattrs = FILE_ATTRIBUTE_DIRECTORY;
+         ctime = atime = wtime = utc_base_ft;
+       }
+      else
+       {
+         if (IS_DIRECTORY_SEP (name[len-1]))
+           name[len - 1] = 0;
+
+         /* (This is hacky, but helps when doing file completions on
+            network drives.)  Optimize by using information available from
+            active readdir if possible.  */
+         len = strlen (dir_pathname);
+         if (IS_DIRECTORY_SEP (dir_pathname[len-1]))
+           len--;
+         if (dir_find_handle != INVALID_HANDLE_VALUE
+             && !(is_a_symlink && follow_symlinks)
+             && strnicmp (save_name, dir_pathname, len) == 0
+             && IS_DIRECTORY_SEP (name[len])
+             && xstrcasecmp (name + len + 1, dir_static.d_name) == 0)
+           {
+             /* This was the last entry returned by readdir.  */
+             wfd = dir_find_data;
+           }
+         else
+           {
+             logon_network_drive (name);
+
+             fh = FindFirstFile (name, &wfd);
+             if (fh == INVALID_HANDLE_VALUE)
+               {
+                 errno = ENOENT;
+                 return -1;
+               }
+             FindClose (fh);
+           }
+         /* Note: if NAME is a symlink, the information we get from
+            FindFirstFile is for the symlink, not its target.  */
+         fattrs = wfd.dwFileAttributes;
+         ctime = wfd.ftCreationTime;
+         atime = wfd.ftLastAccessTime;
+         wtime = wfd.ftLastWriteTime;
+         fs_high = wfd.nFileSizeHigh;
+         fs_low = wfd.nFileSizeLow;
+         fake_inode = 0;
+         nlinks = 1;
+         serialnum = volume_info.serialnum;
+       }
+      if (is_a_symlink && !follow_symlinks)
+       buf->st_mode = S_IFLNK;
+      else if (fattrs & FILE_ATTRIBUTE_DIRECTORY)
+       buf->st_mode = S_IFDIR;
+      else
+       buf->st_mode = S_IFREG;
 
       get_file_owner_and_group (NULL, name, buf);
     }
-  xfree (psd);
 
 #if 0
   /* Not sure if there is any point in this.  */
@@ -3547,43 +3790,56 @@ stat (const char * path, struct stat * buf)
     }
 #endif
 
-  /* MSVC defines _ino_t to be short; other libc's might not.  */
-  if (sizeof (buf->st_ino) == 2)
-    buf->st_ino = fake_inode ^ (fake_inode >> 16);
-  else
-    buf->st_ino = fake_inode;
+  buf->st_ino = fake_inode;
 
-  /* volume_info is set indirectly by map_w32_filename */
-  buf->st_dev = volume_info.serialnum;
-  buf->st_rdev = volume_info.serialnum;
+  buf->st_dev = serialnum;
+  buf->st_rdev = serialnum;
 
-  buf->st_size = wfd.nFileSizeHigh;
+  buf->st_size = fs_high;
   buf->st_size <<= 32;
-  buf->st_size += wfd.nFileSizeLow;
+  buf->st_size += fs_low;
+  buf->st_nlink = nlinks;
 
   /* Convert timestamps to Unix format. */
-  buf->st_mtime = convert_time (wfd.ftLastWriteTime);
-  buf->st_atime = convert_time (wfd.ftLastAccessTime);
+  buf->st_mtime = convert_time (wtime);
+  buf->st_atime = convert_time (atime);
   if (buf->st_atime == 0) buf->st_atime = buf->st_mtime;
-  buf->st_ctime = convert_time (wfd.ftCreationTime);
+  buf->st_ctime = convert_time (ctime);
   if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime;
 
   /* determine rwx permissions */
-  if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
-    permission = S_IREAD;
+  if (is_a_symlink && !follow_symlinks)
+    permission = S_IREAD | S_IWRITE | S_IEXEC; /* Posix expectations */
   else
-    permission = S_IREAD | S_IWRITE;
+    {
+      if (fattrs & FILE_ATTRIBUTE_READONLY)
+       permission = S_IREAD;
+      else
+       permission = S_IREAD | S_IWRITE;
 
-  if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-    permission |= S_IEXEC;
-  else if (is_exec (name))
-    permission |= S_IEXEC;
+      if (fattrs & FILE_ATTRIBUTE_DIRECTORY)
+       permission |= S_IEXEC;
+      else if (is_exec (name))
+       permission |= S_IEXEC;
+    }
 
   buf->st_mode |= permission | (permission >> 3) | (permission >> 6);
 
   return 0;
 }
 
+int
+stat (const char * path, struct stat * buf)
+{
+  return stat_worker (path, buf, 1);
+}
+
+int
+lstat (const char * path, struct stat * buf)
+{
+  return stat_worker (path, buf, 0);
+}
+
 /* Provide fstat and utime as well as stat for consistent handling of
    file timestamps. */
 int
@@ -3701,9 +3957,13 @@ utime (const char *name, struct utimbuf *times)
     }
 
   /* Need write access to set times.  */
-  fh = CreateFile (name, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
-                  0, OPEN_EXISTING, 0, NULL);
-  if (fh)
+  fh = CreateFile (name, FILE_WRITE_ATTRIBUTES,
+                  /* If NAME specifies a directory, FILE_SHARE_DELETE
+                     allows other processes to delete files inside it,
+                     while we have the directory open.  */
+                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                  0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+  if (fh != INVALID_HANDLE_VALUE)
     {
       convert_from_time_t (times->actime, &atime);
       convert_from_time_t (times->modtime, &mtime);
@@ -3724,31 +3984,460 @@ utime (const char *name, struct utimbuf *times)
 }
 
 \f
-/* Symlink-related functions that always fail.  Used in fileio.c and in
-   sysdep.c to avoid #ifdef's.  */
+/* Symlink-related functions.  */
+#ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
+#define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1
+#endif
+
 int
-symlink (char const *dummy1, char const *dummy2)
+symlink (char const *filename, char const *linkname)
 {
-  errno = ENOSYS;
-  return -1;
+  char linkfn[MAX_PATH], *tgtfn;
+  DWORD flags = 0;
+  int dir_access, filename_ends_in_slash;
+
+  /* Diagnostics follows Posix as much as possible.  */
+  if (filename == NULL || linkname == NULL)
+    {
+      errno = EFAULT;
+      return -1;
+    }
+  if (!*filename)
+    {
+      errno = ENOENT;
+      return -1;
+    }
+  if (strlen (filename) > MAX_PATH || strlen (linkname) > MAX_PATH)
+    {
+      errno = ENAMETOOLONG;
+      return -1;
+    }
+
+  strcpy (linkfn, map_w32_filename (linkname, NULL));
+  if ((volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) == 0)
+    {
+      errno = EPERM;
+      return -1;
+    }
+
+  /* Note: since empty FILENAME was already rejected, we can safely
+     refer to FILENAME[1].  */
+  if (!(IS_DIRECTORY_SEP (filename[0]) || IS_DEVICE_SEP (filename[1])))
+    {
+      /* Non-absolute FILENAME is understood as being relative to
+        LINKNAME's directory.  We need to prepend that directory to
+        FILENAME to get correct results from sys_access below, since
+        otherwise it will interpret FILENAME relative to the
+        directory where the Emacs process runs.  Note that
+        make-symbolic-link always makes sure LINKNAME is a fully
+        expanded file name.  */
+      char tem[MAX_PATH];
+      char *p = linkfn + strlen (linkfn);
+
+      while (p > linkfn && !IS_ANY_SEP (p[-1]))
+       p--;
+      if (p > linkfn)
+       strncpy (tem, linkfn, p - linkfn);
+      tem[p - linkfn] = '\0';
+      strcat (tem, filename);
+      dir_access = sys_access (tem, D_OK);
+    }
+  else
+    dir_access = sys_access (filename, D_OK);
+
+  /* Since Windows distinguishes between symlinks to directories and
+     to files, we provide a kludgy feature: if FILENAME doesn't
+     exist, but ends in a slash, we create a symlink to directory.  If
+     FILENAME exists and is a directory, we always create a symlink to
+     directory.  */
+  filename_ends_in_slash = IS_DIRECTORY_SEP (filename[strlen (filename) - 1]);
+  if (dir_access == 0 || filename_ends_in_slash)
+    flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
+
+  tgtfn = (char *)map_w32_filename (filename, NULL);
+  if (filename_ends_in_slash)
+    tgtfn[strlen (tgtfn) - 1] = '\0';
+
+  errno = 0;
+  if (!create_symbolic_link (linkfn, tgtfn, flags))
+    {
+      /* ENOSYS is set by create_symbolic_link, when it detects that
+        the OS doesn't support the CreateSymbolicLink API.  */
+      if (errno != ENOSYS)
+       {
+         DWORD w32err = GetLastError ();
+
+         switch (w32err)
+           {
+             /* ERROR_SUCCESS is sometimes returned when LINKFN and
+                TGTFN point to the same file name, go figure.  */
+           case ERROR_SUCCESS:
+           case ERROR_FILE_EXISTS:
+             errno = EEXIST;
+             break;
+           case ERROR_ACCESS_DENIED:
+             errno = EACCES;
+             break;
+           case ERROR_FILE_NOT_FOUND:
+           case ERROR_PATH_NOT_FOUND:
+           case ERROR_BAD_NETPATH:
+           case ERROR_INVALID_REPARSE_DATA:
+             errno = ENOENT;
+             break;
+           case ERROR_DIRECTORY:
+             errno = EISDIR;
+             break;
+           case ERROR_PRIVILEGE_NOT_HELD:
+           case ERROR_NOT_ALL_ASSIGNED:
+             errno = EPERM;
+             break;
+           case ERROR_DISK_FULL:
+             errno = ENOSPC;
+             break;
+           default:
+             errno = EINVAL;
+             break;
+           }
+       }
+      return -1;
+    }
+  return 0;
+}
+
+/* A quick inexpensive test of whether FILENAME identifies a file that
+   is a symlink.  Returns non-zero if it is, zero otherwise.  FILENAME
+   must already be in the normalized form returned by
+   map_w32_filename.
+
+   Note: for repeated operations on many files, it is best to test
+   whether the underlying volume actually supports symlinks, by
+   testing the FILE_SUPPORTS_REPARSE_POINTS bit in volume's flags, and
+   avoid the call to this function if it doesn't.  That's because the
+   call to GetFileAttributes takes a non-negligible time, especially
+   on non-local or removable filesystems.  See stat_worker for an
+   example of how to do that.  */
+static int
+is_symlink (const char *filename)
+{
+  DWORD attrs;
+  WIN32_FIND_DATA wfd;
+  HANDLE fh;
+
+  attrs = GetFileAttributes (filename);
+  if (attrs == -1)
+    {
+      DWORD w32err = GetLastError ();
+
+      switch (w32err)
+       {
+       case ERROR_BAD_NETPATH: /* network share, can't be a symlink */
+         break;
+       case ERROR_ACCESS_DENIED:
+         errno = EACCES;
+         break;
+       case ERROR_FILE_NOT_FOUND:
+       case ERROR_PATH_NOT_FOUND:
+       default:
+         errno = ENOENT;
+         break;
+       }
+      return 0;
+    }
+  if ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
+    return 0;
+  logon_network_drive (filename);
+  fh = FindFirstFile (filename, &wfd);
+  if (fh == INVALID_HANDLE_VALUE)
+    return 0;
+  FindClose (fh);
+  return (wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0
+         && (wfd.dwReserved0 & IO_REPARSE_TAG_SYMLINK) == IO_REPARSE_TAG_SYMLINK;
 }
 
+/* If NAME identifies a symbolic link, copy into BUF the file name of
+   the symlink's target.  Copy at most BUF_SIZE bytes, and do NOT
+   null-terminate the target name, even if it fits.  Return the number
+   of bytes copied, or -1 if NAME is not a symlink or any error was
+   encountered while resolving it.  The file name copied into BUF is
+   encoded in the current ANSI codepage.  */
 ssize_t
-readlink (const char *name, char *dummy1, size_t dummy2)
+readlink (const char *name, char *buf, size_t buf_size)
 {
-  /* `access' is much faster than `stat' on MS-Windows.  */
-  if (sys_access (name, 0) == 0)
-    errno = EINVAL;
-  return -1;
+  const char *path;
+  TOKEN_PRIVILEGES privs;
+  int restore_privs = 0;
+  HANDLE sh;
+  ssize_t retval;
+
+  if (name == NULL)
+    {
+      errno = EFAULT;
+      return -1;
+    }
+  if (!*name)
+    {
+      errno = ENOENT;
+      return -1;
+    }
+
+  path = map_w32_filename (name, NULL);
+
+  if (strlen (path) > MAX_PATH)
+    {
+      errno = ENAMETOOLONG;
+      return -1;
+    }
+
+  errno = 0;
+  if (is_windows_9x () == TRUE
+      || (volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) == 0
+      || !is_symlink (path))
+    {
+      if (!errno)
+       errno = EINVAL; /* not a symlink */
+      return -1;
+    }
+
+  /* Done with simple tests, now we're in for some _real_ work.  */
+  if (enable_privilege (SE_BACKUP_NAME, TRUE, &privs))
+    restore_privs = 1;
+  /* Implementation note: From here and onward, don't return early,
+     since that will fail to restore the original set of privileges of
+     the calling thread.  */
+
+  retval = -1; /* not too optimistic, are we? */
+
+  /* Note: In the next call to CreateFile, we use zero as the 2nd
+     argument because, when the symlink is a hidden/system file,
+     e.g. 'C:\Users\All Users', GENERIC_READ fails with
+     ERROR_ACCESS_DENIED.  Zero seems to work just fine, both for file
+     and directory symlinks.  */
+  sh = CreateFile (path, 0, 0, NULL, OPEN_EXISTING,
+                  FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
+                  NULL);
+  if (sh != INVALID_HANDLE_VALUE)
+    {
+      BYTE reparse_buf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+      REPARSE_DATA_BUFFER *reparse_data = (REPARSE_DATA_BUFFER *)&reparse_buf[0];
+      DWORD retbytes;
+
+      if (!DeviceIoControl (sh, FSCTL_GET_REPARSE_POINT, NULL, 0,
+                           reparse_buf, MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
+                           &retbytes, NULL))
+       errno = EIO;
+      else if (reparse_data->ReparseTag != IO_REPARSE_TAG_SYMLINK)
+       errno = EINVAL;
+      else
+       {
+         /* Copy the link target name, in wide characters, fro
+            reparse_data, then convert it to multibyte encoding in
+            the current locale's codepage.  */
+         WCHAR *lwname;
+         BYTE  lname[MAX_PATH];
+         USHORT lname_len;
+         USHORT lwname_len =
+           reparse_data->SymbolicLinkReparseBuffer.PrintNameLength;
+         WCHAR *lwname_src =
+           reparse_data->SymbolicLinkReparseBuffer.PathBuffer
+           + reparse_data->SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(WCHAR);
+
+         /* According to MSDN, PrintNameLength does not include the
+            terminating null character.  */
+         lwname = alloca ((lwname_len + 1) * sizeof(WCHAR));
+         memcpy (lwname, lwname_src, lwname_len);
+         lwname[lwname_len/sizeof(WCHAR)] = 0; /* null-terminate */
+
+         /* FIXME: Should we use the current file-name coding system
+            instead of the fixed value of the ANSI codepage?  */
+         lname_len = WideCharToMultiByte (w32_ansi_code_page, 0, lwname, -1,
+                                          lname, MAX_PATH, NULL, NULL);
+         if (!lname_len)
+           {
+             /* WideCharToMultiByte failed.  */
+             DWORD w32err1 = GetLastError ();
+
+             switch (w32err1)
+               {
+               case ERROR_INSUFFICIENT_BUFFER:
+                 errno = ENAMETOOLONG;
+                 break;
+               case ERROR_INVALID_PARAMETER:
+                 errno = EFAULT;
+                 break;
+               case ERROR_NO_UNICODE_TRANSLATION:
+                 errno = ENOENT;
+                 break;
+               default:
+                 errno = EINVAL;
+                 break;
+               }
+           }
+         else
+           {
+             size_t size_to_copy = buf_size;
+             BYTE *p = lname;
+             BYTE *pend = p + lname_len;
+
+             /* Normalize like dostounix_filename does, but we don't
+                want to assume that lname is null-terminated.  */
+             if (*p && p[1] == ':' && *p >= 'A' && *p <= 'Z')
+               *p += 'a' - 'A';
+             while (p <= pend)
+               {
+                 if (*p == '\\')
+                   *p = '/';
+                 ++p;
+               }
+             /* Testing for null-terminated LNAME is paranoia:
+                WideCharToMultiByte should always return a
+                null-terminated string when its 4th argument is -1
+                and its 3rd argument is null-terminated (which they
+                are, see above).  */
+             if (lname[lname_len - 1] == '\0')
+               lname_len--;
+             if (lname_len <= buf_size)
+               size_to_copy = lname_len;
+             strncpy (buf, lname, size_to_copy);
+             /* Success!  */
+             retval = size_to_copy;
+           }
+       }
+      CloseHandle (sh);
+    }
+  else
+    {
+      /* CreateFile failed.  */
+      DWORD w32err2 = GetLastError ();
+
+      switch (w32err2)
+       {
+       case ERROR_FILE_NOT_FOUND:
+       case ERROR_PATH_NOT_FOUND:
+         errno = ENOENT;
+         break;
+       case ERROR_ACCESS_DENIED:
+       case ERROR_TOO_MANY_OPEN_FILES:
+         errno = EACCES;
+         break;
+       default:
+         errno = EPERM;
+         break;
+       }
+    }
+  if (restore_privs)
+    {
+      restore_privilege (&privs);
+      revert_to_self ();
+    }
+
+  return retval;
+}
+
+/* If FILE is a symlink, return its target (stored in a static
+   buffer); otherwise return FILE.
+
+   This function repeatedly resolves symlinks in the last component of
+   a chain of symlink file names, as in foo -> bar -> baz -> ...,
+   until it arrives at a file whose last component is not a symlink,
+   or some error occurs.  It returns the target of the last
+   successfully resolved symlink in the chain.  If it succeeds to
+   resolve even a single symlink, the value returned is an absolute
+   file name with backslashes (result of GetFullPathName).  By
+   contrast, if the original FILE is returned, it is unaltered.
+
+   Note: This function can set errno even if it succeeds.
+
+   Implementation note: we only resolve the last portion ("basename")
+   of the argument FILE and of each following file in the chain,
+   disregarding any possible symlinks in its leading directories.
+   This is because Windows system calls and library functions
+   transparently resolve symlinks in leading directories and return
+   correct information, as long as the basename is not a symlink.  */
+static char *
+chase_symlinks (const char *file)
+{
+  static char target[MAX_PATH];
+  char link[MAX_PATH];
+  ssize_t res, link_len;
+  int loop_count = 0;
+
+  if (is_windows_9x () == TRUE || !is_symlink (file))
+    return (char *)file;
+
+  if ((link_len = GetFullPathName (file, MAX_PATH, link, NULL)) == 0)
+    return (char *)file;
+
+  target[0] = '\0';
+  do {
+
+    /* Remove trailing slashes, as we want to resolve the last
+       non-trivial part of the link name.  */
+    while (link_len > 3 && IS_DIRECTORY_SEP (link[link_len-1]))
+      link[link_len--] = '\0';
+
+    res = readlink (link, target, MAX_PATH);
+    if (res > 0)
+      {
+       target[res] = '\0';
+       if (!(IS_DEVICE_SEP (target[1])
+             || (IS_DIRECTORY_SEP (target[0]) && IS_DIRECTORY_SEP (target[1]))))
+         {
+           /* Target is relative.  Append it to the directory part of
+              the symlink, then copy the result back to target.  */
+           char *p = link + link_len;
+
+           while (p > link && !IS_ANY_SEP (p[-1]))
+             p--;
+           strcpy (p, target);
+           strcpy (target, link);
+         }
+       /* Resolve any "." and ".." to get a fully-qualified file name
+          in link[] again. */
+       link_len = GetFullPathName (target, MAX_PATH, link, NULL);
+      }
+  } while (res > 0 && link_len > 0 && ++loop_count <= 100);
+
+  if (loop_count > 100)
+    errno = ELOOP;
+
+  if (target[0] == '\0') /* not a single call to readlink succeeded */
+    return (char *)file;
+  return target;
 }
 
+/* MS-Windows version of careadlinkat (cf. ../lib/careadlinkat.c).  We
+   have a fixed max size for file names, so we don't need the kind of
+   alloc/malloc/realloc dance the gnulib version does.  We also don't
+   support FD-relative symlinks.  */
 char *
 careadlinkat (int fd, char const *filename,
               char *buffer, size_t buffer_size,
               struct allocator const *alloc,
               ssize_t (*preadlinkat) (int, char const *, char *, size_t))
 {
-  errno = ENOSYS;
+  char linkname[MAX_PATH];
+  ssize_t link_size;
+
+  if (fd != AT_FDCWD)
+    {
+      errno = EINVAL;
+      return NULL;
+    }
+
+  link_size = preadlinkat (fd, filename, linkname, sizeof(linkname));
+
+  if (link_size > 0)
+    {
+      char *retval = buffer;
+
+      linkname[link_size++] = '\0';
+      if (link_size > buffer_size)
+       retval = (char *)(alloc ? alloc->allocate : xmalloc) (link_size);
+      if (retval)
+       memcpy (retval, linkname, link_size);
+
+      return retval;
+    }
   return NULL;
 }
 
@@ -3931,8 +4620,8 @@ get_process_memory_info (HANDLE h_proc,
 
 static BOOL WINAPI
 get_process_working_set_size (HANDLE h_proc,
-                             DWORD *minrss,
-                             DWORD *maxrss)
+                             PSIZE_T minrss,
+                             PSIZE_T maxrss)
 {
   static GetProcessWorkingSetSize_Proc
     s_pfn_Get_Process_Working_Set_Size = NULL;
@@ -4177,7 +4866,7 @@ system_process_attributes (Lisp_Object pid)
   unsigned egid;
   PROCESS_MEMORY_COUNTERS mem;
   PROCESS_MEMORY_COUNTERS_EX mem_ex;
-  DWORD minrss, maxrss;
+  SIZE_T minrss, maxrss;
   MEMORYSTATUS memst;
   MEMORY_STATUS_EX memstex;
   double totphys = 0.0;
@@ -4405,7 +5094,7 @@ system_process_attributes (Lisp_Object pid)
       && get_process_memory_info (h_proc, (PROCESS_MEMORY_COUNTERS *)&mem_ex,
                                  sizeof (mem_ex)))
     {
-      DWORD rss = mem_ex.WorkingSetSize / 1024;
+      SIZE_T rss = mem_ex.WorkingSetSize / 1024;
 
       attrs = Fcons (Fcons (Qmajflt,
                            make_fixnum_or_float (mem_ex.PageFaultCount)),
@@ -4420,7 +5109,7 @@ system_process_attributes (Lisp_Object pid)
   else if (h_proc
           && get_process_memory_info (h_proc, &mem, sizeof (mem)))
     {
-      DWORD rss = mem_ex.WorkingSetSize / 1024;
+      SIZE_T rss = mem_ex.WorkingSetSize / 1024;
 
       attrs = Fcons (Fcons (Qmajflt,
                            make_fixnum_or_float (mem.PageFaultCount)),
@@ -4846,7 +5535,7 @@ socket_to_fd (SOCKET s)
          if (fd_info[ fd ].cp != NULL)
            {
              DebPrint (("sys_socket: fd_info[%d] apparently in use!\n", fd));
-             abort ();
+             emacs_abort ();
            }
 
          fd_info[ fd ].cp = cp;
@@ -5225,7 +5914,7 @@ sys_close (int fd)
            {
              if (fd_info[fd].flags & FILE_SOCKET)
                {
-                 if (winsock_lib == NULL) abort ();
+                 if (winsock_lib == NULL) emacs_abort ();
 
                  pfn_shutdown (SOCK_HANDLE (fd), 2);
                  rc = pfn_closesocket (SOCK_HANDLE (fd));
@@ -5343,7 +6032,7 @@ _sys_read_ahead (int fd)
       || (fd_info[fd].flags & FILE_READ) == 0)
     {
       DebPrint (("_sys_read_ahead: internal error: fd %d is not a pipe, serial port, or socket!\n", fd));
-      abort ();
+      emacs_abort ();
     }
 
   cp->status = STATUS_READ_IN_PROGRESS;
@@ -5479,7 +6168,7 @@ sys_read (int fd, char * buffer, unsigned int count)
       /* re-read CR carried over from last read */
       if (fd_info[fd].flags & FILE_LAST_CR)
        {
-         if (fd_info[fd].flags & FILE_BINARY) abort ();
+         if (fd_info[fd].flags & FILE_BINARY) emacs_abort ();
          *buffer++ = 0x0d;
          count--;
          nchars++;
@@ -5582,7 +6271,7 @@ sys_read (int fd, char * buffer, unsigned int count)
            }
          else /* FILE_SOCKET */
            {
-             if (winsock_lib == NULL) abort ();
+             if (winsock_lib == NULL) emacs_abort ();
 
              /* do the equivalent of a non-blocking read */
              pfn_ioctlsocket (SOCK_HANDLE (fd), FIONREAD, &waiting);
@@ -5733,7 +6422,7 @@ sys_write (int fd, const void * buffer, unsigned int count)
   else if (fd < MAXDESC && fd_info[fd].flags & FILE_SOCKET)
     {
       unsigned long nblock = 0;
-      if (winsock_lib == NULL) abort ();
+      if (winsock_lib == NULL) emacs_abort ();
 
       /* TODO: implement select() properly so non-blocking I/O works. */
       /* For now, make sure the write blocks.  */
@@ -5805,33 +6494,27 @@ sys_localtime (const time_t *t)
 
 
 \f
-/* Delayed loading of libraries.  */
-
-Lisp_Object Vlibrary_cache;
-
-/* The argument LIBRARIES is an alist that associates a symbol
-   LIBRARY_ID, identifying an external DLL library known to Emacs, to
-   a list of filenames under which the library is usually found.  In
-   most cases, the argument passed as LIBRARIES is the variable
-   `dynamic-library-alist', which is initialized to a list of common
-   library names.  If the function loads the library successfully, it
-   returns the handle of the DLL, and records the filename in the
-   property :loaded-from of LIBRARY_ID; it returns NULL if the library
-   could not be found, or when it was already loaded (because the
-   handle is not recorded anywhere, and so is lost after use).  It
-   would be trivial to save the handle too in :loaded-from, but
-   currently there's no use case for it.  */
+/* Try loading LIBRARY_ID from the file(s) specified in
+   Vdynamic_library_alist.  If the library is loaded successfully,
+   return the handle of the DLL, and record the filename in the
+   property :loaded-from of LIBRARY_ID.  If the library could not be
+   found, or when it was already loaded (because the handle is not
+   recorded anywhere, and so is lost after use), return NULL.
+
+   We could also save the handle in :loaded-from, but currently
+   there's no use case for it.  */
 HMODULE
-w32_delayed_load (Lisp_Object libraries, Lisp_Object library_id)
+w32_delayed_load (Lisp_Object library_id)
 {
   HMODULE library_dll = NULL;
 
   CHECK_SYMBOL (library_id);
 
-  if (CONSP (libraries) && NILP (Fassq (library_id, Vlibrary_cache)))
+  if (CONSP (Vdynamic_library_alist)
+      && NILP (Fassq (library_id, Vlibrary_cache)))
     {
       Lisp_Object found = Qnil;
-      Lisp_Object dlls = Fassq (library_id, libraries);
+      Lisp_Object dlls = Fassq (library_id, Vdynamic_library_alist);
 
       if (CONSP (dlls))
         for (dlls = XCDR (dlls); CONSP (dlls); dlls = XCDR (dlls))
@@ -5859,7 +6542,7 @@ w32_delayed_load (Lisp_Object libraries, Lisp_Object library_id)
 }
 
 \f
-static void
+void
 check_windows_init_file (void)
 {
   /* A common indication that Emacs is not installed properly is when
@@ -5871,19 +6554,14 @@ check_windows_init_file (void)
         loadup.el.  */
       && NILP (Vpurify_flag))
     {
-      Lisp_Object objs[2];
-      Lisp_Object full_load_path;
       Lisp_Object init_file;
       int fd;
 
-      objs[0] = Vload_path;
-      objs[1] = decode_env_path (0, (getenv ("EMACSLOADPATH")));
-      full_load_path = Fappend (2, objs);
       init_file = build_string ("term/w32-win");
-      fd = openp (full_load_path, init_file, Fget_load_suffixes (), NULL, Qnil);
+      fd = openp (Vload_path, init_file, Fget_load_suffixes (), NULL, Qnil);
       if (fd < 0)
        {
-         Lisp_Object load_path_print = Fprin1_to_string (full_load_path, Qnil);
+         Lisp_Object load_path_print = Fprin1_to_string (Vload_path, Qnil);
          char *init_file_name = SDATA (init_file);
          char *load_path = SDATA (load_path_print);
          char *buffer = alloca (1024
@@ -5904,8 +6582,7 @@ check_windows_init_file (void)
                      buffer,
                      "Emacs Abort Dialog",
                      MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
-      /* Use the low-level Emacs abort. */
-#undef abort
+         /* Use the low-level system abort. */
          abort ();
        }
       else
@@ -5916,8 +6593,12 @@ check_windows_init_file (void)
 }
 
 void
-term_ntproc (void)
+term_ntproc (int ignored)
 {
+  (void)ignored;
+
+  term_timers ();
+
   /* shutdown the socket interface if necessary */
   term_winsock ();
 
@@ -5925,8 +6606,10 @@ term_ntproc (void)
 }
 
 void
-init_ntproc (void)
+init_ntproc (int dumping)
 {
+  sigset_t initial_mask = 0;
+
   /* Initialize the socket interface now if available and requested by
      the user by defining PRELOAD_WINSOCK; otherwise loading will be
      delayed until open-network-stream is called (w32-has-winsock can
@@ -5982,19 +6665,19 @@ init_ntproc (void)
     fclose (stderr);
 
     if (stdin_save != INVALID_HANDLE_VALUE)
-      _open_osfhandle ((long) stdin_save, O_TEXT);
+      _open_osfhandle ((intptr_t) stdin_save, O_TEXT);
     else
       _open ("nul", O_TEXT | O_NOINHERIT | O_RDONLY);
     _fdopen (0, "r");
 
     if (stdout_save != INVALID_HANDLE_VALUE)
-      _open_osfhandle ((long) stdout_save, O_TEXT);
+      _open_osfhandle ((intptr_t) stdout_save, O_TEXT);
     else
       _open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY);
     _fdopen (1, "w");
 
     if (stderr_save != INVALID_HANDLE_VALUE)
-      _open_osfhandle ((long) stderr_save, O_TEXT);
+      _open_osfhandle ((intptr_t) stderr_save, O_TEXT);
     else
       _open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY);
     _fdopen (2, "w");
@@ -6002,7 +6685,13 @@ init_ntproc (void)
 
   /* unfortunately, atexit depends on implementation of malloc */
   /* atexit (term_ntproc); */
-  signal (SIGABRT, term_ntproc);
+  if (!dumping)
+    {
+      /* Make sure we start with all signals unblocked.  */
+      sigprocmask (SIG_SETMASK, &initial_mask, NULL);
+      signal (SIGABRT, term_ntproc);
+    }
+  init_timers ();
 
   /* determine which drives are fixed, for GetCachedVolumeInformation */
   {
@@ -6022,9 +6711,6 @@ init_ntproc (void)
     /* Reset the volume info cache.  */
     volume_cache = NULL;
   }
-
-  /* Check to see if Emacs has been installed correctly.  */
-  check_windows_init_file ();
 }
 
 /*
@@ -6040,7 +6726,7 @@ shutdown_handler (DWORD type)
       || type == CTRL_SHUTDOWN_EVENT) /* User shutsdown.  */
     {
       /* Shut down cleanly, making sure autosave files are up to date.  */
-      shut_down_emacs (0, 0, Qnil);
+      shut_down_emacs (0, Qnil);
     }
 
   /* Allow other handlers to handle this signal.  */
@@ -6062,15 +6748,13 @@ globals_of_w32 (void)
 
   DEFSYM (QCloaded_from, ":loaded-from");
 
-  Vlibrary_cache = Qnil;
-  staticpro (&Vlibrary_cache);
-
   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_sub_authority = 0;
   g_b_init_get_sid_sub_authority_count = 0;
+  g_b_init_get_security_info = 0;
   g_b_init_get_file_security = 0;
   g_b_init_get_security_descriptor_owner = 0;
   g_b_init_get_security_descriptor_group = 0;
@@ -6090,6 +6774,7 @@ globals_of_w32 (void)
   g_b_init_get_length_sid = 0;
   g_b_init_get_native_system_info = 0;
   g_b_init_get_system_times = 0;
+  g_b_init_create_symbolic_link = 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
@@ -6113,7 +6798,7 @@ serial_open (char *port)
                    OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
   if (hnd == INVALID_HANDLE_VALUE)
     error ("Could not open %s", port);
-  fd = (int) _open_osfhandle ((int) hnd, 0);
+  fd = (int) _open_osfhandle ((intptr_t) hnd, 0);
   if (fd == -1)
     error ("Could not open %s", port);
 
@@ -6288,7 +6973,7 @@ serial_configure (struct Lisp_Process *p, Lisp_Object contact)
     error ("SetCommState() failed");
 
   childp2 = Fplist_put (childp2, QCsummary, build_string (summary));
-  p->childp = childp2;
+  pset_childp (p, childp2);
 }
 
 #ifdef HAVE_GNUTLS
@@ -6298,7 +6983,7 @@ emacs_gnutls_pull (gnutls_transport_ptr_t p, void* buf, size_t sz)
 {
   int n, sc, err;
   SELECT_TYPE fdset;
-  struct timeval timeout;
+  EMACS_TIME timeout;
   struct Lisp_Process *process = (struct Lisp_Process *)p;
   int fd = process->infd;
 
@@ -6314,14 +6999,13 @@ emacs_gnutls_pull (gnutls_transport_ptr_t p, void* buf, size_t sz)
       if (err == EWOULDBLOCK)
         {
           /* Set a small timeout.  */
-         timeout.tv_sec = 1;
-         timeout.tv_usec = 0;
+         timeout = make_emacs_time (1, 0);
           FD_ZERO (&fdset);
           FD_SET ((int)fd, &fdset);
 
           /* Use select with the timeout to poll the selector.  */
           sc = select (fd + 1, &fdset, (SELECT_TYPE *)0, (SELECT_TYPE *)0,
-                       &timeout);
+                       &timeout, NULL);
 
           if (sc > 0)
             continue;  /* Try again.  */