X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/c1bb5ba7e161411850f704e5a2fcdc39d098948b..0e963201d03d9229bb8ac4323291d2b0119526ed:/src/w32.c diff --git a/src/w32.c b/src/w32.c index 547db0f6dd..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; } @@ -7038,6 +7137,9 @@ int (PASCAL *pfn_WSAStartup) (WORD wVersionRequired, LPWSADATA lpWSAData); void (PASCAL *pfn_WSASetLastError) (int iError); int (PASCAL *pfn_WSAGetLastError) (void); int (PASCAL *pfn_WSAEventSelect) (SOCKET s, HANDLE hEventObject, long lNetworkEvents); +int (PASCAL *pfn_WSAEnumNetworkEvents) (SOCKET s, HANDLE hEventObject, + WSANETWORKEVENTS *NetworkEvents); + HANDLE (PASCAL *pfn_WSACreateEvent) (void); int (PASCAL *pfn_WSACloseEvent) (HANDLE hEvent); int (PASCAL *pfn_socket) (int af, int type, int protocol); @@ -7123,6 +7225,7 @@ init_winsock (int load_now) LOAD_PROC (WSASetLastError); LOAD_PROC (WSAGetLastError); LOAD_PROC (WSAEventSelect); + LOAD_PROC (WSAEnumNetworkEvents); LOAD_PROC (WSACreateEvent); LOAD_PROC (WSACloseEvent); LOAD_PROC (socket); @@ -7206,6 +7309,8 @@ set_errno (void) case WSAEMFILE: errno = EMFILE; break; case WSAENAMETOOLONG: errno = ENAMETOOLONG; break; case WSAENOTEMPTY: errno = ENOTEMPTY; break; + case WSAEWOULDBLOCK: errno = EWOULDBLOCK; break; + case WSAENOTCONN: errno = ENOTCONN; break; default: errno = wsa_err; break; } } @@ -7327,7 +7432,7 @@ sys_socket (int af, int type, int protocol) if (winsock_lib == NULL) { errno = ENETDOWN; - return INVALID_SOCKET; + return -1; } check_errno (); @@ -7390,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); @@ -7473,7 +7578,17 @@ sys_connect (int s, const struct sockaddr * name, int namelen) { int rc = pfn_connect (SOCK_HANDLE (s), name, namelen); if (rc == SOCKET_ERROR) - set_errno (); + { + set_errno (); + /* If this is a non-blocking 'connect', set the bit in flags + that will tell reader_thread to wait for connection + before trying to read. */ + if (errno == EWOULDBLOCK && (fd_info[s].flags & FILE_NDELAY) != 0) + { + errno = EINPROGRESS; /* that's what process.c expects */ + fd_info[s].flags |= FILE_CONNECT; + } + } return rc; } errno = ENOTSOCK; @@ -7984,6 +8099,8 @@ _sys_read_ahead (int fd) emacs_abort (); } + if ((fd_info[fd].flags & FILE_CONNECT) != 0) + DebPrint (("_sys_read_ahead: read requested from fd %d, which waits for async connect!\n", fd)); cp->status = STATUS_READ_IN_PROGRESS; if (fd_info[fd].flags & FILE_PIPE) @@ -8105,6 +8222,60 @@ _sys_wait_accept (int fd) return cp->status; } +int +_sys_wait_connect (int fd) +{ + HANDLE hEv; + child_process * cp; + int rc; + + if (fd < 0 || fd >= MAXDESC) + return STATUS_READ_ERROR; + + cp = fd_info[fd].cp; + if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY) + return STATUS_READ_ERROR; + + cp->status = STATUS_READ_FAILED; + + hEv = pfn_WSACreateEvent (); + rc = pfn_WSAEventSelect (SOCK_HANDLE (fd), hEv, FD_CONNECT); + if (rc != SOCKET_ERROR) + { + do { + rc = WaitForSingleObject (hEv, 500); + Sleep (5); + } while (rc == WAIT_TIMEOUT + && cp->status != STATUS_READ_ERROR + && cp->char_avail); + if (rc == WAIT_OBJECT_0) + { + /* We've got an event, but it could be a successful + connection, or it could be a failure. Find out + which one is it. */ + WSANETWORKEVENTS events; + + pfn_WSAEnumNetworkEvents (SOCK_HANDLE (fd), hEv, &events); + if ((events.lNetworkEvents & FD_CONNECT) != 0 + && events.iErrorCode[FD_CONNECT_BIT]) + { + cp->status = STATUS_CONNECT_FAILED; + cp->errcode = events.iErrorCode[FD_CONNECT_BIT]; + } + else + { + cp->status = STATUS_READ_SUCCEEDED; + cp->errcode = 0; + } + } + pfn_WSAEventSelect (SOCK_HANDLE (fd), NULL, 0); + } + else + pfn_WSACloseEvent (hEv); + + return cp->status; +} + int sys_read (int fd, char * buffer, unsigned int count) { @@ -8174,6 +8345,7 @@ sys_read (int fd, char * buffer, unsigned int count) ResetEvent (cp->char_avail); case STATUS_READ_ACKNOWLEDGED: + case STATUS_CONNECT_FAILED: break; default: @@ -8239,7 +8411,29 @@ sys_read (int fd, char * buffer, unsigned int count) { if (winsock_lib == NULL) emacs_abort (); - /* do the equivalent of a non-blocking read */ + /* When a non-blocking 'connect' call fails, + wait_reading_process_output detects this by calling + 'getpeername', and then attempts to obtain the connection + error code by trying to read 1 byte from the socket. If + we try to serve that read by calling 'recv' below, the + error we get is a generic WSAENOTCONN, not the actual + connection error. So instead, we use the actual error + code stashed by '_sys_wait_connect' in cp->errcode. + Alternatively, we could have used 'getsockopt', like on + GNU/Linux, but: (a) I have no idea whether the winsock + version could hang, as it does "on some systems" (see the + comment in process.c); and (b) 'getsockopt' on Windows is + documented to clear the socket error for the entire + process, which I'm not sure is TRT; FIXME. */ + if (current_status == STATUS_CONNECT_FAILED + && (fd_info[fd].flags & FILE_CONNECT) != 0 + && cp->errcode != 0) + { + pfn_WSASetLastError (cp->errcode); + set_errno (); + return -1; + } + /* Do the equivalent of a non-blocking read. */ pfn_ioctlsocket (SOCK_HANDLE (fd), FIONREAD, &waiting); if (waiting == 0 && nchars == 0) { @@ -8253,9 +8447,9 @@ sys_read (int fd, char * buffer, unsigned int count) int res = pfn_recv (SOCK_HANDLE (fd), buffer, count, 0); if (res == SOCKET_ERROR) { - DebPrint (("sys_read.recv failed with error %d on socket %ld\n", - pfn_WSAGetLastError (), SOCK_HANDLE (fd))); set_errno (); + DebPrint (("sys_read.recv failed with error %d on socket %ld\n", + errno, SOCK_HANDLE (fd))); return -1; } nchars += res; @@ -8326,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) { @@ -8868,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)); @@ -9076,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 @@ -9185,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 */ @@ -9378,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