/* 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_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;
+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;
/*
PSECURITY_DESCRIPTOR pSecurityDescriptor,
DWORD nLength,
LPDWORD lpnLengthNeeded);
-typedef BOOL (WINAPI *SetFileSecurity_Proc) (
- LPCTSTR lpFileName,
+typedef BOOL (WINAPI *SetFileSecurityW_Proc) (
+ LPCWSTR lpFileName,
+ SECURITY_INFORMATION SecurityInformation,
+ PSECURITY_DESCRIPTOR pSecurityDescriptor);
+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,
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)
ppSecurityDescriptor));
}
-static int filename_to_ansi (const char *, char *);
-static int filename_to_utf16 (const char *, wchar_t *);
-
static BOOL WINAPI
get_file_security (const char *lpFileName,
SECURITY_INFORMATION RequestedInformation,
}
static BOOL WINAPI
-set_file_security (LPCTSTR lpFileName,
+set_file_security (const char *lpFileName,
SECURITY_INFORMATION SecurityInformation,
PSECURITY_DESCRIPTOR pSecurityDescriptor)
{
- static SetFileSecurity_Proc s_pfn_Set_File_Security = NULL;
+ static SetFileSecurityW_Proc s_pfn_Set_File_SecurityW = NULL;
+ static SetFileSecurityA_Proc s_pfn_Set_File_SecurityA = NULL;
HMODULE hm_advapi32 = NULL;
if (is_windows_9x () == TRUE)
{
errno = ENOTSUP;
return FALSE;
}
- if (g_b_init_set_file_security == 0)
+ if (w32_unicode_filenames)
{
- g_b_init_set_file_security = 1;
- hm_advapi32 = LoadLibrary ("Advapi32.dll");
- s_pfn_Set_File_Security =
- (SetFileSecurity_Proc) GetProcAddress (
- hm_advapi32, "SetFileSecurityA");
+ wchar_t filename_w[MAX_PATH];
+
+ if (g_b_init_set_file_security_w == 0)
+ {
+ g_b_init_set_file_security_w = 1;
+ hm_advapi32 = LoadLibrary ("Advapi32.dll");
+ s_pfn_Set_File_SecurityW =
+ (SetFileSecurityW_Proc) GetProcAddress (hm_advapi32,
+ "SetFileSecurityW");
+ }
+ if (s_pfn_Set_File_SecurityW == NULL)
+ {
+ errno = ENOTSUP;
+ return FALSE;
+ }
+ filename_to_utf16 (lpFileName, filename_w);
+ return (s_pfn_Set_File_SecurityW (filename_w, SecurityInformation,
+ pSecurityDescriptor));
}
- if (s_pfn_Set_File_Security == NULL)
+ else
+ {
+ char filename_a[MAX_PATH];
+
+ if (g_b_init_set_file_security_a == 0)
+ {
+ g_b_init_set_file_security_a = 1;
+ hm_advapi32 = LoadLibrary ("Advapi32.dll");
+ s_pfn_Set_File_SecurityA =
+ (SetFileSecurityA_Proc) GetProcAddress (hm_advapi32,
+ "SetFileSecurityA");
+ }
+ if (s_pfn_Set_File_SecurityA == NULL)
+ {
+ errno = ENOTSUP;
+ return FALSE;
+ }
+ filename_to_ansi (lpFileName, filename_a);
+ return (s_pfn_Set_File_SecurityA (filename_a, SecurityInformation,
+ pSecurityDescriptor));
+ }
+}
+
+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 FALSE;
+ 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));
}
- return (s_pfn_Set_File_Security (lpFileName, SecurityInformation,
- pSecurityDescriptor));
}
static BOOL WINAPI
\f
+/* Here's an overview of how the Windows build supports file names
+ that cannot be encoded by the current system codepage.
+
+ From the POV of Lisp and layers of C code above the functions here,
+ Emacs on Windows pretends that its file names are encoded in UTF-8;
+ see encode_file and decode_file on coding.c. Any file name that is
+ passed as a unibyte string to C functions defined here is assumed
+ to be in UTF-8 encoding. Any file name returned by functions
+ defined here must be in UTF-8 encoding, with only a few exceptions
+ reserved for a couple of special cases. (Be sure to use
+ MAX_UTF8_PATH for char arrays that store UTF-8 encoded file names,
+ as they can be much longer than MAX_PATH!)
+
+ The UTF-8 encoded file names cannot be passed to system APIs, as
+ Windows does not support that. Therefore, they are converted
+ either to UTF-16 or to the ANSI codepage, depending on the value of
+ w32-unicode-filenames, before calling any system APIs or CRT library
+ functions. The default value of that variable is determined by the
+ OS on which Emacs runs: nil on Windows 9X and t otherwise, but the
+ user can change that default (although I don't see why would she
+ want to).
+
+ The 4 functions defined below, filename_to_utf16, filename_to_ansi,
+ filename_from_utf16, and filename_from_ansi, are the workhorses of
+ these conversions. They rely on Windows native APIs
+ MultiByteToWideChar and WideCharToMultiByte; we cannot use
+ functions from coding.c here, because they allocate memory, which
+ is a bad idea on the level of libc, which is what the functions
+ here emulate. (If you worry about performance due to constant
+ 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 did anyway, because they are just thin wrappers around the
+ Unicode APIs.)
+
+ The variables file-name-coding-system and default-file-name-coding-system
+ still exist, but are actually used only when a file name needs to
+ be converted to the ANSI codepage. This happens all the time when
+ w32-unicode-filenames is nil, but can also happen from time to time
+ when it is t. Otherwise, these variables have no effect on file-name
+ encoding when w32-unicode-filenames is t; this is similar to
+ selection-coding-system.
+
+ This arrangement works very well, but it has a few gotchas and
+ limitations:
+
+ . Lisp code that encodes or decodes file names manually should
+ normally use 'utf-8' as the coding-system on Windows,
+ disregarding file-name-coding-system. This is a somewhat
+ unpleasant consequence, but it cannot be avoided. Fortunately,
+ very few Lisp packages need to do that.
+
+ More generally, passing to library functions (e.g., fopen or
+ opendir) file names already encoded in the ANSI codepage is
+ 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
+ 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
+ whose value is a file name or a list of directories, it needs to
+ be converted to UTF-8, before it is used as argument to functions
+ or decoded into a Lisp string.
+
+ . File names passed to external libraries, like the image libraries
+ and GnuTLS, need special handling. These libraries generally
+ don't support UTF-16 or UTF-8 file names, so they must get file
+ names encoded in the ANSI codepage. To facilitate using these
+ libraries with file names that are not encodable in the ANSI
+ codepage, use the function ansi_encode_filename, which will try
+ to use the short 8+3 alias of a file name if that file name is
+ not encodable in the ANSI codepage. See image.c and gnutls.c for
+ examples of how this should be done.
+
+ . 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
+ 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
+ current ANSI codepage), so w32proc.c functions cannot re-encode
+ them in UTF-16. This should be fixed, but will also require
+ changes in cmdproxy. The current limitation is not terribly bad
+ anyway, since very few, if any, Windows console programs that are
+ likely to be invoked by Emacs support UTF-16 encoded command
+ lines.
+
+ . For similar reasons, server.el and emacsclient are also limited
+ to the current ANSI codepage for now.
+
+ . Emacs itself can only handle command-line arguments encoded in
+ the current codepage.
+
+ . Turning on w32-unicode-filename on Windows 9X (if it at all
+ works) requires UNICOWS.DLL, which is thus a requirement even in
+ non-GUI sessions, something the we previously avoided. */
+
+\f
+
/* Converting file names from UTF-8 to either UTF-16 or the ANSI
codepage defined by file-name-coding-system. */
return file_name_codepage;
}
-static int
+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)
{
return 0;
}
-static int
+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)
{
return 0;
}
-static int
+int
filename_to_ansi (const char *fn_in, char *fn_out)
{
wchar_t fn_utf16[MAX_PATH];
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);
while (*fp)
{
- if (*fp == '/' || *fp == '\\')
+ if ((*fp == '/' || *fp == '\\') && *fp != path_sep)
*fp = path_sep;
fp++;
}
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;
+ int cstatus = -1;
/* Must be valid filename, no wild cards or other invalid characters. */
if (strpbrk (name, "*?|<>\""))
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)
}
}
+/* Re-encode FILENAME, a UTF-8 encoded unibyte string, using the
+ MS-Windows ANSI codepage. If FILENAME includes characters not
+ supported by the ANSI codepage, return the 8+3 alias of FILENAME,
+ if it exists. This is needed because the w32 build wants to
+ support file names outside of the system locale, but image
+ libraries typically don't support wide (a.k.a. "Unicode") APIs
+ required for that. */
+
+Lisp_Object
+ansi_encode_filename (Lisp_Object filename)
+{
+ Lisp_Object encoded_filename;
+ char fname[MAX_PATH];
+
+ filename_to_ansi (SSDATA (filename), fname);
+ if (_mbspbrk (fname, "?"))
+ {
+ char shortname[MAX_PATH];
+
+ if (w32_get_short_filename (SDATA (filename), shortname, MAX_PATH))
+ {
+ dostounix_filename (shortname);
+ encoded_filename = build_string (shortname);
+ }
+ }
+ else
+ encoded_filename = build_unibyte_string (fname);
+ return encoded_filename;
+}
+
static int
is_unc_volume (const char *filename)
{
{
char *var;
size_t name_len;
- int retval;
if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
{
return (NULL);
}
+/* The argv[] array holds ANSI-encoded strings, and so this function
+ works with ANS_encoded strings. */
void
init_environment (char ** argv)
{
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
char *p;
char modname[MAX_PATH];
- if (!GetModuleFileName (NULL, modname, MAX_PATH))
+ if (!GetModuleFileNameA (NULL, modname, MAX_PATH))
emacs_abort ();
if ((p = _mbsrchr (modname, '\\')) == NULL)
emacs_abort ();
{
static char modname[MAX_PATH];
- if (!GetModuleFileName (NULL, modname, MAX_PATH))
+ if (!GetModuleFileNameA (NULL, modname, MAX_PATH))
emacs_abort ();
argv[0] = modname;
}
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
}
}
else if (err == ERROR_FILE_NOT_FOUND
- || err == ERROR_PATH_NOT_FOUND)
+ || err == ERROR_PATH_NOT_FOUND
+ /* ERROR_INVALID_NAME is what we get if
+ w32-unicode-filenames is nil and the file cannot
+ be encoded in the current ANSI codepage. */
+ || err == ERROR_INVALID_NAME)
errno = ENOENT;
else
errno = EIO;
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;
- if (!set_file_security ((char *)fname, flags, (PSECURITY_DESCRIPTOR)acl))
+ /* 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
acl_free (current_acl);
}
}
- else if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND)
+ else if (err == ERROR_FILE_NOT_FOUND
+ || err == ERROR_PATH_NOT_FOUND
+ /* ERROR_INVALID_NAME is what we get if
+ w32-unicode-filenames is nil and the file cannot be
+ encoded in the current ANSI codepage. */
+ || err == ERROR_INVALID_NAME)
errno = ENOENT;
else
errno = EACCES;
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;
}
}
/* Implementation note: this function runs early during Emacs
startup, before startup.el is run. So Vload_path is still in
- its initial unibyte form, holding ANSI-encoded file names.
- That is why we never bother to ENCODE_FILE here, nor use wide
- APIs for file names: we will never get UTF-8 encoded file
- names here. */
+ its initial unibyte form, but it holds UTF-8 encoded file
+ names, since init_callproc was already called. So we do not
+ 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);
char *buffer = alloca (1024
+ strlen (init_file_name)
+ strlen (load_path));
+ char *msg = buffer;
+ int needed;
sprintf (buffer,
"The Emacs Windows initialization file \"%s.el\" "
"not unpacked properly.\nSee the README.W32 file in the "
"top-level Emacs directory for more information.",
init_file_name, load_path);
+ 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));
+
+ 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);
+
+ pWideCharToMultiByte (CP_ACP, 0, msg_w, -1, msg_a, needed,
+ NULL, NULL);
+ msg = msg_a;
+ }
+ }
MessageBox (NULL,
- buffer,
+ msg,
"Emacs Abort Dialog",
MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
/* Use the low-level system abort. */
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_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;
+ 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;