X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/d133cf839421462280ac0bfd9bd84c591f0e0249..0e963201d03d9229bb8ac4323291d2b0119526ed:/src/w32.c diff --git a/src/w32.c b/src/w32.c index 1917fea343..c77ed87b00 100644 --- a/src/w32.c +++ b/src/w32.c @@ -1,6 +1,6 @@ /* Utility and Unix shadow routines for GNU Emacs on the Microsoft Windows API. -Copyright (C) 1994-1995, 2000-2015 Free Software Foundation, Inc. +Copyright (C) 1994-1995, 2000-2016 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -67,7 +67,7 @@ along with GNU Emacs. If not, see . */ #undef localtime #include "lisp.h" -#include "epaths.h" /* for SHELL */ +#include "epaths.h" /* for PATH_EXEC */ #include #include @@ -224,20 +224,22 @@ typedef struct _REPARSE_DATA_BUFFER { #include /* should be after winsock2.h */ +#include + #include "w32.h" #include #include "w32common.h" -#include "w32heap.h" #include "w32select.h" -#include "systime.h" +#include "systime.h" /* for current_timespec, struct timespec */ #include "dispextern.h" /* for xstrcasecmp */ #include "coding.h" /* for Vlocale_coding_system */ #include "careadlinkat.h" #include "allocator.h" -/* For serial_configure and serial_open. */ +/* For Lisp_Process, serial_configure and serial_open. */ #include "process.h" +#include "systty.h" typedef HRESULT (WINAPI * ShGetFolderPath_fn) (IN HWND, IN int, IN HANDLE, IN DWORD, OUT char *); @@ -1490,7 +1492,7 @@ static int file_name_codepage; /* Produce a Windows ANSI codepage suitable for encoding file names. Return the information about that codepage in CP_INFO. */ -static int +int codepage_for_filenames (CPINFO *cp_info) { /* A simple cache to avoid calling GetCPInfo every time we need to @@ -1511,7 +1513,7 @@ codepage_for_filenames (CPINFO *cp_info) if (NILP (current_encoding)) { - char *cpname = SDATA (SYMBOL_NAME (current_encoding)); + char *cpname = SSDATA (SYMBOL_NAME (current_encoding)); char *cp = NULL, *end; int cpnum; @@ -2163,11 +2165,11 @@ unixtodos_filename (register char *p) (From msdos.c...probably should figure out a way to share it, although this code isn't going to ever change.) */ static int -crlf_to_lf (register int n, register unsigned char *buf) +crlf_to_lf (register int n, register char *buf) { - unsigned char *np = buf; - unsigned char *startp = buf; - unsigned char *endp = buf + n; + unsigned char *np = (unsigned char *)buf; + unsigned char *startp = np; + char *endp = buf + n; if (n == 0) return n; @@ -2384,7 +2386,7 @@ ansi_encode_filename (Lisp_Object filename) { char shortname[MAX_PATH]; - if (w32_get_short_filename (SDATA (filename), shortname, MAX_PATH)) + if (w32_get_short_filename (SSDATA (filename), shortname, MAX_PATH)) { dostounix_filename (shortname); encoded_filename = build_string (shortname); @@ -3399,30 +3401,41 @@ sys_readdir (DIR *dirp) /* If we aren't dir_finding, do a find-first, otherwise do a find-next. */ else if (dir_find_handle == INVALID_HANDLE_VALUE) { - char filename[MAX_UTF8_PATH + 2]; + char filename[MAX_UTF8_PATH]; int ln; + bool last_slash = true; + /* Note: We don't need to worry about dir_pathname being longer + than MAX_UTF8_PATH, as sys_opendir already took care of that + when it called map_w32_filename: that function will put a "?" + in its return value in that case, thus failing all the calls + below. */ strcpy (filename, dir_pathname); ln = strlen (filename); if (!IS_DIRECTORY_SEP (filename[ln - 1])) - filename[ln++] = '\\'; - strcpy (filename + ln, "*"); + last_slash = false; /* Note: No need to resolve symlinks in FILENAME, because FindFirst opens the directory that is the target of a symlink. */ if (w32_unicode_filenames) { - wchar_t fnw[MAX_PATH]; + wchar_t fnw[MAX_PATH + 2]; filename_to_utf16 (filename, fnw); + if (!last_slash) + wcscat (fnw, L"\\"); + wcscat (fnw, L"*"); dir_find_handle = FindFirstFileW (fnw, &dir_find_data_w); } else { - char fna[MAX_PATH]; + char fna[MAX_PATH + 2]; filename_to_ansi (filename, fna); + if (!last_slash) + strcat (fna, "\\"); + strcat (fna, "*"); /* If FILENAME is not representable by the current ANSI codepage, we don't want FindFirstFileA to interpret the '?' characters as a wildcard. */ @@ -3815,7 +3828,7 @@ faccessat (int dirfd, const char * path, int mode, int flags) errno = EACCES; return -1; } - break; + goto check_attrs; } /* FALLTHROUGH */ case ERROR_FILE_NOT_FOUND: @@ -3828,6 +3841,8 @@ faccessat (int dirfd, const char * path, int mode, int flags) } return -1; } + + check_attrs: if ((mode & X_OK) != 0 && !(is_exec (path) || (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0)) { @@ -3847,6 +3862,76 @@ faccessat (int dirfd, const char * path, int mode, int flags) return 0; } +/* A special test for DIRNAME being a directory accessible by the + current user. This is needed because the security permissions in + directory's ACLs are not visible in the Posix-style mode bits + returned by 'stat' and in attributes returned by GetFileAttributes. + So a directory would seem like it's readable by the current user, + but will in fact error out with EACCES when they actually try. */ +int +w32_accessible_directory_p (const char *dirname, ptrdiff_t dirlen) +{ + char pattern[MAX_UTF8_PATH]; + bool last_slash = dirlen > 0 && IS_DIRECTORY_SEP (dirname[dirlen - 1]); + HANDLE dh; + + /* Network volumes need a different reading method. */ + if (is_unc_volume (dirname)) + { + void *read_result = NULL; + wchar_t fnw[MAX_PATH]; + char fna[MAX_PATH]; + + dh = open_unc_volume (dirname); + if (dh != INVALID_HANDLE_VALUE) + { + read_result = read_unc_volume (dh, fnw, fna, MAX_PATH); + close_unc_volume (dh); + } + /* Treat empty volumes as accessible. */ + return read_result != NULL || GetLastError () == ERROR_NO_MORE_ITEMS; + } + + /* Note: map_w32_filename makes sure DIRNAME is not longer than + MAX_UTF8_PATH. */ + strcpy (pattern, map_w32_filename (dirname, NULL)); + + /* Note: No need to resolve symlinks in FILENAME, because FindFirst + opens the directory that is the target of a symlink. */ + if (w32_unicode_filenames) + { + wchar_t pat_w[MAX_PATH + 2]; + WIN32_FIND_DATAW dfd_w; + + filename_to_utf16 (pattern, pat_w); + if (!last_slash) + wcscat (pat_w, L"\\"); + wcscat (pat_w, L"*"); + dh = FindFirstFileW (pat_w, &dfd_w); + } + else + { + char pat_a[MAX_PATH + 2]; + WIN32_FIND_DATAA dfd_a; + + filename_to_ansi (pattern, pat_a); + if (!last_slash) + strcpy (pat_a, "\\"); + strcat (pat_a, "*"); + /* In case DIRNAME cannot be expressed in characters from the + current ANSI codepage. */ + if (_mbspbrk (pat_a, "?")) + dh = INVALID_HANDLE_VALUE; + else + dh = FindFirstFileA (pat_a, &dfd_a); + } + + if (dh == INVALID_HANDLE_VALUE) + return 0; + FindClose (dh); + return 1; +} + /* A version of 'access' to be used locally with file names in locale-specific encoding. Does not resolve symlinks and does not support file names on FAT12 and FAT16 volumes, but that's OK, since @@ -4451,6 +4536,8 @@ sys_rmdir (const char * path) int sys_unlink (const char * path) { + int rmstatus, e; + path = map_w32_filename (path, NULL); if (w32_unicode_filenames) @@ -4458,9 +4545,18 @@ sys_unlink (const char * path) wchar_t path_w[MAX_PATH]; filename_to_utf16 (path, path_w); - /* On Unix, unlink works without write permission. */ + /* On Unix, unlink works without write permission. */ _wchmod (path_w, 0666); - return _wunlink (path_w); + rmstatus = _wunlink (path_w); + e = errno; + /* Symlinks to directories can only be deleted by _rmdir; + _unlink returns EACCES. */ + if (rmstatus != 0 + && errno == EACCES + && (is_symlink (path) & FILE_ATTRIBUTE_DIRECTORY) != 0) + rmstatus = _wrmdir (path_w); + else + errno = e; } else { @@ -4468,8 +4564,17 @@ sys_unlink (const char * path) filename_to_ansi (path, path_a); _chmod (path_a, 0666); - return _unlink (path_a); + rmstatus = _unlink (path_a); + e = errno; + if (rmstatus != 0 + && errno == EACCES + && (is_symlink (path) & FILE_ATTRIBUTE_DIRECTORY) != 0) + rmstatus = _rmdir (path_a); + else + errno = e; } + + return rmstatus; } static FILETIME utc_base_ft; @@ -5543,7 +5648,8 @@ symlink (char const *filename, char const *linkname) /* A quick inexpensive test of whether FILENAME identifies a file that is a symlink. Returns non-zero if it is, zero otherwise. FILENAME must already be in the normalized form returned by - map_w32_filename. + map_w32_filename. If the symlink is to a directory, the + FILE_ATTRIBUTE_DIRECTORY bit will be set in the return value. Note: for repeated operations on many files, it is best to test whether the underlying volume actually supports symlinks, by @@ -5601,6 +5707,8 @@ is_symlink (const char *filename) attrs_mean_symlink = (wfdw.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0 && (wfdw.dwReserved0 & IO_REPARSE_TAG_SYMLINK) == IO_REPARSE_TAG_SYMLINK; + if (attrs_mean_symlink) + attrs_mean_symlink |= (wfdw.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); } else if (_mbspbrk (filename_a, "?")) { @@ -5614,6 +5722,8 @@ is_symlink (const char *filename) attrs_mean_symlink = (wfda.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0 && (wfda.dwReserved0 & IO_REPARSE_TAG_SYMLINK) == IO_REPARSE_TAG_SYMLINK; + if (attrs_mean_symlink) + attrs_mean_symlink |= (wfda.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); } if (fh == INVALID_HANDLE_VALUE) return 0; @@ -6544,7 +6654,6 @@ global_memory_status_ex (MEMORY_STATUS_EX *buf) Lisp_Object list_system_processes (void) { - struct gcpro gcpro1; Lisp_Object proclist = Qnil; HANDLE h_snapshot; @@ -6556,8 +6665,6 @@ list_system_processes (void) DWORD proc_id; BOOL res; - GCPRO1 (proclist); - proc_entry.dwSize = sizeof (PROCESSENTRY32); for (res = process32_first (h_snapshot, &proc_entry); res; res = process32_next (h_snapshot, &proc_entry)) @@ -6567,7 +6674,6 @@ list_system_processes (void) } CloseHandle (h_snapshot); - UNGCPRO; proclist = Fnreverse (proclist); } @@ -6696,7 +6802,6 @@ process_times (HANDLE h_proc, Lisp_Object *ctime, Lisp_Object *etime, Lisp_Object system_process_attributes (Lisp_Object pid) { - struct gcpro gcpro1, gcpro2, gcpro3; Lisp_Object attrs = Qnil; Lisp_Object cmd_str, decoded_cmd, tem; HANDLE h_snapshot, h_proc; @@ -6728,8 +6833,6 @@ system_process_attributes (Lisp_Object pid) h_snapshot = create_toolhelp32_snapshot (TH32CS_SNAPPROCESS, 0); - GCPRO3 (attrs, decoded_cmd, tem); - if (h_snapshot != INVALID_HANDLE_VALUE) { PROCESSENTRY32 pe; @@ -6771,10 +6874,7 @@ system_process_attributes (Lisp_Object pid) } if (!found_proc) - { - UNGCPRO; - return Qnil; - } + return Qnil; h_proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, proc_id); @@ -6991,7 +7091,6 @@ system_process_attributes (Lisp_Object pid) if (h_proc) CloseHandle (h_proc); - UNGCPRO; return attrs; } @@ -7333,7 +7432,7 @@ sys_socket (int af, int type, int protocol) if (winsock_lib == NULL) { errno = ENETDOWN; - return INVALID_SOCKET; + return -1; } check_errno (); @@ -7396,7 +7495,7 @@ socket_to_fd (SOCKET s) though the socket wasn't really a kernel handle, because a real handle has the same value. So test whether the new handle really is a socket. */ - long nonblocking = 0; + unsigned long nonblocking = 0; if (pfn_ioctlsocket ((SOCKET) new_s, FIONBIO, &nonblocking) == 0) { pfn_closesocket (s); @@ -7489,8 +7588,8 @@ sys_connect (int s, const struct sockaddr * name, int namelen) errno = EINPROGRESS; /* that's what process.c expects */ fd_info[s].flags |= FILE_CONNECT; } - return rc; } + return rc; } errno = ENOTSOCK; return SOCKET_ERROR; @@ -8421,7 +8520,7 @@ sys_write (int fd, const void * buffer, unsigned int count) int nbytes = count; SAFE_NALLOCA (tmpbuf, 2, count); - dst = tmpbuf; + dst = (unsigned char *)tmpbuf; while (1) { @@ -8963,8 +9062,8 @@ check_windows_init_file (void) if (fd < 0) { Lisp_Object load_path_print = Fprin1_to_string (Vload_path, Qnil); - char *init_file_name = SDATA (init_file); - char *load_path = SDATA (load_path_print); + char *init_file_name = SSDATA (init_file); + char *load_path = SSDATA (load_path_print); char *buffer = alloca (1024 + strlen (init_file_name) + strlen (load_path)); @@ -9171,8 +9270,10 @@ maybe_load_unicows_dll (void) 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"); + pMultiByteToWideChar = + (MultiByteToWideChar_Proc)GetProcAddress (ret, "MultiByteToWideChar"); + pWideCharToMultiByte = + (WideCharToMultiByte_Proc)GetProcAddress (ret, "WideCharToMultiByte"); return ret; } else @@ -9280,6 +9381,11 @@ globals_of_w32 (void) w32_unicode_filenames = 0; else w32_unicode_filenames = 1; + +#ifdef HAVE_MODULES + extern void dynlib_reset_last_error (void); + dynlib_reset_last_error (); +#endif } /* For make-serial-process */ @@ -9473,6 +9579,26 @@ serial_configure (struct Lisp_Process *p, Lisp_Object contact) pset_childp (p, childp2); } +/* For make-pipe-process */ +void +register_aux_fd (int infd) +{ + child_process *cp; + + cp = new_child (); + if (!cp) + error ("Could not create child process"); + cp->fd = infd; + cp->status = STATUS_READ_ACKNOWLEDGED; + + if (fd_info[ infd ].cp != NULL) + { + error ("fd_info[fd = %d] is already in use", infd); + } + fd_info[ infd ].cp = cp; + fd_info[ infd ].hnd = (HANDLE) _get_osfhandle (infd); +} + #ifdef HAVE_GNUTLS ssize_t