]> code.delx.au - gnu-emacs/blobdiff - src/w32.c
Replace doc references to deleted etc/ files
[gnu-emacs] / src / w32.c
index 7e32da84c58cc50b18d48c436e7662fc151ef574..8aca8c1b71ccc25b2ff85937b45fba8e830dda90 100644 (file)
--- a/src/w32.c
+++ b/src/w32.c
@@ -1,5 +1,6 @@
 /* Utility and Unix shadow routines for GNU Emacs on the Microsoft Windows API.
-   Copyright (C) 1994-1995, 2000-2013 Free Software Foundation, Inc.
+
+Copyright (C) 1994-1995, 2000-2014 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -140,6 +141,7 @@ typedef struct _PROCESS_MEMORY_COUNTERS_EX {
 #include <sddl.h>
 
 #include <sys/acl.h>
+#include <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.  */
@@ -301,6 +303,8 @@ 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_w;
 static BOOL g_b_init_set_file_security_a;
+static BOOL g_b_init_set_named_security_info_w;
+static BOOL g_b_init_set_named_security_info_a;
 static BOOL g_b_init_get_adapters_info;
 
 /*
@@ -375,6 +379,22 @@ typedef BOOL (WINAPI *SetFileSecurityA_Proc) (
     LPCSTR lpFileName,
     SECURITY_INFORMATION SecurityInformation,
     PSECURITY_DESCRIPTOR pSecurityDescriptor);
+typedef DWORD (WINAPI *SetNamedSecurityInfoW_Proc) (
+    LPCWSTR lpObjectName,
+    SE_OBJECT_TYPE ObjectType,
+    SECURITY_INFORMATION SecurityInformation,
+    PSID psidOwner,
+    PSID psidGroup,
+    PACL pDacl,
+    PACL pSacl);
+typedef DWORD (WINAPI *SetNamedSecurityInfoA_Proc) (
+    LPCSTR lpObjectName,
+    SE_OBJECT_TYPE ObjectType,
+    SECURITY_INFORMATION SecurityInformation,
+    PSID psidOwner,
+    PSID psidGroup,
+    PACL pDacl,
+    PACL pSacl);
 typedef BOOL (WINAPI * GetSecurityDescriptorOwner_Proc) (
     PSECURITY_DESCRIPTOR pSecurityDescriptor,
     PSID *pOwner,
@@ -809,6 +829,69 @@ set_file_security (const char *lpFileName,
     }
 }
 
+static DWORD WINAPI
+set_named_security_info (LPCTSTR lpObjectName,
+                        SE_OBJECT_TYPE ObjectType,
+                        SECURITY_INFORMATION SecurityInformation,
+                        PSID psidOwner,
+                        PSID psidGroup,
+                        PACL pDacl,
+                        PACL pSacl)
+{
+  static SetNamedSecurityInfoW_Proc s_pfn_Set_Named_Security_InfoW = NULL;
+  static SetNamedSecurityInfoA_Proc s_pfn_Set_Named_Security_InfoA = NULL;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      errno = ENOTSUP;
+      return ENOTSUP;
+    }
+  if (w32_unicode_filenames)
+    {
+      wchar_t filename_w[MAX_PATH];
+
+      if (g_b_init_set_named_security_info_w == 0)
+       {
+         g_b_init_set_named_security_info_w = 1;
+         hm_advapi32 = LoadLibrary ("Advapi32.dll");
+         s_pfn_Set_Named_Security_InfoW =
+           (SetNamedSecurityInfoW_Proc) GetProcAddress (hm_advapi32,
+                                                        "SetNamedSecurityInfoW");
+       }
+      if (s_pfn_Set_Named_Security_InfoW == NULL)
+       {
+         errno = ENOTSUP;
+         return ENOTSUP;
+       }
+      filename_to_utf16 (lpObjectName, filename_w);
+      return (s_pfn_Set_Named_Security_InfoW (filename_w, ObjectType,
+                                             SecurityInformation, psidOwner,
+                                             psidGroup, pDacl, pSacl));
+    }
+  else
+    {
+      char filename_a[MAX_PATH];
+
+      if (g_b_init_set_named_security_info_a == 0)
+       {
+         g_b_init_set_named_security_info_a = 1;
+         hm_advapi32 = LoadLibrary ("Advapi32.dll");
+         s_pfn_Set_Named_Security_InfoA =
+           (SetNamedSecurityInfoA_Proc) GetProcAddress (hm_advapi32, 
+                                                        "SetNamedSecurityInfoA");
+       }
+      if (s_pfn_Set_Named_Security_InfoA == NULL)
+       {
+         errno = ENOTSUP;
+         return ENOTSUP;
+       }
+      filename_to_ansi (lpObjectName, filename_a);
+      return (s_pfn_Set_Named_Security_InfoA (filename_a, ObjectType,
+                                             SecurityInformation, psidOwner,
+                                             psidGroup, pDacl, pSacl));
+    }
+}
+
 static BOOL WINAPI
 get_security_descriptor_owner (PSECURITY_DESCRIPTOR pSecurityDescriptor,
                               PSID *pOwner,
@@ -1344,7 +1427,7 @@ w32_valid_pointer_p (void *p, int size)
 
      More generally, passing to library functions (e.g., fopen or
      opendir) file names already encoded in the ANSI codepage is
-     explictly *verboten*, as all those functions, as shadowed and
+     explicitly *verboten*, as all those functions, as shadowed and
      emulated here, assume they will receive UTF-8 encoded file names.
 
      For the same reasons, no CRT function or Win32 API can be called
@@ -1371,7 +1454,7 @@ w32_valid_pointer_p (void *p, int size)
    . Running subprocesses in non-ASCII directories and with non-ASCII
      file arguments is limited to the current codepage (even though
      Emacs is perfectly capable of finding an executable program file
-     even in a directory whose name cannot be encoded in the curreent
+     even in a directory whose name cannot be encoded in the current
      codepage).  This is because the command-line arguments are
      encoded _before_ they get to the w32-specific level, and the
      encoding is not known in advance (it doesn't have to be the
@@ -1731,9 +1814,28 @@ getloadavg (double loadavg[], int nelem)
   ULONGLONG idle, kernel, user;
   time_t now = time (NULL);
 
+  /* If system time jumped back for some reason, delete all samples
+     whose time is later than the current wall-clock time.  This
+     prevents load average figures from becoming frozen for prolonged
+     periods of time, when system time is reset backwards.  */
+  if (last_idx >= 0)
+    {
+      while (difftime (now, samples[last_idx].sample_time) < -1.0)
+       {
+         if (last_idx == first_idx)
+           {
+             first_idx = last_idx = -1;
+             break;
+           }
+         last_idx = buf_prev (last_idx);
+       }
+    }
+
   /* Store another sample.  We ignore samples that are less than 1 sec
      apart.  */
-  if (difftime (now, samples[last_idx].sample_time) >= 1.0 - 2*DBL_EPSILON*now)
+  if (last_idx < 0
+      || (difftime (now, samples[last_idx].sample_time)
+         >= 1.0 - 2*DBL_EPSILON*now))
     {
       sample_system_load (&idle, &kernel, &user);
       last_idx = buf_next (last_idx);
@@ -2123,7 +2225,7 @@ parse_root (const char * name, const char ** pPath)
 static int
 get_long_basename (char * name, char * buf, int size)
 {
-  HANDLE dir_handle;
+  HANDLE dir_handle = INVALID_HANDLE_VALUE;
   char fname_utf8[MAX_UTF8_PATH];
   int len = 0;
   int cstatus = -1;
@@ -2148,9 +2250,28 @@ get_long_basename (char * name, char * buf, int size)
       WIN32_FIND_DATAA find_data_ansi;
 
       filename_to_ansi (name, fname_ansi);
-      dir_handle = FindFirstFileA (fname_ansi, &find_data_ansi);
-      if (dir_handle != INVALID_HANDLE_VALUE)
-       cstatus = filename_from_ansi (find_data_ansi.cFileName, fname_utf8);
+      /* If the ANSI name includes ? characters, it is not encodable
+        in the ANSI codepage.  In that case, we deliver the question
+        marks to the caller; calling FindFirstFileA in this case
+        could return some unrelated file name in the same
+        directory.  */
+      if (_mbspbrk (fname_ansi, "?"))
+       {
+         /* Find the basename of fname_ansi.  */
+         char *p = strrchr (fname_ansi, '\\');
+
+         if (!p)
+           p = fname_ansi;
+         else
+           p++;
+         cstatus = filename_from_ansi (p, fname_utf8);
+       }
+      else
+       {
+         dir_handle = FindFirstFileA (fname_ansi, &find_data_ansi);
+         if (dir_handle != INVALID_HANDLE_VALUE)
+           cstatus = filename_from_ansi (find_data_ansi.cFileName, fname_utf8);
+       }
     }
 
   if (cstatus == 0 && (len = strlen (fname_utf8)) < size)
@@ -4868,7 +4989,7 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks)
              && !(is_a_symlink && follow_symlinks)
              /* The 2 file-name comparisons below support only ASCII
                 characters, and will lose (compare not equal) when
-                the file names include non-ASCII charcaters that are
+                the file names include non-ASCII characters that are
                 the same but for the case.  However, doing this
                 properly involves: (a) converting both file names to
                 UTF-16, (b) lower-casing both names using CharLowerW,
@@ -4902,7 +5023,7 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks)
                  /* If NAME includes characters not representable by
                     the current ANSI codepage, filename_to_ansi
                     usually replaces them with a '?'.  We don't want
-                    to let FindFirstFileA interpret those as widlcards,
+                    to let FindFirstFileA interpret those as wildcards,
                     and "succeed", returning us data from some random
                     file in the same directory.  */
                  if (_mbspbrk (name_a, "?"))
@@ -5225,6 +5346,35 @@ utime (const char *name, struct utimbuf *times)
   return 0;
 }
 
+/* Emacs expects us to support the traditional octal form of the mode
+   bits, which is not what msvcrt.dll wants.  */
+
+#define WRITE_USER 00200
+
+int
+sys_umask (int mode)
+{
+  static int current_mask;
+  int retval, arg = 0;
+
+  /* The only bit we really support is the write bit.  Files are
+     always readable on MS-Windows, and the execute bit does not exist
+     at all.  */
+  /* FIXME: if the GROUP and OTHER bits are reset, we should use ACLs
+     to prevent access by other users on NTFS.  */
+  if ((mode & WRITE_USER) != 0)
+    arg |= S_IWRITE;
+
+  retval = _umask (arg);
+  /* Merge into the return value the bits they've set the last time,
+     which msvcrt.dll ignores and never returns.  Emacs insists on its
+     notion of mask being identical to what we return.  */
+  retval |= (current_mask & ~WRITE_USER);
+  current_mask = mode;
+
+  return retval;
+}
+
 \f
 /* Symlink-related functions.  */
 #ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
@@ -5834,7 +5984,7 @@ acl_set_file (const char *fname, acl_type_t type, acl_t acl)
   DWORD err;
   int st = 0, retval = -1;
   SECURITY_INFORMATION flags = 0;
-  PSID psid;
+  PSID psidOwner, psidGroup;
   PACL pacl;
   BOOL dflt;
   BOOL dacl_present;
@@ -5860,11 +6010,13 @@ acl_set_file (const char *fname, acl_type_t type, acl_t acl)
   else
     fname = filename;
 
-  if (get_security_descriptor_owner ((PSECURITY_DESCRIPTOR)acl, &psid, &dflt)
-      && psid)
+  if (get_security_descriptor_owner ((PSECURITY_DESCRIPTOR)acl, &psidOwner,
+                                    &dflt)
+      && psidOwner)
     flags |= OWNER_SECURITY_INFORMATION;
-  if (get_security_descriptor_group ((PSECURITY_DESCRIPTOR)acl, &psid, &dflt)
-      && psid)
+  if (get_security_descriptor_group ((PSECURITY_DESCRIPTOR)acl, &psidGroup,
+                                    &dflt)
+      && psidGroup)
     flags |= GROUP_SECURITY_INFORMATION;
   if (get_security_descriptor_dacl ((PSECURITY_DESCRIPTOR)acl, &dacl_present,
                                    &pacl, &dflt)
@@ -5891,10 +6043,22 @@ acl_set_file (const char *fname, acl_type_t type, acl_t acl)
 
   e = errno;
   errno = 0;
+  /* SetFileSecurity is deprecated by MS, and sometimes fails when
+     DACL inheritance is involved, but it seems to preserve ownership
+     better than SetNamedSecurityInfo, which is important e.g., in
+     copy-file.  */
   if (!set_file_security (fname, flags, (PSECURITY_DESCRIPTOR)acl))
     {
       err = GetLastError ();
 
+      if (errno != ENOTSUP)
+       err = set_named_security_info (fname, SE_FILE_OBJECT, flags,
+                                      psidOwner, psidGroup, pacl, NULL);
+    }
+  else
+    err = ERROR_SUCCESS;
+  if (err != ERROR_SUCCESS)
+    {
       if (errno == ENOTSUP)
        ;
       else if (err == ERROR_INVALID_OWNER
@@ -5982,6 +6146,116 @@ careadlinkat (int fd, char const *filename,
   return NULL;
 }
 
+int
+w32_copy_file (const char *from, const char *to,
+              int keep_time, int preserve_ownership, int copy_acls)
+{
+  acl_t acl = NULL;
+  BOOL copy_result;
+  wchar_t from_w[MAX_PATH], to_w[MAX_PATH];
+  char from_a[MAX_PATH], to_a[MAX_PATH];
+
+  /* We ignore preserve_ownership for now.  */
+  preserve_ownership = preserve_ownership;
+
+  if (copy_acls)
+    {
+      acl = acl_get_file (from, ACL_TYPE_ACCESS);
+      if (acl == NULL && acl_errno_valid (errno))
+       return -2;
+    }
+  if (w32_unicode_filenames)
+    {
+      filename_to_utf16 (from, from_w);
+      filename_to_utf16 (to, to_w);
+      copy_result = CopyFileW (from_w, to_w, FALSE);
+    }
+  else
+    {
+      filename_to_ansi (from, from_a);
+      filename_to_ansi (to, to_a);
+      copy_result = CopyFileA (from_a, to_a, FALSE);
+    }
+  if (!copy_result)
+    {
+      /* CopyFile doesn't set errno when it fails.  By far the most
+        "popular" reason is that the target is read-only.  */
+      DWORD err = GetLastError ();
+
+      switch (err)
+       {
+       case ERROR_FILE_NOT_FOUND:
+         errno = ENOENT;
+         break;
+       case ERROR_ACCESS_DENIED:
+         errno = EACCES;
+         break;
+       case ERROR_ENCRYPTION_FAILED:
+         errno = EIO;
+         break;
+       default:
+         errno = EPERM;
+         break;
+       }
+
+      if (acl)
+       acl_free (acl);
+      return -1;
+    }
+  /* CopyFile retains the timestamp by default.  However, see
+     "Community Additions" for CopyFile: it sounds like that is not
+     entirely true.  Testing on Windows XP confirms that modified time
+     is copied, but creation and last-access times are not.
+     FIXME?  */
+  else if (!keep_time)
+    {
+      struct timespec now;
+      DWORD attributes;
+
+      if (w32_unicode_filenames)
+       {
+         /* Ensure file is writable while its times are set.  */
+         attributes = GetFileAttributesW (to_w);
+         SetFileAttributesW (to_w, attributes & ~FILE_ATTRIBUTE_READONLY);
+         now = current_timespec ();
+         if (set_file_times (-1, to, now, now))
+           {
+             /* Restore original attributes.  */
+             SetFileAttributesW (to_w, attributes);
+             if (acl)
+               acl_free (acl);
+             return -3;
+           }
+         /* Restore original attributes.  */
+         SetFileAttributesW (to_w, attributes);
+       }
+      else
+       {
+         attributes = GetFileAttributesA (to_a);
+         SetFileAttributesA (to_a, attributes & ~FILE_ATTRIBUTE_READONLY);
+         now = current_timespec ();
+         if (set_file_times (-1, to, now, now))
+           {
+             SetFileAttributesA (to_a, attributes);
+             if (acl)
+               acl_free (acl);
+             return -3;
+           }
+         SetFileAttributesA (to_a, attributes);
+       }
+    }
+  if (acl != NULL)
+    {
+      bool fail =
+       acl_set_file (to, ACL_TYPE_ACCESS, acl) != 0;
+      acl_free (acl);
+      if (fail && acl_errno_valid (errno))
+       return -4;
+    }
+
+  return 0;
+}
+
 \f
 /* Support for browsing other processes and their attributes.  See
    process.c for the Lisp bindings.  */
@@ -8453,7 +8727,7 @@ check_windows_init_file (void)
         need to ENCODE_FILE here, but we do need to convert the file
         names from UTF-8 to ANSI.  */
       init_file = build_string ("term/w32-win");
-      fd = openp (Vload_path, init_file, Fget_load_suffixes (), NULL, Qnil);
+      fd = openp (Vload_path, init_file, Fget_load_suffixes (), NULL, Qnil, 0);
       if (fd < 0)
        {
          Lisp_Object load_path_print = Fprin1_to_string (Vload_path, Qnil);
@@ -8699,6 +8973,8 @@ globals_of_w32 (void)
   g_b_init_is_valid_security_descriptor = 0;
   g_b_init_set_file_security_w = 0;
   g_b_init_set_file_security_a = 0;
+  g_b_init_set_named_security_info_w = 0;
+  g_b_init_set_named_security_info_a = 0;
   g_b_init_get_adapters_info = 0;
   num_of_processors = 0;
   /* The following sets a handler for shutdown notifications for