]> code.delx.au - gnu-emacs/blobdiff - src/w32.c
Merge from emacs-24; up to 2012-12-17T11:17:34Z!rgm@gnu.org
[gnu-emacs] / src / w32.c
index da778eb85415ff37114fb89065cdf3fce99c05b7..8b89bd3e660e5e0599baadc04cceb4439fa3d0ce 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 Windows API.
-   Copyright (C) 1994-1995, 2000-201 Free Software Foundation, Inc.
+   Copyright (C) 1994-1995, 2000-2013 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -37,7 +37,7 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 /* must include CRT headers *before* config.h */
 
 #include <config.h>
-#include <mbstring.h>  /* for _mbspbrk */
+#include <mbstring.h>  /* for _mbspbrk, _mbslwr, _mbsrchr, ... */
 
 #undef access
 #undef chdir
@@ -101,22 +101,31 @@ 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>
+#include <sddl.h>
+
+#include <sys/acl.h>
+
+/* This is not in MinGW's sddl.h (but they are in MSVC headers), so we
+   define them by hand if not already defined.  */
+#ifndef SDDL_REVISION_1
+#define SDDL_REVISION_1        1
+#endif /* SDDL_REVISION_1 */
 
 #ifdef _MSC_VER
 /* MSVC doesn't provide the definition of REPARSE_DATA_BUFFER and the
@@ -150,10 +159,18 @@ typedef struct _REPARSE_DATA_BUFFER {
     } 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
@@ -179,7 +196,7 @@ typedef struct _REPARSE_DATA_BUFFER {
 #undef sendto
 
 #include "w32.h"
-#include "ndir.h"
+#include <dirent.h>
 #include "w32common.h"
 #include "w32heap.h"
 #include "w32select.h"
@@ -249,6 +266,11 @@ 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;
+static BOOL g_b_init_get_security_descriptor_dacl;
+static BOOL g_b_init_convert_sd_to_sddl;
+static BOOL g_b_init_convert_sddl_to_sd;
+static BOOL g_b_init_is_valid_security_descriptor;
+static BOOL g_b_init_set_file_security;
 
 /*
   BEGIN: Wrapper functions around OpenProcessToken
@@ -278,9 +300,11 @@ GetProcessTimes_Proc get_process_times_fn = NULL;
 #ifdef _UNICODE
 const char * const LookupAccountSid_Name = "LookupAccountSidW";
 const char * const GetFileSecurity_Name =  "GetFileSecurityW";
+const char * const SetFileSecurity_Name =  "SetFileSecurityW";
 #else
 const char * const LookupAccountSid_Name = "LookupAccountSidA";
 const char * const GetFileSecurity_Name =  "GetFileSecurityA";
+const char * const SetFileSecurity_Name =  "SetFileSecurityA";
 #endif
 typedef BOOL (WINAPI * LookupAccountSid_Proc) (
     LPCTSTR lpSystemName,
@@ -310,6 +334,10 @@ typedef BOOL (WINAPI * GetFileSecurity_Proc) (
     PSECURITY_DESCRIPTOR pSecurityDescriptor,
     DWORD nLength,
     LPDWORD lpnLengthNeeded);
+typedef BOOL (WINAPI *SetFileSecurity_Proc) (
+    LPCTSTR lpFileName,
+    SECURITY_INFORMATION SecurityInformation,
+    PSECURITY_DESCRIPTOR pSecurityDescriptor);
 typedef BOOL (WINAPI * GetSecurityDescriptorOwner_Proc) (
     PSECURITY_DESCRIPTOR pSecurityDescriptor,
     PSID *pOwner,
@@ -318,6 +346,11 @@ typedef BOOL (WINAPI * GetSecurityDescriptorGroup_Proc) (
     PSECURITY_DESCRIPTOR pSecurityDescriptor,
     PSID *pGroup,
     LPBOOL lpbGroupDefaulted);
+typedef BOOL (WINAPI *GetSecurityDescriptorDacl_Proc) (
+    PSECURITY_DESCRIPTOR pSecurityDescriptor,
+    LPBOOL lpbDaclPresent,
+    PACL *pDacl,
+    LPBOOL lpbDaclDefaulted);
 typedef BOOL (WINAPI * IsValidSid_Proc) (
     PSID sid);
 typedef HANDLE (WINAPI * CreateToolhelp32Snapshot_Proc) (
@@ -343,8 +376,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) (
@@ -368,6 +401,18 @@ typedef BOOLEAN (WINAPI *CreateSymbolicLink_Proc) (
     LPTSTR lpSymlinkFileName,
     LPTSTR lpTargetFileName,
     DWORD  dwFlags);
+typedef BOOL (WINAPI *ConvertStringSecurityDescriptorToSecurityDescriptor_Proc) (
+    LPCTSTR StringSecurityDescriptor,
+    DWORD StringSDRevision,
+    PSECURITY_DESCRIPTOR  *SecurityDescriptor,
+    PULONG  SecurityDescriptorSize);
+typedef BOOL (WINAPI *ConvertSecurityDescriptorToStringSecurityDescriptor_Proc) (
+    PSECURITY_DESCRIPTOR  SecurityDescriptor,
+    DWORD RequestedStringSDRevision,
+    SECURITY_INFORMATION SecurityInformation,
+    LPTSTR  *StringSecurityDescriptor,
+    PULONG StringSecurityDescriptorLen);
+typedef BOOL (WINAPI *IsValidSecurityDescriptor_Proc) (PSECURITY_DESCRIPTOR);
 
   /* ** A utility function ** */
 static BOOL
@@ -613,6 +658,7 @@ get_file_security (LPCTSTR lpFileName,
   HMODULE hm_advapi32 = NULL;
   if (is_windows_9x () == TRUE)
     {
+      errno = ENOTSUP;
       return FALSE;
     }
   if (g_b_init_get_file_security == 0)
@@ -625,6 +671,7 @@ get_file_security (LPCTSTR lpFileName,
     }
   if (s_pfn_Get_File_Security == NULL)
     {
+      errno = ENOTSUP;
       return FALSE;
     }
   return (s_pfn_Get_File_Security (lpFileName, RequestedInformation,
@@ -632,6 +679,35 @@ get_file_security (LPCTSTR lpFileName,
                                   lpnLengthNeeded));
 }
 
+static BOOL WINAPI
+set_file_security (LPCTSTR lpFileName,
+                  SECURITY_INFORMATION SecurityInformation,
+                  PSECURITY_DESCRIPTOR pSecurityDescriptor)
+{
+  static SetFileSecurity_Proc s_pfn_Set_File_Security = NULL;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      errno = ENOTSUP;
+      return FALSE;
+    }
+  if (g_b_init_set_file_security == 0)
+    {
+      g_b_init_set_file_security = 1;
+      hm_advapi32 = LoadLibrary ("Advapi32.dll");
+      s_pfn_Set_File_Security =
+        (SetFileSecurity_Proc) GetProcAddress (
+            hm_advapi32, SetFileSecurity_Name);
+    }
+  if (s_pfn_Set_File_Security == NULL)
+    {
+      errno = ENOTSUP;
+      return FALSE;
+    }
+  return (s_pfn_Set_File_Security (lpFileName, SecurityInformation,
+                                  pSecurityDescriptor));
+}
+
 static BOOL WINAPI
 get_security_descriptor_owner (PSECURITY_DESCRIPTOR pSecurityDescriptor,
                               PSID *pOwner,
@@ -641,6 +717,7 @@ get_security_descriptor_owner (PSECURITY_DESCRIPTOR pSecurityDescriptor,
   HMODULE hm_advapi32 = NULL;
   if (is_windows_9x () == TRUE)
     {
+      errno = ENOTSUP;
       return FALSE;
     }
   if (g_b_init_get_security_descriptor_owner == 0)
@@ -653,6 +730,7 @@ get_security_descriptor_owner (PSECURITY_DESCRIPTOR pSecurityDescriptor,
     }
   if (s_pfn_Get_Security_Descriptor_Owner == NULL)
     {
+      errno = ENOTSUP;
       return FALSE;
     }
   return (s_pfn_Get_Security_Descriptor_Owner (pSecurityDescriptor, pOwner,
@@ -668,6 +746,7 @@ get_security_descriptor_group (PSECURITY_DESCRIPTOR pSecurityDescriptor,
   HMODULE hm_advapi32 = NULL;
   if (is_windows_9x () == TRUE)
     {
+      errno = ENOTSUP;
       return FALSE;
     }
   if (g_b_init_get_security_descriptor_group == 0)
@@ -680,12 +759,44 @@ get_security_descriptor_group (PSECURITY_DESCRIPTOR pSecurityDescriptor,
     }
   if (s_pfn_Get_Security_Descriptor_Group == NULL)
     {
+      errno = ENOTSUP;
       return FALSE;
     }
   return (s_pfn_Get_Security_Descriptor_Group (pSecurityDescriptor, pGroup,
                                               lpbGroupDefaulted));
 }
 
+static BOOL WINAPI
+get_security_descriptor_dacl (PSECURITY_DESCRIPTOR pSecurityDescriptor,
+                             LPBOOL lpbDaclPresent,
+                             PACL *pDacl,
+                             LPBOOL lpbDaclDefaulted)
+{
+  static GetSecurityDescriptorDacl_Proc s_pfn_Get_Security_Descriptor_Dacl = NULL;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      errno = ENOTSUP;
+      return FALSE;
+    }
+  if (g_b_init_get_security_descriptor_dacl == 0)
+    {
+      g_b_init_get_security_descriptor_dacl = 1;
+      hm_advapi32 = LoadLibrary ("Advapi32.dll");
+      s_pfn_Get_Security_Descriptor_Dacl =
+        (GetSecurityDescriptorDacl_Proc) GetProcAddress (
+            hm_advapi32, "GetSecurityDescriptorDacl");
+    }
+  if (s_pfn_Get_Security_Descriptor_Dacl == NULL)
+    {
+      errno = ENOTSUP;
+      return FALSE;
+    }
+  return (s_pfn_Get_Security_Descriptor_Dacl (pSecurityDescriptor,
+                                             lpbDaclPresent, pDacl,
+                                             lpbDaclDefaulted));
+}
+
 static BOOL WINAPI
 is_valid_sid (PSID sid)
 {
@@ -880,6 +991,120 @@ create_symbolic_link (LPTSTR lpSymlinkFilename,
     }
   return retval;
 }
+
+static BOOL WINAPI
+is_valid_security_descriptor (PSECURITY_DESCRIPTOR pSecurityDescriptor)
+{
+  static IsValidSecurityDescriptor_Proc s_pfn_Is_Valid_Security_Descriptor_Proc = NULL;
+
+  if (is_windows_9x () == TRUE)
+    {
+      errno = ENOTSUP;
+      return FALSE;
+    }
+
+  if (g_b_init_is_valid_security_descriptor == 0)
+    {
+      g_b_init_is_valid_security_descriptor = 1;
+      s_pfn_Is_Valid_Security_Descriptor_Proc =
+       (IsValidSecurityDescriptor_Proc)GetProcAddress (GetModuleHandle ("Advapi32.dll"),
+                                                       "IsValidSecurityDescriptor");
+    }
+  if (s_pfn_Is_Valid_Security_Descriptor_Proc == NULL)
+    {
+      errno = ENOTSUP;
+      return FALSE;
+    }
+
+  return s_pfn_Is_Valid_Security_Descriptor_Proc (pSecurityDescriptor);
+}
+
+static BOOL WINAPI
+convert_sd_to_sddl (PSECURITY_DESCRIPTOR SecurityDescriptor,
+                   DWORD RequestedStringSDRevision,
+                   SECURITY_INFORMATION SecurityInformation,
+                   LPTSTR  *StringSecurityDescriptor,
+                   PULONG StringSecurityDescriptorLen)
+{
+  static ConvertSecurityDescriptorToStringSecurityDescriptor_Proc s_pfn_Convert_SD_To_SDDL = NULL;
+  BOOL retval;
+
+  if (is_windows_9x () == TRUE)
+    {
+      errno = ENOTSUP;
+      return FALSE;
+    }
+
+  if (g_b_init_convert_sd_to_sddl == 0)
+    {
+      g_b_init_convert_sd_to_sddl = 1;
+#ifdef _UNICODE
+      s_pfn_Convert_SD_To_SDDL =
+       (ConvertSecurityDescriptorToStringSecurityDescriptor_Proc)GetProcAddress (GetModuleHandle ("Advapi32.dll"),
+                                                                                 "ConvertSecurityDescriptorToStringSecurityDescriptorW");
+#else
+      s_pfn_Convert_SD_To_SDDL =
+       (ConvertSecurityDescriptorToStringSecurityDescriptor_Proc)GetProcAddress (GetModuleHandle ("Advapi32.dll"),
+                                                                                 "ConvertSecurityDescriptorToStringSecurityDescriptorA");
+#endif
+    }
+  if (s_pfn_Convert_SD_To_SDDL == NULL)
+    {
+      errno = ENOTSUP;
+      return FALSE;
+    }
+
+  retval = s_pfn_Convert_SD_To_SDDL (SecurityDescriptor,
+                                    RequestedStringSDRevision,
+                                    SecurityInformation,
+                                    StringSecurityDescriptor,
+                                    StringSecurityDescriptorLen);
+
+  return retval;
+}
+
+static BOOL WINAPI
+convert_sddl_to_sd (LPCTSTR StringSecurityDescriptor,
+                   DWORD StringSDRevision,
+                   PSECURITY_DESCRIPTOR  *SecurityDescriptor,
+                   PULONG  SecurityDescriptorSize)
+{
+  static ConvertStringSecurityDescriptorToSecurityDescriptor_Proc s_pfn_Convert_SDDL_To_SD = NULL;
+  BOOL retval;
+
+  if (is_windows_9x () == TRUE)
+    {
+      errno = ENOTSUP;
+      return FALSE;
+    }
+
+  if (g_b_init_convert_sddl_to_sd == 0)
+    {
+      g_b_init_convert_sddl_to_sd = 1;
+#ifdef _UNICODE
+      s_pfn_Convert_SDDL_To_SD =
+       (ConvertStringSecurityDescriptorToSecurityDescriptor_Proc)GetProcAddress (GetModuleHandle ("Advapi32.dll"),
+                                                                                 "ConvertStringSecurityDescriptorToSecurityDescriptorW");
+#else
+      s_pfn_Convert_SDDL_To_SD =
+       (ConvertStringSecurityDescriptorToSecurityDescriptor_Proc)GetProcAddress (GetModuleHandle ("Advapi32.dll"),
+                                                                                 "ConvertStringSecurityDescriptorToSecurityDescriptorA");
+#endif
+    }
+  if (s_pfn_Convert_SDDL_To_SD == NULL)
+    {
+      errno = ENOTSUP;
+      return FALSE;
+    }
+
+  retval = s_pfn_Convert_SDDL_To_SD (StringSecurityDescriptor,
+                                    StringSDRevision,
+                                    SecurityDescriptor,
+                                    SecurityDescriptorSize);
+
+  return retval;
+}
+
 \f
 
 /* Return 1 if P is a valid pointer to an object of size SIZE.  Return
@@ -908,7 +1133,7 @@ static char startup_dir[MAXPATHLEN];
 
 /* Get the current working directory.  */
 char *
-getcwd (char *dir, size_t dirsize)
+getcwd (char *dir, int dirsize)
 {
   if (!dirsize)
     {
@@ -1306,35 +1531,110 @@ srandom (int seed)
   srand (seed);
 }
 
+/* Current codepage for encoding file names.  */
+static int file_name_codepage;
+
+/* Return the maximum length in bytes of a multibyte character
+   sequence encoded in the current ANSI codepage.  This is required to
+   correctly walk the encoded file names one character at a time.  */
+static int
+max_filename_mbslen (void)
+{
+  /* A simple cache to avoid calling GetCPInfo every time we need to
+     normalize a file name.  The file-name encoding is not supposed to
+     be changed too frequently, if ever.  */
+  static Lisp_Object last_file_name_encoding;
+  static int last_max_mbslen;
+  Lisp_Object current_encoding;
+
+  current_encoding = Vfile_name_coding_system;
+  if (NILP (current_encoding))
+    current_encoding = Vdefault_file_name_coding_system;
+
+  if (!EQ (last_file_name_encoding, current_encoding))
+    {
+      CPINFO cp_info;
+
+      last_file_name_encoding = current_encoding;
+      /* Default to the current ANSI codepage.  */
+      file_name_codepage = w32_ansi_code_page;
+      if (!NILP (current_encoding))
+       {
+         char *cpname = SDATA (SYMBOL_NAME (current_encoding));
+         char *cp = NULL, *end;
+         int cpnum;
+
+         if (strncmp (cpname, "cp", 2) == 0)
+           cp = cpname + 2;
+         else if (strncmp (cpname, "windows-", 8) == 0)
+           cp = cpname + 8;
+
+         if (cp)
+           {
+             end = cp;
+             cpnum = strtol (cp, &end, 10);
+             if (cpnum && *end == '\0' && end - cp >= 2)
+               file_name_codepage = cpnum;
+           }
+       }
+
+      if (!file_name_codepage)
+       file_name_codepage = CP_ACP; /* CP_ACP = 0, but let's not assume that */
+
+      if (!GetCPInfo (file_name_codepage, &cp_info))
+       {
+         file_name_codepage = CP_ACP;
+         if (!GetCPInfo (file_name_codepage, &cp_info))
+           emacs_abort ();
+       }
+      last_max_mbslen = cp_info.MaxCharSize;
+    }
+
+  return last_max_mbslen;
+}
 
 /* Normalize filename by converting all path separators to
    the specified separator.  Also conditionally convert upper
    case path name components to lower case.  */
 
 static void
-normalize_filename (register char *fp, char path_sep)
+normalize_filename (register char *fp, char path_sep, int multibyte)
 {
   char sep;
-  char *elem;
+  char *elem, *p2;
+  int dbcs_p = max_filename_mbslen () > 1;
+
+  /* Multibyte file names are in the Emacs internal representation, so
+     we can traverse them by bytes with no problems.  */
+  if (multibyte)
+    dbcs_p = 0;
 
   /* Always lower-case drive letters a-z, even if the filesystem
      preserves case in filenames.
      This is so filenames can be compared by string comparison
      functions that are case-sensitive.  Even case-preserving filesystems
      do not distinguish case in drive letters.  */
-  if (fp[1] == ':' && *fp >= 'A' && *fp <= 'Z')
+  if (dbcs_p)
+    p2 = CharNextExA (file_name_codepage, fp, 0);
+  else
+    p2 = fp + 1;
+
+  if (*p2 == ':' && *fp >= 'A' && *fp <= 'Z')
     {
       *fp += 'a' - 'A';
       fp += 2;
     }
 
-  if (NILP (Vw32_downcase_file_names))
+  if (multibyte || NILP (Vw32_downcase_file_names))
     {
       while (*fp)
        {
          if (*fp == '/' || *fp == '\\')
            *fp = path_sep;
-         fp++;
+         if (!dbcs_p)
+           fp++;
+         else
+           fp = CharNextExA (file_name_codepage, fp, 0);
        }
       return;
     }
@@ -1357,27 +1657,36 @@ normalize_filename (register char *fp, char path_sep)
        if (elem && elem != fp)
          {
            *fp = 0;            /* temporary end of string */
-           _strlwr (elem);     /* while we convert to lower case */
+           _mbslwr (elem);     /* while we convert to lower case */
          }
        *fp = sep;              /* convert (or restore) path separator */
        elem = fp + 1;          /* next element starts after separator */
        sep = path_sep;
       }
-  } while (*fp++);
+    if (*fp)
+      {
+       if (!dbcs_p)
+         fp++;
+       else
+         fp = CharNextExA (file_name_codepage, fp, 0);
+      }
+  } while (*fp);
 }
 
-/* Destructively turn backslashes into slashes.  */
+/* Destructively turn backslashes into slashes.  MULTIBYTE non-zero
+   means the file name is a multibyte string in Emacs's internal
+   representation.  */
 void
-dostounix_filename (register char *p)
+dostounix_filename (register char *p, int multibyte)
 {
-  normalize_filename (p, '/');
+  normalize_filename (p, '/', multibyte);
 }
 
 /* Destructively turn slashes into backslashes.  */
 void
 unixtodos_filename (register char *p)
 {
-  normalize_filename (p, '\\');
+  normalize_filename (p, '\\', 0);
 }
 
 /* Remove all CR's that are followed by a LF.
@@ -1428,12 +1737,17 @@ parse_root (char * name, char ** pPath)
   else if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1]))
     {
       int slashes = 2;
+      int dbcs_p = max_filename_mbslen () > 1;
+
       name += 2;
       do
         {
          if (IS_DIRECTORY_SEP (*name) && --slashes == 0)
            break;
-         name++;
+         if (dbcs_p)
+           name = CharNextExA (file_name_codepage, name, 0);
+         else
+           name++;
        }
       while ( *name );
       if (IS_DIRECTORY_SEP (name[0]))
@@ -1498,7 +1812,7 @@ w32_get_long_filename (char * name, char * buf, int size)
   while (p != NULL && *p)
     {
       q = p;
-      p = strchr (q, '\\');
+      p = _mbschr (q, '\\');
       if (p) *p = '\0';
       len = get_long_basename (full, o, size);
       if (len > 0)
@@ -1536,6 +1850,51 @@ is_unc_volume (const char *filename)
   return 1;
 }
 
+/* Emulate the Posix unsetenv.  */
+int
+unsetenv (const char *name)
+{
+  char *var;
+  size_t name_len;
+  int retval;
+
+  if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+  name_len = strlen (name);
+  /* MS docs says an environment variable cannot be longer than 32K.  */
+  if (name_len > 32767)
+    {
+      errno = ENOMEM;
+      return 0;
+    }
+  /* It is safe to use 'alloca' with 32K size, since the stack is at
+     least 2MB, and we set it to 8MB in the link command line.  */
+  var = alloca (name_len + 2);
+  strncpy (var, name, name_len);
+  var[name_len++] = '=';
+  var[name_len] = '\0';
+  return _putenv (var);
+}
+
+/* MS _putenv doesn't support removing a variable when the argument
+   does not include the '=' character, so we fix that here.  */
+int
+sys_putenv (char *str)
+{
+  const char *const name_end = strchr (str, '=');
+
+  if (name_end == NULL)
+    {
+      /* Remove the variable from the environment.  */
+      return unsetenv (str);
+    }
+
+  return _putenv (str);
+}
+
 #define REG_ROOT "SOFTWARE\\GNU\\Emacs"
 
 LPBYTE
@@ -1725,16 +2084,16 @@ init_environment (char ** argv)
 
       if (!GetModuleFileName (NULL, modname, MAX_PATH))
        emacs_abort ();
-      if ((p = strrchr (modname, '\\')) == NULL)
+      if ((p = _mbsrchr (modname, '\\')) == NULL)
        emacs_abort ();
       *p = 0;
 
-      if ((p = strrchr (modname, '\\')) && xstrcasecmp (p, "\\bin") == 0)
+      if ((p = _mbsrchr (modname, '\\')) && xstrcasecmp (p, "\\bin") == 0)
        {
          char buf[SET_ENV_BUF_SIZE];
 
          *p = 0;
-         for (p = modname; *p; p++)
+         for (p = modname; *p; p = CharNext (p))
            if (*p == '\\') *p = '/';
 
          _snprintf (buf, sizeof (buf)-1, "emacs_dir=%s", modname);
@@ -1749,17 +2108,17 @@ init_environment (char ** argv)
                      || xstrcasecmp (p, "\\AMD64") == 0))
        {
          *p = 0;
-         p = strrchr (modname, '\\');
+         p = _mbsrchr (modname, '\\');
          if (p != NULL)
            {
              *p = 0;
-             p = strrchr (modname, '\\');
+             p = _mbsrchr (modname, '\\');
              if (p && xstrcasecmp (p, "\\src") == 0)
                {
                  char buf[SET_ENV_BUF_SIZE];
 
                  *p = 0;
-                 for (p = modname; *p; p++)
+                 for (p = modname; *p; p = CharNext (p))
                    if (*p == '\\') *p = '/';
 
                  _snprintf (buf, sizeof (buf)-1, "emacs_dir=%s", modname);
@@ -1870,7 +2229,7 @@ emacs_root_dir (void)
     emacs_abort ();
   strcpy (root_dir, p);
   root_dir[parse_root (root_dir, NULL)] = '\0';
-  dostounix_filename (root_dir);
+  dostounix_filename (root_dir, 0);
   return root_dir;
 }
 
@@ -2294,12 +2653,23 @@ get_volume_info (const char * name, const char ** pPath)
     {
       char *str = temp;
       int slashes = 4;
+      int dbcs_p = max_filename_mbslen () > 1;
+
       rootname = temp;
       do
         {
          if (IS_DIRECTORY_SEP (*name) && --slashes == 0)
            break;
-         *str++ = *name++;
+         if (!dbcs_p)
+           *str++ = *name++;
+         else
+           {
+             const char *p = name;
+
+             name = CharNextExA (file_name_codepage, name, 0);
+             memcpy (str, p, name - p);
+             str += name - p;
+           }
        }
       while ( *name );
 
@@ -2448,7 +2818,7 @@ is_exec (const char * name)
    and readdir.  We can't use the procedures supplied in sysdep.c,
    so we provide them here.  */
 
-struct direct dir_static;       /* simulated directory contents */
+struct dirent dir_static;       /* simulated directory contents */
 static HANDLE dir_find_handle = INVALID_HANDLE_VALUE;
 static int    dir_is_fat;
 static char   dir_pathname[MAXPATHLEN+1];
@@ -2462,7 +2832,7 @@ static char  *read_unc_volume (HANDLE, char *, int);
 static void   close_unc_volume (HANDLE);
 
 DIR *
-opendir (char *filename)
+opendir (const char *filename)
 {
   DIR *dirp;
 
@@ -2518,7 +2888,7 @@ closedir (DIR *dirp)
   xfree ((char *) dirp);
 }
 
-struct direct *
+struct dirent *
 readdir (DIR *dirp)
 {
   int downcase = !NILP (Vw32_downcase_file_names);
@@ -2535,11 +2905,23 @@ readdir (DIR *dirp)
     {
       char filename[MAXNAMLEN + 3];
       int ln;
+      int dbcs_p = max_filename_mbslen () > 1;
 
       strcpy (filename, dir_pathname);
       ln = strlen (filename) - 1;
-      if (!IS_DIRECTORY_SEP (filename[ln]))
-       strcat (filename, "\\");
+      if (!dbcs_p)
+       {
+         if (!IS_DIRECTORY_SEP (filename[ln]))
+           strcat (filename, "\\");
+       }
+      else
+       {
+         char *end = filename + ln + 1;
+         char *last_char = CharPrevExA (file_name_codepage, filename, end, 0);
+
+         if (!IS_DIRECTORY_SEP (*last_char))
+           strcat (filename, "\\");
+       }
       strcat (filename, "*");
 
       /* Note: No need to resolve symlinks in FILENAME, because
@@ -2572,7 +2954,7 @@ readdir (DIR *dirp)
       downcase = 1;    /* 8+3 aliases are returned in all caps */
     }
   dir_static.d_namlen = strlen (dir_static.d_name);
-  dir_static.d_reclen = sizeof (struct direct) - MAXNAMLEN + 3 +
+  dir_static.d_reclen = sizeof (struct dirent) - MAXNAMLEN + 3 +
     dir_static.d_namlen - dir_static.d_namlen % 4;
 
   /* If the file name in cFileName[] includes `?' characters, it means
@@ -2590,15 +2972,22 @@ readdir (DIR *dirp)
     strcpy (dir_static.d_name, dir_find_data.cFileName);
   dir_static.d_namlen = strlen (dir_static.d_name);
   if (dir_is_fat)
-    _strlwr (dir_static.d_name);
+    _mbslwr (dir_static.d_name);
   else if (downcase)
     {
       register char *p;
-      for (p = dir_static.d_name; *p; p++)
-       if (*p >= 'a' && *p <= 'z')
-         break;
+      int dbcs_p = max_filename_mbslen () > 1;
+      for (p = dir_static.d_name; *p; )
+       {
+         if (*p >= 'a' && *p <= 'z')
+           break;
+         if (dbcs_p)
+           p = CharNextExA (file_name_codepage, p, 0);
+         else
+           p++;
+       }
       if (!*p)
-       _strlwr (dir_static.d_name);
+       _mbslwr (dir_static.d_name);
     }
 
   return &dir_static;
@@ -2637,6 +3026,7 @@ read_unc_volume (HANDLE henum, char *readbuf, int size)
   DWORD bufsize = 512;
   char *buffer;
   char *ptr;
+  int dbcs_p = max_filename_mbslen () > 1;
 
   count = 1;
   buffer = alloca (bufsize);
@@ -2647,7 +3037,13 @@ read_unc_volume (HANDLE henum, char *readbuf, int size)
   /* WNetEnumResource returns \\resource\share...skip forward to "share". */
   ptr = ((LPNETRESOURCE) buffer)->lpRemoteName;
   ptr += 2;
-  while (*ptr && !IS_DIRECTORY_SEP (*ptr)) ptr++;
+  if (!dbcs_p)
+    while (*ptr && !IS_DIRECTORY_SEP (*ptr)) ptr++;
+  else
+    {
+      while (*ptr && !IS_DIRECTORY_SEP (*ptr))
+       ptr = CharNextExA (file_name_codepage, ptr, 0);
+    }
   ptr++;
 
   strncpy (readbuf, ptr, size);
@@ -2684,9 +3080,11 @@ logon_network_drive (const char *path)
 {
   NETRESOURCE resource;
   char share[MAX_PATH];
-  int i, n_slashes;
+  int n_slashes;
   char drive[4];
   UINT drvtype;
+  char *p;
+  int dbcs_p;
 
   if (IS_DIRECTORY_SEP (path[0]) && IS_DIRECTORY_SEP (path[1]))
     drvtype = DRIVE_REMOTE;
@@ -2708,13 +3106,18 @@ logon_network_drive (const char *path)
   n_slashes = 2;
   strncpy (share, path, MAX_PATH);
   /* Truncate to just server and share name.  */
-  for (i = 2; i < MAX_PATH; i++)
+  dbcs_p = max_filename_mbslen () > 1;
+  for (p = share + 2; *p && p < share + MAX_PATH; )
     {
-      if (IS_DIRECTORY_SEP (share[i]) && ++n_slashes > 3)
+      if (IS_DIRECTORY_SEP (*p) && ++n_slashes > 3)
         {
-          share[i] = '\0';
+          *p = '\0';
           break;
         }
+      if (dbcs_p)
+       p = CharNextExA (file_name_codepage, p, 0);
+      else
+       p++;
     }
 
   resource.dwType = RESOURCETYPE_DISK;
@@ -3011,9 +3414,12 @@ sys_open (const char * path, int oflag, int mode)
      and system files. Force all file handles to be
      non-inheritable. */
   int res = _open (mpath, (oflag & ~_O_CREAT) | _O_NOINHERIT, mode);
-  if (res >= 0)
-    return res;
-  return _open (mpath, oflag | _O_NOINHERIT, mode);
+  if (res < 0)
+    res = _open (mpath, oflag | _O_NOINHERIT, mode);
+  if (res >= 0 && res < MAXDESC)
+    fd_info[res].flags = 0;
+
+  return res;
 }
 
 int
@@ -3375,18 +3781,15 @@ w32_add_to_cache (PSID sid, unsigned id, char *name)
 #define GID 2
 
 static int
-get_name_and_id (PSECURITY_DESCRIPTOR psd, const char *fname,
-                unsigned *id, char *nm, int what)
+get_name_and_id (PSECURITY_DESCRIPTOR psd, unsigned *id, char *nm, int what)
 {
   PSID sid = NULL;
-  char machine[MAX_COMPUTERNAME_LENGTH+1];
   BOOL dflt;
   SID_NAME_USE ignore;
   char name[UNLEN+1];
   DWORD name_len = sizeof (name);
   char domain[1024];
   DWORD domain_len = sizeof (domain);
-  char *mp = NULL;
   int use_dflt = 0;
   int result;
 
@@ -3401,22 +3804,7 @@ get_name_and_id (PSECURITY_DESCRIPTOR psd, const char *fname,
     use_dflt = 1;
   else if (!w32_cached_id (sid, id, nm))
     {
-      /* If FNAME is a UNC, we need to lookup account on the
-        specified machine.  */
-      if (IS_DIRECTORY_SEP (fname[0]) && IS_DIRECTORY_SEP (fname[1])
-         && fname[2] != '\0')
-       {
-         const char *s;
-         char *p;
-
-         for (s = fname + 2, p = machine;
-              *s && !IS_DIRECTORY_SEP (*s); s++, p++)
-           *p = *s;
-         *p = '\0';
-         mp = machine;
-       }
-
-      if (!lookup_account_sid (mp, sid, name, &name_len,
+      if (!lookup_account_sid (NULL, sid, name, &name_len,
                               domain, &domain_len, &ignore)
          || name_len > UNLEN+1)
        use_dflt = 1;
@@ -3431,9 +3819,7 @@ get_name_and_id (PSECURITY_DESCRIPTOR psd, const char *fname,
 }
 
 static void
-get_file_owner_and_group (PSECURITY_DESCRIPTOR psd,
-                         const char *fname,
-                         struct stat *st)
+get_file_owner_and_group (PSECURITY_DESCRIPTOR psd, struct stat *st)
 {
   int dflt_usr = 0, dflt_grp = 0;
 
@@ -3444,9 +3830,9 @@ get_file_owner_and_group (PSECURITY_DESCRIPTOR psd,
     }
   else
     {
-      if (get_name_and_id (psd, fname, &st->st_uid, st->st_uname, UID))
+      if (get_name_and_id (psd, &st->st_uid, st->st_uname, UID))
        dflt_usr = 1;
-      if (get_name_and_id (psd, fname, &st->st_gid, st->st_gname, GID))
+      if (get_name_and_id (psd, &st->st_gid, st->st_gname, GID))
        dflt_grp = 1;
     }
   /* Consider files to belong to current user/group, if we cannot get
@@ -3485,6 +3871,10 @@ is_slow_fs (const char *name)
   return !(devtype == DRIVE_FIXED || devtype == DRIVE_RAMDISK);
 }
 
+/* If this is non-zero, the caller wants accurate information about
+   file's owner and group, which could be expensive to get.  */
+int w32_stat_get_owner_group;
+
 /* 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 and owner/group without hacks in the main Emacs code. */
@@ -3505,6 +3895,7 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks)
   DWORD access_rights = 0;
   DWORD fattrs = 0, serialnum = 0, fs_high = 0, fs_low = 0, nlinks = 1;
   FILETIME ctime, atime, wtime;
+  int dbcs_p;
 
   if (path == NULL || buf == NULL)
     {
@@ -3656,6 +4047,7 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks)
       /* We produce the fallback owner and group data, based on the
         current user that runs Emacs, in the following cases:
 
+         . caller didn't request owner and group info
          . this is Windows 9X
          . getting security by handle failed, and we need to produce
            information for the target of a symlink (this is better
@@ -3664,23 +4056,25 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks)
 
         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))
+      if (!w32_stat_get_owner_group || is_windows_9x () == TRUE)
+       get_file_owner_and_group (NULL, buf);
+      else
        {
-         psd = get_file_security_desc_by_name (name);
-         get_file_owner_and_group (psd, name, buf);
-         xfree (psd);
+         psd = get_file_security_desc_by_handle (fh);
+         if (psd)
+           {
+             get_file_owner_and_group (psd, buf);
+             LocalFree (psd);
+           }
+         else if (!(is_a_symlink && follow_symlinks))
+           {
+             psd = get_file_security_desc_by_name (name);
+             get_file_owner_and_group (psd, buf);
+             xfree (psd);
+           }
+         else
+           get_file_owner_and_group (NULL, buf);
        }
-      else
-       get_file_owner_and_group (NULL, name, buf);
       CloseHandle (fh);
     }
   else
@@ -3699,6 +4093,7 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks)
         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.  */
+      dbcs_p = max_filename_mbslen () > 1;
       rootdir = (path >= save_name + len - 1
                 && (IS_DIRECTORY_SEP (*path) || *path == 0));
 
@@ -3726,8 +4121,19 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks)
        }
       else if (rootdir)
        {
-         if (!IS_DIRECTORY_SEP (name[len-1]))
-           strcat (name, "\\");
+         if (!dbcs_p)
+           {
+             if (!IS_DIRECTORY_SEP (name[len-1]))
+               strcat (name, "\\");
+           }
+         else
+           {
+             char *end = name + len;
+             char *n = CharPrevExA (file_name_codepage, name, end, 0);
+
+             if (!IS_DIRECTORY_SEP (*n))
+               strcat (name, "\\");
+           }
          if (GetDriveType (name) < 2)
            {
              errno = ENOENT;
@@ -3739,15 +4145,37 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks)
        }
       else
        {
-         if (IS_DIRECTORY_SEP (name[len-1]))
-           name[len - 1] = 0;
+         if (!dbcs_p)
+           {
+             if (IS_DIRECTORY_SEP (name[len-1]))
+               name[len - 1] = 0;
+           }
+         else
+           {
+             char *end = name + len;
+             char *n = CharPrevExA (file_name_codepage, name, end, 0);
+
+             if (IS_DIRECTORY_SEP (*n))
+               *n = 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 (!dbcs_p)
+           {
+             if (IS_DIRECTORY_SEP (dir_pathname[len-1]))
+               len--;
+           }
+         else
+           {
+             char *end = dir_pathname + len;
+             char *n = CharPrevExA (file_name_codepage, dir_pathname, end, 0);
+
+             if (IS_DIRECTORY_SEP (*n))
+               len--;
+           }
          if (dir_find_handle != INVALID_HANDLE_VALUE
              && !(is_a_symlink && follow_symlinks)
              && strnicmp (save_name, dir_pathname, len) == 0
@@ -3788,7 +4216,7 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks)
       else
        buf->st_mode = S_IFREG;
 
-      get_file_owner_and_group (NULL, name, buf);
+      get_file_owner_and_group (NULL, buf);
     }
 
 #if 0
@@ -3853,6 +4281,30 @@ lstat (const char * path, struct stat * buf)
   return stat_worker (path, buf, 0);
 }
 
+int
+fstatat (int fd, char const *name, struct stat *st, int flags)
+{
+  /* Rely on a hack: an open directory is modeled as file descriptor 0.
+     This is good enough for the current usage in Emacs, but is fragile.
+
+     FIXME: Add proper support for fdopendir, fstatat, readlinkat.
+     Gnulib does this and can serve as a model.  */
+  char fullname[MAX_PATH];
+
+  if (fd != AT_FDCWD)
+    {
+      if (_snprintf (fullname, sizeof fullname, "%s/%s", dir_pathname, name)
+         < 0)
+       {
+         errno = ENAMETOOLONG;
+         return -1;
+       }
+      name = fullname;
+    }
+
+  return stat_worker (name, st, ! (flags & AT_SYMLINK_NOFOLLOW));
+}
+
 /* Provide fstat and utime as well as stat for consistent handling of
    file timestamps. */
 int
@@ -3907,13 +4359,23 @@ fstat (int desc, struct stat * buf)
   else
     buf->st_ino = fake_inode;
 
-  /* Consider files to belong to current user.
-     FIXME: this should use GetSecurityInfo API, but it is only
-     available for _WIN32_WINNT >= 0x501.  */
-  buf->st_uid = dflt_passwd.pw_uid;
-  buf->st_gid = dflt_passwd.pw_gid;
-  strcpy (buf->st_uname, dflt_passwd.pw_name);
-  strcpy (buf->st_gname, dflt_group.gr_name);
+  /* If the caller so requested, get the true file owner and group.
+     Otherwise, consider the file to belong to the current user.  */
+  if (!w32_stat_get_owner_group || is_windows_9x () == TRUE)
+    get_file_owner_and_group (NULL, buf);
+  else
+    {
+      PSECURITY_DESCRIPTOR psd = NULL;
+
+      psd = get_file_security_desc_by_handle (fh);
+      if (psd)
+       {
+         get_file_owner_and_group (psd, buf);
+         LocalFree (psd);
+       }
+      else
+       get_file_owner_and_group (NULL, buf);
+    }
 
   buf->st_dev = info.dwVolumeSerialNumber;
   buf->st_rdev = info.dwVolumeSerialNumber;
@@ -4008,6 +4470,7 @@ symlink (char const *filename, char const *linkname)
   char linkfn[MAX_PATH], *tgtfn;
   DWORD flags = 0;
   int dir_access, filename_ends_in_slash;
+  int dbcs_p;
 
   /* Diagnostics follows Posix as much as possible.  */
   if (filename == NULL || linkname == NULL)
@@ -4033,6 +4496,8 @@ symlink (char const *filename, char const *linkname)
       return -1;
     }
 
+  dbcs_p = max_filename_mbslen () > 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])))
@@ -4047,8 +4512,21 @@ symlink (char const *filename, char const *linkname)
       char tem[MAX_PATH];
       char *p = linkfn + strlen (linkfn);
 
-      while (p > linkfn && !IS_ANY_SEP (p[-1]))
-       p--;
+      if (!dbcs_p)
+       {
+         while (p > linkfn && !IS_ANY_SEP (p[-1]))
+           p--;
+       }
+      else
+       {
+         char *p1 = CharPrevExA (file_name_codepage, linkfn, p, 0);
+
+         while (p > linkfn && !IS_ANY_SEP (*p1))
+           {
+             p = p1;
+             p1 = CharPrevExA (file_name_codepage, linkfn, p1, 0);
+           }
+       }
       if (p > linkfn)
        strncpy (tem, linkfn, p - linkfn);
       tem[p - linkfn] = '\0';
@@ -4063,7 +4541,15 @@ symlink (char const *filename, char const *linkname)
      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 (!dbcs_p)
+    filename_ends_in_slash = IS_DIRECTORY_SEP (filename[strlen (filename) - 1]);
+  else
+    {
+      const char *end = filename + strlen (filename);
+      const char *n = CharPrevExA (file_name_codepage, filename, end, 0);
+
+      filename_ends_in_slash = IS_DIRECTORY_SEP (*n);
+    }
   if (dir_access == 0 || filename_ends_in_slash)
     flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
 
@@ -4242,7 +4728,7 @@ readlink (const char *name, char *buf, size_t buf_size)
        errno = EINVAL;
       else
        {
-         /* Copy the link target name, in wide characters, fro
+         /* Copy the link target name, in wide characters, from
             reparse_data, then convert it to multibyte encoding in
             the current locale's codepage.  */
          WCHAR *lwname;
@@ -4253,6 +4739,8 @@ readlink (const char *name, char *buf, size_t buf_size)
          WCHAR *lwname_src =
            reparse_data->SymbolicLinkReparseBuffer.PathBuffer
            + reparse_data->SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(WCHAR);
+         /* This updates file_name_codepage which we need below.  */
+         int dbcs_p = max_filename_mbslen () > 1;
 
          /* According to MSDN, PrintNameLength does not include the
             terminating null character.  */
@@ -4260,9 +4748,7 @@ readlink (const char *name, char *buf, size_t buf_size)
          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_len = WideCharToMultiByte (file_name_codepage, 0, lwname, -1,
                                           lname, MAX_PATH, NULL, NULL);
          if (!lname_len)
            {
@@ -4288,18 +4774,33 @@ readlink (const char *name, char *buf, size_t buf_size)
          else
            {
              size_t size_to_copy = buf_size;
-             BYTE *p = lname;
+             BYTE *p = lname, *p2;
              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';
+             if (dbcs_p)
+               p2 = CharNextExA (file_name_codepage, p, 0);
+             else
+               p2 = p + 1;
+             if (*p && *p2 == ':' && *p >= 'A' && *p <= 'Z')
+               {
+                 *p += 'a' - 'A';
+                 p += 2;
+               }
              while (p <= pend)
                {
                  if (*p == '\\')
                    *p = '/';
-                 ++p;
+                 if (dbcs_p)
+                   {
+                     p = CharNextExA (file_name_codepage, p, 0);
+                     /* CharNextExA doesn't advance at null character.  */
+                     if (!*p)
+                       break;
+                   }
+                 else
+                   ++p;
                }
              /* Testing for null-terminated LNAME is paranoia:
                 WideCharToMultiByte should always return a
@@ -4346,6 +4847,28 @@ readlink (const char *name, char *buf, size_t buf_size)
   return retval;
 }
 
+ssize_t
+readlinkat (int fd, char const *name, char *buffer,
+           size_t buffer_size)
+{
+  /* Rely on a hack: an open directory is modeled as file descriptor 0,
+     as in fstatat.  FIXME: Add proper support for readlinkat.  */
+  char fullname[MAX_PATH];
+
+  if (fd != AT_FDCWD)
+    {
+      if (_snprintf (fullname, sizeof fullname, "%s/%s", dir_pathname, name)
+         < 0)
+       {
+         errno = ENAMETOOLONG;
+         return -1;
+       }
+      name = fullname;
+    }
+
+  return readlink (name, buffer, buffer_size);
+}
+
 /* If FILE is a symlink, return its target (stored in a static
    buffer); otherwise return FILE.
 
@@ -4373,6 +4896,7 @@ chase_symlinks (const char *file)
   char link[MAX_PATH];
   ssize_t res, link_len;
   int loop_count = 0;
+  int dbcs_p;
 
   if (is_windows_9x () == TRUE || !is_symlink (file))
     return (char *)file;
@@ -4380,13 +4904,27 @@ chase_symlinks (const char *file)
   if ((link_len = GetFullPathName (file, MAX_PATH, link, NULL)) == 0)
     return (char *)file;
 
+  dbcs_p = max_filename_mbslen () > 1;
   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';
+    if (!dbcs_p)
+      {
+       while (link_len > 3 && IS_DIRECTORY_SEP (link[link_len-1]))
+         link[link_len--] = '\0';
+      }
+    else if (link_len > 3)
+      {
+       char *n = CharPrevExA (file_name_codepage, link, link + link_len, 0);
+
+       while (n >= link + 2 && IS_DIRECTORY_SEP (*n))
+         {
+           n[1] = '\0';
+           n = CharPrevExA (file_name_codepage, link, n, 0);
+         }
+      }
 
     res = readlink (link, target, MAX_PATH);
     if (res > 0)
@@ -4399,8 +4937,21 @@ chase_symlinks (const char *file)
               the symlink, then copy the result back to target.  */
            char *p = link + link_len;
 
-           while (p > link && !IS_ANY_SEP (p[-1]))
-             p--;
+           if (!dbcs_p)
+             {
+               while (p > link && !IS_ANY_SEP (p[-1]))
+                 p--;
+             }
+           else
+             {
+               char *p1 = CharPrevExA (file_name_codepage, link, p, 0);
+
+               while (p > link && !IS_ANY_SEP (*p1))
+                 {
+                   p = p1;
+                   p1 = CharPrevExA (file_name_codepage, link, p1, 0);
+                 }
+             }
            strcpy (p, target);
            strcpy (target, link);
          }
@@ -4418,6 +4969,245 @@ chase_symlinks (const char *file)
   return target;
 }
 
+\f
+/* Posix ACL emulation.  */
+
+int
+acl_valid (acl_t acl)
+{
+  return is_valid_security_descriptor ((PSECURITY_DESCRIPTOR)acl) ? 0 : -1;
+}
+
+char *
+acl_to_text (acl_t acl, ssize_t *size)
+{
+  LPTSTR str_acl;
+  SECURITY_INFORMATION flags =
+    OWNER_SECURITY_INFORMATION |
+    GROUP_SECURITY_INFORMATION |
+    DACL_SECURITY_INFORMATION;
+  char *retval = NULL;
+  ULONG local_size;
+  int e = errno;
+
+  errno = 0;
+
+  if (convert_sd_to_sddl ((PSECURITY_DESCRIPTOR)acl, SDDL_REVISION_1, flags, &str_acl, &local_size))
+    {
+      errno = e;
+      /* We don't want to mix heaps, so we duplicate the string in our
+        heap and free the one allocated by the API.  */
+      retval = xstrdup (str_acl);
+      if (size)
+       *size = local_size;
+      LocalFree (str_acl);
+    }
+  else if (errno != ENOTSUP)
+    errno = EINVAL;
+
+  return retval;
+}
+
+acl_t
+acl_from_text (const char *acl_str)
+{
+  PSECURITY_DESCRIPTOR psd, retval = NULL;
+  ULONG sd_size;
+  int e = errno;
+
+  errno = 0;
+
+  if (convert_sddl_to_sd (acl_str, SDDL_REVISION_1, &psd, &sd_size))
+    {
+      errno = e;
+      retval = xmalloc (sd_size);
+      memcpy (retval, psd, sd_size);
+      LocalFree (psd);
+    }
+  else if (errno != ENOTSUP)
+    errno = EINVAL;
+
+  return retval;
+}
+
+int
+acl_free (void *ptr)
+{
+  xfree (ptr);
+  return 0;
+}
+
+acl_t
+acl_get_file (const char *fname, acl_type_t type)
+{
+  PSECURITY_DESCRIPTOR psd = NULL;
+  const char *filename;
+
+  if (type == ACL_TYPE_ACCESS)
+    {
+      DWORD sd_len, err;
+      SECURITY_INFORMATION si =
+       OWNER_SECURITY_INFORMATION |
+       GROUP_SECURITY_INFORMATION |
+       DACL_SECURITY_INFORMATION ;
+      int e = errno;
+
+      filename = map_w32_filename (fname, NULL);
+      if ((volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) != 0)
+       fname = chase_symlinks (filename);
+      else
+       fname = filename;
+
+      errno = 0;
+      if (!get_file_security (fname, si, psd, 0, &sd_len)
+         && errno != ENOTSUP)
+       {
+         err = GetLastError ();
+         if (err == ERROR_INSUFFICIENT_BUFFER)
+           {
+             psd = xmalloc (sd_len);
+             if (!get_file_security (fname, si, psd, sd_len, &sd_len))
+               {
+                 xfree (psd);
+                 errno = EIO;
+                 psd = NULL;
+               }
+           }
+         else if (err == ERROR_FILE_NOT_FOUND
+                  || err == ERROR_PATH_NOT_FOUND)
+           errno = ENOENT;
+         else
+           errno = EIO;
+       }
+      else if (!errno)
+       errno = e;
+    }
+  else if (type != ACL_TYPE_DEFAULT)
+    errno = EINVAL;
+
+  return psd;
+}
+
+int
+acl_set_file (const char *fname, acl_type_t type, acl_t acl)
+{
+  TOKEN_PRIVILEGES old1, old2;
+  DWORD err;
+  int st = 0, retval = -1;
+  SECURITY_INFORMATION flags = 0;
+  PSID psid;
+  PACL pacl;
+  BOOL dflt;
+  BOOL dacl_present;
+  int e;
+  const char *filename;
+
+  if (acl_valid (acl) != 0
+      || (type != ACL_TYPE_DEFAULT && type != ACL_TYPE_ACCESS))
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  if (type == ACL_TYPE_DEFAULT)
+    {
+      errno = ENOSYS;
+      return -1;
+    }
+
+  filename = map_w32_filename (fname, NULL);
+  if ((volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) != 0)
+    fname = chase_symlinks (filename);
+  else
+    fname = filename;
+
+  if (get_security_descriptor_owner ((PSECURITY_DESCRIPTOR)acl, &psid, &dflt)
+      && psid)
+    flags |= OWNER_SECURITY_INFORMATION;
+  if (get_security_descriptor_group ((PSECURITY_DESCRIPTOR)acl, &psid, &dflt)
+      && psid)
+    flags |= GROUP_SECURITY_INFORMATION;
+  if (get_security_descriptor_dacl ((PSECURITY_DESCRIPTOR)acl, &dacl_present,
+                                   &pacl, &dflt)
+      && dacl_present)
+    flags |= DACL_SECURITY_INFORMATION;
+  if (!flags)
+    return 0;
+
+  /* According to KB-245153, setting the owner will succeed if either:
+     (1) the caller is the user who will be the new owner, and has the
+         SE_TAKE_OWNERSHIP privilege, or
+     (2) the caller has the SE_RESTORE privilege, in which case she can
+         set any valid user or group as the owner
+
+     We request below both SE_TAKE_OWNERSHIP and SE_RESTORE
+     privileges, and disregard any failures in obtaining them.  If
+     these privileges cannot be obtained, and do not already exist in
+     the calling thread's security token, this function could fail
+     with EPERM.  */
+  if (enable_privilege (SE_TAKE_OWNERSHIP_NAME, TRUE, &old1))
+    st++;
+  if (enable_privilege (SE_RESTORE_NAME, TRUE, &old2))
+    st++;
+
+  e = errno;
+  errno = 0;
+  if (!set_file_security ((char *)fname, flags, (PSECURITY_DESCRIPTOR)acl))
+    {
+      err = GetLastError ();
+
+      if (errno == ENOTSUP)
+       ;
+      else if (err == ERROR_INVALID_OWNER
+              || err == ERROR_NOT_ALL_ASSIGNED
+              || err == ERROR_ACCESS_DENIED)
+       {
+         /* Maybe the requested ACL and the one the file already has
+            are identical, in which case we can silently ignore the
+            failure.  (And no, Windows doesn't.)  */
+         acl_t current_acl = acl_get_file (fname, ACL_TYPE_ACCESS);
+
+         errno = EPERM;
+         if (current_acl)
+           {
+             char *acl_from = acl_to_text (current_acl, NULL);
+             char *acl_to = acl_to_text (acl, NULL);
+
+             if (acl_from && acl_to && xstrcasecmp (acl_from, acl_to) == 0)
+               {
+                 retval = 0;
+                 errno = e;
+               }
+             if (acl_from)
+               acl_free (acl_from);
+             if (acl_to)
+               acl_free (acl_to);
+             acl_free (current_acl);
+           }
+       }
+      else if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND)
+       errno = ENOENT;
+      else
+       errno = EACCES;
+    }
+  else
+    {
+      retval = 0;
+      errno = e;
+    }
+
+  if (st)
+    {
+      if (st >= 2)
+       restore_privilege (&old2);
+      restore_privilege (&old1);
+      revert_to_self ();
+    }
+
+  return retval;
+}
+
+\f
 /* 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
@@ -4431,12 +5221,6 @@ careadlinkat (int fd, char const *filename,
   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)
@@ -4454,14 +5238,6 @@ careadlinkat (int fd, char const *filename,
   return NULL;
 }
 
-ssize_t
-careadlinkatcwd (int fd, char const *filename, char *buffer,
-                 size_t buffer_size)
-{
-  (void) fd;
-  return readlink (filename, buffer, buffer_size);
-}
-
 \f
 /* Support for browsing other processes and their attributes.  See
    process.c for the Lisp bindings.  */
@@ -4633,8 +5409,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;
@@ -4879,7 +5655,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;
@@ -5107,7 +5883,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)),
@@ -5122,7 +5898,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)),
@@ -5934,20 +6710,34 @@ sys_close (int fd)
 
                  winsock_inuse--; /* count open sockets */
                }
-             delete_child (cp);
+             /* If the process handle is NULL, it's either a socket
+                or serial connection, or a subprocess that was
+                already reaped by reap_subprocess, but whose
+                resources were not yet freed, because its output was
+                not fully read yet by the time it was reaped.  (This
+                usually happens with async subprocesses whose output
+                is being read by Emacs.)  Otherwise, this process was
+                not reaped yet, so we set its FD to a negative value
+                to make sure sys_select will eventually get to
+                calling the SIGCHLD handler for it, which will then
+                invoke waitpid and reap_subprocess.  */
+             if (cp->procinfo.hProcess == NULL)
+               delete_child (cp);
+             else
+               cp->fd = -1;
            }
        }
     }
 
+  if (fd >= 0 && fd < MAXDESC)
+    fd_info[fd].flags = 0;
+
   /* Note that sockets do not need special treatment here (at least on
      NT and Windows 95 using the standard tcp/ip stacks) - it appears that
      closesocket is equivalent to CloseHandle, which is to be expected
      because socket handles are fully fledged kernel handles. */
   rc = _close (fd);
 
-  if (rc == 0 && fd < MAXDESC)
-    fd_info[fd].flags = 0;
-
   return rc;
 }
 
@@ -6026,7 +6816,8 @@ sys_pipe (int * phandles)
 }
 
 /* Function to do blocking read of one byte, needed to implement
-   select.  It is only allowed on sockets and pipes. */
+   select.  It is only allowed on communication ports, sockets, or
+   pipes. */
 int
 _sys_read_ahead (int fd)
 {
@@ -6788,6 +7579,11 @@ globals_of_w32 (void)
   g_b_init_get_native_system_info = 0;
   g_b_init_get_system_times = 0;
   g_b_init_create_symbolic_link = 0;
+  g_b_init_get_security_descriptor_dacl = 0;
+  g_b_init_convert_sd_to_sddl = 0;
+  g_b_init_convert_sddl_to_sd = 0;
+  g_b_init_is_valid_security_descriptor = 0;
+  g_b_init_set_file_security = 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
@@ -6797,6 +7593,9 @@ globals_of_w32 (void)
 
   /* "None" is the default group name on standalone workstations.  */
   strcpy (dflt_group_name, "None");
+
+  /* Reset, in case it has some value inherited from dump time.  */
+  w32_stat_get_owner_group = 0;
 }
 
 /* For make-serial-process  */