Fix handling of fatal signals and exceptions on MS-Windows.
[bpt/emacs.git] / src / w32.c
index de72e18..f7e1635 100644 (file)
--- a/src/w32.c
+++ b/src/w32.c
@@ -1,5 +1,5 @@
-/* Utility and Unix shadow routines for GNU Emacs on the Microsoft W32 API.
-   Copyright (C) 1994-1995, 2000-2011  Free Software Foundation, Inc.
+/* 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.
 
@@ -33,7 +33,6 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #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 */
@@ -94,8 +93,10 @@ typedef struct _MEMORY_STATUS_EX {
 
 #include <tlhelp32.h>
 #include <psapi.h>
+#ifndef _MSC_VER
 #include <w32api.h>
-#if !defined(__MINGW32__) || __W32API_MAJOR_VERSION < 3 || (__W32API_MAJOR_VERSION == 3 && __W32API_MINOR_VERSION < 15)
+#endif
+#if !defined (__MINGW32__) || __W32API_MAJOR_VERSION < 3 || (__W32API_MAJOR_VERSION == 3 && __W32API_MINOR_VERSION < 15)
 /* This either is not in psapi.h or guarded by higher value of
    _WIN32_WINNT than what we use.  w32api supplied with MinGW 3.15
    defines it in psapi.h  */
@@ -114,6 +115,42 @@ typedef struct _PROCESS_MEMORY_COUNTERS_EX {
 } 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, 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;
+
+#endif
+
 /* TCP connection support.  */
 #include <sys/socket.h>
 #undef socket
@@ -154,6 +191,11 @@ 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);
 
 \f
 /* Initialization states.
@@ -171,6 +213,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;
@@ -190,6 +233,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
@@ -236,6 +280,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,
@@ -296,6 +349,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
@@ -316,8 +373,10 @@ is_windows_9x (void)
   return s_b_ret;
 }
 
+static Lisp_Object ltime (ULONGLONG);
+
 /* 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
+   Returns a list of 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 (void)
@@ -329,27 +388,12 @@ w32_get_internal_run_time (void)
       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 ltime (total.QuadPart);
         }
     }
 
@@ -510,6 +554,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,
@@ -737,6 +814,57 @@ get_system_times (LPFILETIME lpIdleTime,
     return FALSE;
   return (s_pfn_Get_System_times (lpIdleTime, lpKernelTime, lpUserTime));
 }
+
+static BOOLEAN WINAPI
+create_symbolic_link (LPTSTR lpSymlinkFilename,
+                     LPTSTR lpTargetFileName,
+                     DWORD dwFlags)
+{
+  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;
+    }
+
+  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;
+
+      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
 /* Equivalent of strerror for W32 error codes.  */
 char *
@@ -789,9 +917,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
@@ -1145,9 +1272,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"));
@@ -1402,34 +1529,10 @@ is_unc_volume (const char *filename)
 }
 
 /* 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)
 {
+  *set = 0;
   return 0;
 }
 
@@ -1488,7 +1591,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);
@@ -1505,7 +1608,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);
@@ -1547,7 +1650,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.  */
-      if (tmp && _access (tmp, D_OK) == 0)
+
+      /* MSVCRT's _access crashes with D_OK.  */
+      if (tmp && sys_access (tmp, D_OK) == 0)
        {
          char * var = alloca (strlen (tmp) + 8);
          sprintf (var, "TMPDIR=%s", tmp);
@@ -1579,17 +1684,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},
     };
@@ -1650,16 +1757,15 @@ init_environment (char ** argv)
 #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)
@@ -1716,13 +1822,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)
@@ -1769,27 +1873,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;
   }
 
@@ -1801,6 +1895,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)
 {
@@ -1809,7 +1905,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);
@@ -1914,6 +2010,9 @@ get_emacs_configuration_options (void)
     cv,  /* To be filled later.  */
 #ifdef EMACSDEBUG
     " --no-opt",
+#endif
+#ifdef ENABLE_CHECKING
+    " --enable-checking",
 #endif
     /* configure.bat already sets USER_CFLAGS and USER_LDFLAGS
        with a starting space to save work here.  */
@@ -1972,7 +2071,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  */
@@ -1980,8 +2079,43 @@ gettimeofday (struct timeval *tv, struct timezone *tz)
     }
 }
 
+/* Emulate fdutimens.  */
+
+/* Set the access and modification time stamps of FD (a.k.a. FILE) to be
+   TIMESPEC[0] and TIMESPEC[1], respectively.
+   FD must be either negative -- in which case it is ignored --
+   or a file descriptor that is open on FILE.
+   If FD is nonnegative, then FILE can be NULL, which means
+   use just futimes instead of utimes.
+   If TIMESPEC is null, FAIL.
+   Return 0 on success, -1 (setting errno) on failure.  */
+
+int
+fdutimens (int fd, char const *file, struct timespec const timespec[2])
+{
+  struct _utimbuf ut;
+
+  if (!timespec)
+    {
+      errno = ENOSYS;
+      return -1;
+    }
+  if (fd < 0 && !file)
+    {
+      errno = EBADF;
+      return -1;
+    }
+  ut.actime = timespec[0].tv_sec;
+  ut.modtime = timespec[1].tv_sec;
+  if (fd >= 0)
+    return _futime (fd, &ut);
+  else
+    return _utime (file, &ut);
+}
+
+
 /* ------------------------------------------------------------------------- */
-/* 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
@@ -2137,7 +2271,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
@@ -2157,8 +2291,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)
 {
@@ -2169,7 +2310,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;
@@ -2209,7 +2350,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)
 {
@@ -2218,7 +2360,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)
 {
@@ -2248,15 +2391,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 */
@@ -2365,6 +2503,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);
@@ -2381,6 +2522,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;
@@ -2427,6 +2571,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)
@@ -2617,21 +2764,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))
@@ -2661,7 +2830,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
@@ -2869,6 +3039,8 @@ sys_rename (const char * oldname, const char * newname)
 {
   BOOL result;
   char temp[MAX_PATH];
+  int newname_dev;
+  int oldname_dev;
 
   /* MoveFile on Windows 95 doesn't correctly change the short file name
      alias in a number of circumstances (it is not easy to predict when
@@ -2885,19 +3057,22 @@ sys_rename (const char * oldname, const char * newname)
 
   strcpy (temp, map_w32_filename (oldname, NULL));
 
-  if (os_subtype == OS_WIN95)
+  /* volume_info is set indirectly by map_w32_filename.  */
+  oldname_dev = volume_info.serialnum;
+
+  if (os_subtype == OS_9X)
     {
       char * o;
       char * p;
       int    i = 0;
 
       oldname = map_w32_filename (oldname, NULL);
-      if (o = strrchr (oldname, '\\'))
+      if ((o = strrchr (oldname, '\\')))
        o++;
       else
        o = (char *) oldname;
 
-      if (p = strrchr (temp, '\\'))
+      if ((p = strrchr (temp, '\\')))
        p++;
       else
        p = temp;
@@ -2928,13 +3103,47 @@ sys_rename (const char * oldname, const char * newname)
      all the permutations of shared or subst'd drives, etc.)  */
 
   newname = map_w32_filename (newname, NULL);
+
+  /* volume_info is set indirectly by map_w32_filename.  */
+  newname_dev = volume_info.serialnum;
+
   result = rename (temp, newname);
 
-  if (result < 0
-      && errno == EEXIST
-      && _chmod (newname, 0666) == 0
-      && _unlink (newname) == 0)
-    result = rename (temp, newname);
+  if (result < 0)
+    {
+      DWORD w32err = GetLastError ();
+
+      if (errno == EACCES
+         && newname_dev != oldname_dev)
+       {
+         /* The implementation of `rename' on Windows does not return
+            errno = EXDEV when you are moving a directory to a
+            different storage device (ex. logical disk).  It returns
+            EACCES instead.  So here we handle such situations and
+            return EXDEV.  */
+         DWORD attributes;
+
+         if ((attributes = GetFileAttributes (temp)) != -1
+             && (attributes & FILE_ATTRIBUTE_DIRECTORY))
+           errno = EXDEV;
+       }
+      else if (errno == EEXIST)
+       {
+         if (_chmod (newname, 0666) != 0)
+           return result;
+         if (_unlink (newname) != 0)
+           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;
 }
@@ -3050,10 +3259,10 @@ generate_inode_val (const char * name)
   unsigned hash;
 
   /* Get the truly canonical filename, if it exists.  (Note: this
-     doesn't resolve aliasing due to subst commands, or recognise hard
+     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. */
@@ -3064,7 +3273,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;
@@ -3280,18 +3505,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)
     {
@@ -3299,7 +3530,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
@@ -3311,99 +3542,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))
     {
-      /* This is more accurate in terms of gettting 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 (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). */
       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
@@ -3412,20 +3611,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;
@@ -3439,21 +3671,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.  */
@@ -3467,43 +3821,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
@@ -3644,31 +4011,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;
 }
 
@@ -4013,14 +4809,17 @@ restore_privilege (TOKEN_PRIVILEGES *priv)
 }
 
 static Lisp_Object
-ltime (long time_sec, long time_usec)
+ltime (ULONGLONG time_100ns)
 {
-  return list3 (make_number ((time_sec >> 16) & 0xffff),
+  ULONGLONG time_sec = time_100ns / 10000000;
+  int subsec = time_100ns % 10000000;
+  return list4 (make_number (time_sec >> 16),
                make_number (time_sec & 0xffff),
-               make_number (time_usec));
+               make_number (subsec / 10),
+               make_number (subsec % 10 * 100000));
 }
 
-#define U64_TO_LISP_TIME(time) ltime ((time) / 1000000L, (time) % 1000000L)
+#define U64_TO_LISP_TIME(time) ltime (time)
 
 static int
 process_times (HANDLE h_proc, Lisp_Object *ctime, Lisp_Object *etime,
@@ -4039,11 +4838,9 @@ process_times (HANDLE h_proc, Lisp_Object *ctime, Lisp_Object *etime,
   GetSystemTimeAsFileTime (&ft_current);
 
   FILETIME_TO_U64 (tem1, ft_kernel);
-  tem1 /= 10L;
   *stime = U64_TO_LISP_TIME (tem1);
 
   FILETIME_TO_U64 (tem2, ft_user);
-  tem2 /= 10L;
   *utime = U64_TO_LISP_TIME (tem2);
 
   tem3 = tem1 + tem2;
@@ -4052,13 +4849,13 @@ process_times (HANDLE h_proc, Lisp_Object *ctime, Lisp_Object *etime,
   FILETIME_TO_U64 (tem, ft_creation);
   /* Process no 4 (System) returns zero creation time.  */
   if (tem)
-    tem = (tem - utc_base) / 10L;
+    tem -= utc_base;
   *ctime = U64_TO_LISP_TIME (tem);
 
   if (tem)
     {
       FILETIME_TO_U64 (tem3, ft_current);
-      tem = (tem3 - utc_base) / 10L - tem;
+      tem = (tem3 - utc_base) - tem;
     }
   *etime = U64_TO_LISP_TIME (tem);
 
@@ -4765,7 +5562,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;
@@ -5144,7 +5941,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));
@@ -5262,7 +6059,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;
@@ -5398,7 +6195,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++;
@@ -5501,7 +6298,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);
@@ -5652,7 +6449,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.  */
@@ -5728,37 +6525,43 @@ sys_localtime (const time_t *t)
 
 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))
           {
             CHECK_STRING_CAR (dlls);
-            if (library_dll = LoadLibrary (SDATA (XCAR (dlls))))
+            if ((library_dll = LoadLibrary (SDATA (XCAR (dlls)))))
               {
-                found = XCAR (dlls);
+                char name[MAX_PATH];
+                DWORD len;
+
+                len = GetModuleFileNameA (library_dll, name, sizeof (name));
+                found = Fcons (XCAR (dlls),
+                               (len > 0)
+                               /* Possibly truncated */
+                               ? make_specified_string (name, -1, len, 1)
+                               : Qnil);
                 break;
               }
           }
@@ -5770,28 +6573,26 @@ 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
      it cannot find the Windows installation file.  If this file does
      not exist in the expected place, tell the user.  */
 
-  if (!noninteractive && !inhibit_window_system)
+  if (!noninteractive && !inhibit_window_system
+      /* Vload_path is not yet initialized when we are loading
+        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
@@ -5812,8 +6613,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
@@ -5824,8 +6624,9 @@ check_windows_init_file (void)
 }
 
 void
-term_ntproc (void)
+term_ntproc (int ignored)
 {
+  (void)ignored;
   /* shutdown the socket interface if necessary */
   term_winsock ();
 
@@ -5833,9 +6634,9 @@ term_ntproc (void)
 }
 
 void
-init_ntproc (void)
+init_ntproc (int dumping)
 {
-  /* Initialise the socket interface now if available and requested by
+  /* 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
      also be used to dynamically load or reload winsock).
@@ -5910,7 +6711,8 @@ init_ntproc (void)
 
   /* unfortunately, atexit depends on implementation of malloc */
   /* atexit (term_ntproc); */
-  signal (SIGABRT, term_ntproc);
+  if (!dumping)
+    signal (SIGABRT, term_ntproc);
 
   /* determine which drives are fixed, for GetCachedVolumeInformation */
   {
@@ -5930,9 +6732,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 ();
 }
 
 /*
@@ -5948,7 +6747,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.  */
@@ -5979,6 +6778,7 @@ globals_of_w32 (void)
   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;
@@ -5998,6 +6798,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
@@ -6196,7 +6997,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
@@ -6206,13 +7007,13 @@ emacs_gnutls_pull (gnutls_transport_ptr_t p, void* buf, size_t sz)
 {
   int n, sc, err;
   SELECT_TYPE fdset;
-  EMACS_TIME timeout;
+  struct timeval timeout;
   struct Lisp_Process *process = (struct Lisp_Process *)p;
   int fd = process->infd;
 
   for (;;)
     {
-      n = sys_read(fd, (char*)buf, sz);
+      n = sys_read (fd, (char*)buf, sz);
 
       if (n >= 0)
         return n;
@@ -6222,13 +7023,14 @@ emacs_gnutls_pull (gnutls_transport_ptr_t p, void* buf, size_t sz)
       if (err == EWOULDBLOCK)
         {
           /* Set a small timeout.  */
-          EMACS_SET_SECS_USECS(timeout, 1, 0);
+         timeout.tv_sec = 1;
+         timeout.tv_usec = 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.  */
@@ -6252,7 +7054,7 @@ emacs_gnutls_push (gnutls_transport_ptr_t p, const void* buf, size_t sz)
 {
   struct Lisp_Process *process = (struct Lisp_Process *)p;
   int fd = process->outfd;
-  ssize_t n = sys_write(fd, buf, sz);
+  ssize_t n = sys_write (fd, buf, sz);
 
   /* 0 or more bytes written means everything went fine.  */
   if (n >= 0)