/* 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.
#include <grp.h>
/* MinGW64 (_W64) defines these in its _mingw.h. */
-#if defined(__GNUC__) && !defined(_W64)
-#define _ANONYMOUS_UNION
-#define _ANONYMOUS_STRUCT
+#ifndef _ANONYMOUS_UNION
+# define _ANONYMOUS_UNION
+#endif
+#ifndef _ANONYMOUS_STRUCT
+# define _ANONYMOUS_STRUCT
#endif
#include <windows.h>
/* Some versions of compiler define MEMORYSTATUSEX, some don't, so we
#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. */
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;
/*
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,
PIP_ADAPTER_INFO pAdapterInfo,
PULONG pOutBufLen);
+int (WINAPI *pMultiByteToWideChar)(UINT,DWORD,LPCSTR,int,LPWSTR,int);
+int (WINAPI *pWideCharToMultiByte)(UINT,DWORD,LPCWSTR,int,LPSTR,int,LPCSTR,LPBOOL);
+
/* ** A utility function ** */
static BOOL
is_windows_9x (void)
}
}
+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,
conversion back and forth from UTF-8 to UTF-16, then don't: first,
it was measured to take only a few microseconds on a not-so-fast
machine, and second, that's exactly what the ANSI APIs we used
- before do anyway, because they are just thin wrappers around the
+ before did anyway, because they are just thin wrappers around the
Unicode APIs.)
The variables file-name-coding-system and default-file-name-coding-system
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
directly in Emacs sources, without either converting the file
- name sfrom UTF-8 to either UTF-16 or ANSI codepage, or going
- through some shadowing function defined here.
+ names from UTF-8 to UTF-16 or ANSI codepage, or going through
+ some shadowing function defined here.
. Environment variables stored in Vprocess_environment are encoded
in the ANSI codepage, so if getenv/egetenv is used for a variable
. 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
+ 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
the current codepage.
. Turning on w32-unicode-filename on Windows 9X (if it at all
- works) requires UNICOWS.DLL, which is currently loaded only in a
- GUI session. */
+ works) requires UNICOWS.DLL, which is thus a requirement even in
+ non-GUI sessions, something the we previously avoided. */
\f
int
filename_to_utf16 (const char *fn_in, wchar_t *fn_out)
{
- int result = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, fn_in, -1,
- fn_out, MAX_PATH);
+ int result = pMultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, fn_in, -1,
+ fn_out, MAX_PATH);
if (!result)
{
int
filename_from_utf16 (const wchar_t *fn_in, char *fn_out)
{
- int result = WideCharToMultiByte (CP_UTF8, 0, fn_in, -1,
- fn_out, MAX_UTF8_PATH, NULL, NULL);
+ int result = pWideCharToMultiByte (CP_UTF8, 0, fn_in, -1,
+ fn_out, MAX_UTF8_PATH, NULL, NULL);
if (!result)
{
int result;
int codepage = codepage_for_filenames (NULL);
- result = WideCharToMultiByte (codepage, 0, fn_utf16, -1,
- fn_out, MAX_PATH, NULL, NULL);
+ result = pWideCharToMultiByte (codepage, 0, fn_utf16, -1,
+ fn_out, MAX_PATH, NULL, NULL);
if (!result)
{
DWORD err = GetLastError ();
{
wchar_t fn_utf16[MAX_PATH];
int codepage = codepage_for_filenames (NULL);
- int result = MultiByteToWideChar (codepage, MB_ERR_INVALID_CHARS, fn_in, -1,
- fn_utf16, MAX_PATH);
+ int result = pMultiByteToWideChar (codepage, MB_ERR_INVALID_CHARS, fn_in, -1,
+ fn_utf16, MAX_PATH);
if (!result)
{
/* We maintain 1-sec samples for the last 16 minutes in a circular buffer. */
static struct load_sample samples[16*60];
static int first_idx = -1, last_idx = -1;
-static int max_idx = sizeof (samples) / sizeof (samples[0]);
+static int max_idx = ARRAYELTS (samples);
static int
buf_next (int from)
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);
{
char *var;
size_t name_len;
- int retval;
if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
{
int i;
- const int imax = sizeof (tempdirs) / sizeof (tempdirs[0]);
+ const int imax = ARRAYELTS (tempdirs);
/* Implementation note: This function explicitly works with ANSI
file names, not with UTF-8 encoded file names. This is because
{"LANG", NULL},
};
-#define N_ENV_VARS sizeof (dflt_envvars)/sizeof (dflt_envvars[0])
+#define N_ENV_VARS ARRAYELTS (dflt_envvars)
/* We need to copy dflt_envvars[] and work on the copy because we
don't want the dumped Emacs to inherit the values of
return NULL;
}
- if (!(dirp = (DIR *) malloc (sizeof (DIR))))
+ if (!(dirp = xmalloc (sizeof (DIR))))
return NULL;
dirp->dd_fd = 0;
/* We used to pass MB_PRECOMPOSED as the 2nd arg here, but MSDN
indicates that flag is unsupported for CP_UTF8, and OTOH says
it is the default anyway. */
- wlen = MultiByteToWideChar (CP_UTF8, 0, newname, -1,
- data.wid.cStreamName, MAX_PATH);
+ wlen = pMultiByteToWideChar (CP_UTF8, 0, newname, -1,
+ data.wid.cStreamName, MAX_PATH);
if (wlen > 0)
{
LPVOID context = NULL;
return -1;
}
- /* 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);
+ /* Allocate 1 extra byte so that we could append a slash to a root
+ directory, down below. */
name = strcpy (alloca (len + 2), name);
/* Avoid a somewhat costly call to is_symlink if the filesystem
}
else if (rootdir)
{
+ /* Make sure root directories end in a slash. */
if (!IS_DIRECTORY_SEP (name[len-1]))
strcat (name, "\\");
if (GetDriveType (name) < 2)
{
int have_wfd = -1;
+ /* Make sure non-root directories do NOT end in a slash,
+ otherwise FindFirstFile might fail. */
if (IS_DIRECTORY_SEP (name[len-1]))
name[len - 1] = 0;
&& !(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,
/* 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, "?"))
if (fd != AT_FDCWD)
{
- if (_snprintf (fullname, sizeof fullname, "%s/%s", dir_pathname, name)
+ char lastc = dir_pathname[strlen (dir_pathname) - 1];
+
+ if (_snprintf (fullname, sizeof fullname, "%s%s%s",
+ dir_pathname, IS_DIRECTORY_SEP (lastc) ? "" : "/", name)
< 0)
{
errno = ENAMETOOLONG;
return 0;
}
+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 & S_IWRITE) != 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 & ~S_IWRITE);
+ current_mask = mode;
+
+ return retval;
+}
+
\f
/* Symlink-related functions. */
#ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
DWORD err;
int st = 0, retval = -1;
SECURITY_INFORMATION flags = 0;
- PSID psid;
+ PSID psidOwner, psidGroup;
PACL pacl;
BOOL dflt;
BOOL dacl_present;
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)
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
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. */
/* Possibly truncated */
? make_specified_string (name, -1, len, 1)
: Qnil);
+ /* This prevents thread start and end notifications
+ from being sent to the DLL, for every thread we
+ start. We don't need those notifications because
+ threads we create never use any of these DLLs, only
+ the main thread uses them. This is supposed to
+ speed up thread creation. */
+ DisableThreadLibraryCalls (dll_handle);
break;
}
}
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);
"not unpacked properly.\nSee the README.W32 file in the "
"top-level Emacs directory for more information.",
init_file_name, load_path);
- needed = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, buffer,
- -1, NULL, 0);
+ needed = pMultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, buffer,
+ -1, NULL, 0);
if (needed > 0)
{
wchar_t *msg_w = alloca ((needed + 1) * sizeof (wchar_t));
- MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, buffer, -1,
- msg_w, needed);
- needed = WideCharToMultiByte (CP_ACP, 0, msg_w, -1,
- NULL, 0, NULL, NULL);
+ pMultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, buffer, -1,
+ msg_w, needed);
+ needed = pWideCharToMultiByte (CP_ACP, 0, msg_w, -1,
+ NULL, 0, NULL, NULL);
if (needed > 0)
{
char *msg_a = alloca (needed + 1);
- WideCharToMultiByte (CP_ACP, 0, msg_w, -1, msg_a, needed,
- NULL, NULL);
+ pWideCharToMultiByte (CP_ACP, 0, msg_w, -1, msg_a, needed,
+ NULL, NULL);
msg = msg_a;
}
}
return FALSE;
}
+/* On Windows 9X, load UNICOWS.DLL and return its handle, or die. On
+ NT, return a handle to GDI32.DLL. */
+HANDLE
+maybe_load_unicows_dll (void)
+{
+ if (os_subtype == OS_9X)
+ {
+ HANDLE ret = LoadLibrary ("Unicows.dll");
+ if (ret)
+ {
+ /* These two functions are present on Windows 9X as stubs
+ that always fail. We need the real implementations from
+ UNICOWS.DLL, so we must call these functions through
+ pointers, and assign the correct addresses to these
+ pointers at program startup (see emacs.c, which calls
+ this function early on). */
+ pMultiByteToWideChar = GetProcAddress (ret, "MultiByteToWideChar");
+ pWideCharToMultiByte = GetProcAddress (ret, "WideCharToMultiByte");
+ return ret;
+ }
+ else
+ {
+ int button;
+
+ button = MessageBox (NULL,
+ "Emacs cannot load the UNICOWS.DLL library.\n"
+ "This library is essential for using Emacs\n"
+ "on this system. You need to install it.\n\n"
+ "Emacs will exit when you click OK.",
+ "Emacs cannot load UNICOWS.DLL",
+ MB_ICONERROR | MB_TASKMODAL
+ | MB_SETFOREGROUND | MB_OK);
+ switch (button)
+ {
+ case IDOK:
+ default:
+ exit (1);
+ }
+ }
+ }
+ else
+ {
+ /* On NT family of Windows, these two functions are always
+ linked in, so we just assign their addresses to the 2
+ pointers; no need for the LoadLibrary dance. */
+ pMultiByteToWideChar = MultiByteToWideChar;
+ pWideCharToMultiByte = WideCharToMultiByte;
+ return LoadLibrary ("Gdi32.dll");
+ }
+}
+
/*
globals_of_w32 is used to initialize those global variables that
must always be initialized on startup even when the global variable
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
emacs_gnutls_pull (gnutls_transport_ptr_t p, void* buf, size_t sz)
{
int n, err;
- SELECT_TYPE fdset;
- struct timespec timeout;
struct Lisp_Process *process = (struct Lisp_Process *)p;
int fd = process->infd;