Converted symlink-related functions with minimal testing.
[bpt/emacs.git] / src / w32.c
CommitLineData
b46a6a83 1/* Utility and Unix shadow routines for GNU Emacs on the Microsoft Windows API.
ab422c4d 2 Copyright (C) 1994-1995, 2000-2013 Free Software Foundation, Inc.
95ed0025 3
3b7ad313
EN
4This file is part of GNU Emacs.
5
9ec0b715 6GNU Emacs is free software: you can redistribute it and/or modify
3b7ad313 7it under the terms of the GNU General Public License as published by
9ec0b715
GM
8the Free Software Foundation, either version 3 of the License, or
9(at your option) any later version.
3b7ad313
EN
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
9ec0b715 17along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
95ed0025 18
9ec0b715 19/*
95ed0025
RS
20 Geoff Voelker (voelker@cs.washington.edu) 7-29-94
21*/
279066b2
EZ
22
23#include <mingw_time.h>
76b3903d 24#include <stddef.h> /* for offsetof */
95ed0025
RS
25#include <stdlib.h>
26#include <stdio.h>
ad9e2d54 27#include <float.h> /* for DBL_EPSILON */
95ed0025 28#include <io.h>
480b0c5b 29#include <errno.h>
95ed0025
RS
30#include <fcntl.h>
31#include <ctype.h>
480b0c5b 32#include <signal.h>
b3308d2e 33#include <sys/file.h>
8f5e14c8 34#include <time.h> /* must be before nt/inc/sys/time.h, for MinGW64 */
480b0c5b 35#include <sys/time.h>
16bb7578 36#include <sys/utime.h>
7c80d5ec 37#include <math.h>
480b0c5b
GV
38
39/* must include CRT headers *before* config.h */
4838e624 40
4838e624 41#include <config.h>
6ee4509a 42#include <mbstring.h> /* for _mbspbrk, _mbslwr, _mbsrchr, ... */
4838e624 43
480b0c5b
GV
44#undef access
45#undef chdir
46#undef chmod
47#undef creat
48#undef ctime
49#undef fopen
50#undef link
51#undef mkdir
480b0c5b
GV
52#undef open
53#undef rename
54#undef rmdir
55#undef unlink
56
57#undef close
58#undef dup
59#undef dup2
60#undef pipe
61#undef read
62#undef write
95ed0025 63
d8fcc1b9
AI
64#undef strerror
65
97a93095
EZ
66#undef localtime
67
95ed0025 68#include "lisp.h"
7d9fb4de 69#include "epaths.h" /* for SHELL */
95ed0025
RS
70
71#include <pwd.h>
3d19b645 72#include <grp.h>
95ed0025 73
a18d7de6
EZ
74/* MinGW64 (_W64) defines these in its _mingw.h. */
75#if defined(__GNUC__) && !defined(_W64)
971bce75
AI
76#define _ANONYMOUS_UNION
77#define _ANONYMOUS_STRUCT
78#endif
480b0c5b 79#include <windows.h>
b8526f6e
EZ
80/* Some versions of compiler define MEMORYSTATUSEX, some don't, so we
81 use a different name to avoid compilation problems. */
82typedef struct _MEMORY_STATUS_EX {
bedf4aab
JB
83 DWORD dwLength;
84 DWORD dwMemoryLoad;
85 DWORDLONG ullTotalPhys;
86 DWORDLONG ullAvailPhys;
87 DWORDLONG ullTotalPageFile;
88 DWORDLONG ullAvailPageFile;
89 DWORDLONG ullTotalVirtual;
90 DWORDLONG ullAvailVirtual;
91 DWORDLONG ullAvailExtendedVirtual;
b8526f6e 92} MEMORY_STATUS_EX,*LPMEMORY_STATUS_EX;
7c80d5ec 93
55902276
EZ
94/* These are here so that GDB would know about these data types. This
95 allows to attach GDB to Emacs when a fatal exception is triggered
96 and Windows pops up the "application needs to be closed" dialog.
97 At that point, _gnu_exception_handler, the top-level exception
98 handler installed by the MinGW startup code, is somewhere on the
99 call-stack of the main thread, so going to that call frame and
100 looking at the argument to _gnu_exception_handler, which is a
101 PEXCEPTION_POINTERS pointer, can reveal the exception code
102 (excptr->ExceptionRecord->ExceptionCode) and the address where the
103 exception happened (excptr->ExceptionRecord->ExceptionAddress), as
104 well as some additional information specific to the exception. */
105PEXCEPTION_POINTERS excptr;
106PEXCEPTION_RECORD excprec;
107PCONTEXT ctxrec;
108
634d3003 109#include <lmcons.h>
2d5324c5 110#include <shlobj.h>
00b3b7b3 111
7c80d5ec
EZ
112#include <tlhelp32.h>
113#include <psapi.h>
a6fc3b5c 114#ifndef _MSC_VER
69e847be 115#include <w32api.h>
a6fc3b5c 116#endif
a18d7de6 117#if _WIN32_WINNT < 0x0500
5e617bc2 118#if !defined (__MINGW32__) || __W32API_MAJOR_VERSION < 3 || (__W32API_MAJOR_VERSION == 3 && __W32API_MINOR_VERSION < 15)
7c80d5ec 119/* This either is not in psapi.h or guarded by higher value of
a6d3e72e 120 _WIN32_WINNT than what we use. w32api supplied with MinGW 3.15
69e847be 121 defines it in psapi.h */
7c80d5ec 122typedef struct _PROCESS_MEMORY_COUNTERS_EX {
cb576b5c
FP
123 DWORD cb;
124 DWORD PageFaultCount;
125 SIZE_T PeakWorkingSetSize;
126 SIZE_T WorkingSetSize;
127 SIZE_T QuotaPeakPagedPoolUsage;
128 SIZE_T QuotaPagedPoolUsage;
129 SIZE_T QuotaPeakNonPagedPoolUsage;
130 SIZE_T QuotaNonPagedPoolUsage;
131 SIZE_T PagefileUsage;
132 SIZE_T PeakPagefileUsage;
133 SIZE_T PrivateUsage;
7c80d5ec 134} PROCESS_MEMORY_COUNTERS_EX,*PPROCESS_MEMORY_COUNTERS_EX;
69e847be 135#endif
a18d7de6 136#endif
7c80d5ec 137
6dad7178
EZ
138#include <winioctl.h>
139#include <aclapi.h>
66447e07
EZ
140#include <sddl.h>
141
142#include <sys/acl.h>
143
144/* This is not in MinGW's sddl.h (but they are in MSVC headers), so we
145 define them by hand if not already defined. */
146#ifndef SDDL_REVISION_1
147#define SDDL_REVISION_1 1
148#endif /* SDDL_REVISION_1 */
6dad7178 149
c6e72e17
EZ
150#if defined(_MSC_VER) || defined(_W64)
151/* MSVC and MinGW64 don't provide the definition of
152 REPARSE_DATA_BUFFER and the associated macros, except on ntifs.h,
153 which cannot be included because it triggers conflicts with other
154 Windows API headers. So we define it here by hand. */
6dad7178
EZ
155
156typedef struct _REPARSE_DATA_BUFFER {
157 ULONG ReparseTag;
158 USHORT ReparseDataLength;
159 USHORT Reserved;
160 union {
161 struct {
162 USHORT SubstituteNameOffset;
163 USHORT SubstituteNameLength;
164 USHORT PrintNameOffset;
165 USHORT PrintNameLength;
166 ULONG Flags;
167 WCHAR PathBuffer[1];
168 } SymbolicLinkReparseBuffer;
169 struct {
170 USHORT SubstituteNameOffset;
171 USHORT SubstituteNameLength;
172 USHORT PrintNameOffset;
173 USHORT PrintNameLength;
174 WCHAR PathBuffer[1];
175 } MountPointReparseBuffer;
176 struct {
177 UCHAR DataBuffer[1];
178 } GenericReparseBuffer;
179 } DUMMYUNIONNAME;
180} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
181
c7b36b95 182#ifndef FILE_DEVICE_FILE_SYSTEM
88c4a13c 183#define FILE_DEVICE_FILE_SYSTEM 9
c7b36b95
FP
184#endif
185#ifndef METHOD_BUFFERED
88c4a13c 186#define METHOD_BUFFERED 0
c7b36b95
FP
187#endif
188#ifndef FILE_ANY_ACCESS
88c4a13c 189#define FILE_ANY_ACCESS 0x00000000
c7b36b95
FP
190#endif
191#ifndef CTL_CODE
88c4a13c 192#define CTL_CODE(t,f,m,a) (((t)<<16)|((a)<<14)|((f)<<2)|(m))
c7b36b95 193#endif
c86f791f
EZ
194/* MinGW64 defines FSCTL_GET_REPARSE_POINT on winioctl.h. */
195#ifndef FSCTL_GET_REPARSE_POINT
88c4a13c
EZ
196#define FSCTL_GET_REPARSE_POINT \
197 CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
6dad7178 198#endif
c86f791f 199#endif
6dad7178 200
7d701334 201/* TCP connection support. */
480b0c5b
GV
202#include <sys/socket.h>
203#undef socket
204#undef bind
205#undef connect
206#undef htons
207#undef ntohs
208#undef inet_addr
209#undef gethostname
210#undef gethostbyname
211#undef getservbyname
ecd270eb 212#undef getpeername
380961a6 213#undef shutdown
962955c5
JR
214#undef setsockopt
215#undef listen
216#undef getsockname
217#undef accept
218#undef recvfrom
219#undef sendto
00b3b7b3 220
3dffe395
EZ
221#include <iphlpapi.h> /* should be after winsock2.h */
222
489f9371 223#include "w32.h"
95ef7787 224#include <dirent.h>
501199a3 225#include "w32common.h"
489f9371 226#include "w32heap.h"
a68089e4 227#include "w32select.h"
253574a6 228#include "systime.h"
f481eb31 229#include "dispextern.h" /* for xstrcasecmp */
7c80d5ec 230#include "coding.h" /* for Vlocale_coding_system */
253574a6 231
973f782d
EZ
232#include "careadlinkat.h"
233#include "allocator.h"
234
1eb8fd91 235/* For serial_configure and serial_open. */
d888760c 236#include "process.h"
d888760c 237
2d5324c5
JR
238typedef HRESULT (WINAPI * ShGetFolderPath_fn)
239 (IN HWND, IN int, IN HANDLE, IN DWORD, OUT char *);
240
0898ca10
JB
241Lisp_Object QCloaded_from;
242
b56ceb92 243void globals_of_w32 (void);
8aaaec6b 244static DWORD get_rid (PSID);
6dad7178
EZ
245static int is_symlink (const char *);
246static char * chase_symlinks (const char *);
247static int enable_privilege (LPCTSTR, BOOL, TOKEN_PRIVILEGES *);
248static int restore_privilege (TOKEN_PRIVILEGES *);
249static BOOL WINAPI revert_to_self (void);
9785d95b 250
b1846422 251static int sys_access (const char *, int);
a68089e4
EZ
252extern void *e_malloc (size_t);
253extern int sys_select (int, SELECT_TYPE *, SELECT_TYPE *, SELECT_TYPE *,
43aac990 254 struct timespec *, void *);
1d442672
EZ
255extern int sys_dup (int);
256
a68089e4
EZ
257
258
18e070ac 259\f
9d95a291
EZ
260/* Initialization states.
261
262 WARNING: If you add any more such variables for additional APIs,
263 you MUST add initialization for them to globals_of_w32
264 below. This is because these variables might get set
265 to non-NULL values during dumping, but the dumped Emacs
266 cannot reuse those values, because it could be run on a
267 different version of the OS, where API addresses are
268 different. */
9785d95b
BK
269static BOOL g_b_init_is_windows_9x;
270static BOOL g_b_init_open_process_token;
271static BOOL g_b_init_get_token_information;
272static BOOL g_b_init_lookup_account_sid;
c617afce
EZ
273static BOOL g_b_init_get_sid_sub_authority;
274static BOOL g_b_init_get_sid_sub_authority_count;
6dad7178 275static BOOL g_b_init_get_security_info;
df87c56c
EZ
276static BOOL g_b_init_get_file_security_w;
277static BOOL g_b_init_get_file_security_a;
8aaaec6b
EZ
278static BOOL g_b_init_get_security_descriptor_owner;
279static BOOL g_b_init_get_security_descriptor_group;
280static BOOL g_b_init_is_valid_sid;
7c80d5ec
EZ
281static BOOL g_b_init_create_toolhelp32_snapshot;
282static BOOL g_b_init_process32_first;
283static BOOL g_b_init_process32_next;
284static BOOL g_b_init_open_thread_token;
285static BOOL g_b_init_impersonate_self;
286static BOOL g_b_init_revert_to_self;
287static BOOL g_b_init_get_process_memory_info;
288static BOOL g_b_init_get_process_working_set_size;
289static BOOL g_b_init_global_memory_status;
290static BOOL g_b_init_global_memory_status_ex;
f8b35b24
EZ
291static BOOL g_b_init_get_length_sid;
292static BOOL g_b_init_equal_sid;
293static BOOL g_b_init_copy_sid;
ad9e2d54
EZ
294static BOOL g_b_init_get_native_system_info;
295static BOOL g_b_init_get_system_times;
eeccd06f
EZ
296static BOOL g_b_init_create_symbolic_link_w;
297static BOOL g_b_init_create_symbolic_link_a;
66447e07
EZ
298static BOOL g_b_init_get_security_descriptor_dacl;
299static BOOL g_b_init_convert_sd_to_sddl;
300static BOOL g_b_init_convert_sddl_to_sd;
301static BOOL g_b_init_is_valid_security_descriptor;
302static BOOL g_b_init_set_file_security;
3dffe395 303static BOOL g_b_init_get_adapters_info;
9785d95b 304
f60ae425
BK
305/*
306 BEGIN: Wrapper functions around OpenProcessToken
307 and other functions in advapi32.dll that are only
308 supported in Windows NT / 2k / XP
309*/
310 /* ** Function pointer typedefs ** */
311typedef BOOL (WINAPI * OpenProcessToken_Proc) (
312 HANDLE ProcessHandle,
313 DWORD DesiredAccess,
314 PHANDLE TokenHandle);
315typedef BOOL (WINAPI * GetTokenInformation_Proc) (
316 HANDLE TokenHandle,
317 TOKEN_INFORMATION_CLASS TokenInformationClass,
318 LPVOID TokenInformation,
319 DWORD TokenInformationLength,
320 PDWORD ReturnLength);
74258518
JR
321typedef BOOL (WINAPI * GetProcessTimes_Proc) (
322 HANDLE process_handle,
323 LPFILETIME creation_time,
324 LPFILETIME exit_time,
325 LPFILETIME kernel_time,
326 LPFILETIME user_time);
327
328GetProcessTimes_Proc get_process_times_fn = NULL;
329
f60ae425
BK
330#ifdef _UNICODE
331const char * const LookupAccountSid_Name = "LookupAccountSidW";
332#else
333const char * const LookupAccountSid_Name = "LookupAccountSidA";
334#endif
335typedef BOOL (WINAPI * LookupAccountSid_Proc) (
336 LPCTSTR lpSystemName,
337 PSID Sid,
338 LPTSTR Name,
339 LPDWORD cbName,
340 LPTSTR DomainName,
341 LPDWORD cbDomainName,
342 PSID_NAME_USE peUse);
c617afce
EZ
343typedef PDWORD (WINAPI * GetSidSubAuthority_Proc) (
344 PSID pSid,
345 DWORD n);
346typedef PUCHAR (WINAPI * GetSidSubAuthorityCount_Proc) (
347 PSID pSid);
6dad7178
EZ
348typedef DWORD (WINAPI * GetSecurityInfo_Proc) (
349 HANDLE handle,
350 SE_OBJECT_TYPE ObjectType,
351 SECURITY_INFORMATION SecurityInfo,
352 PSID *ppsidOwner,
353 PSID *ppsidGroup,
354 PACL *ppDacl,
355 PACL *ppSacl,
356 PSECURITY_DESCRIPTOR *ppSecurityDescriptor);
df87c56c
EZ
357typedef BOOL (WINAPI * GetFileSecurityW_Proc) (
358 LPCWSTR lpFileName,
359 SECURITY_INFORMATION RequestedInformation,
360 PSECURITY_DESCRIPTOR pSecurityDescriptor,
361 DWORD nLength,
362 LPDWORD lpnLengthNeeded);
363typedef BOOL (WINAPI * GetFileSecurityA_Proc) (
364 LPCSTR lpFileName,
8aaaec6b
EZ
365 SECURITY_INFORMATION RequestedInformation,
366 PSECURITY_DESCRIPTOR pSecurityDescriptor,
367 DWORD nLength,
368 LPDWORD lpnLengthNeeded);
66447e07
EZ
369typedef BOOL (WINAPI *SetFileSecurity_Proc) (
370 LPCTSTR lpFileName,
371 SECURITY_INFORMATION SecurityInformation,
372 PSECURITY_DESCRIPTOR pSecurityDescriptor);
8aaaec6b
EZ
373typedef BOOL (WINAPI * GetSecurityDescriptorOwner_Proc) (
374 PSECURITY_DESCRIPTOR pSecurityDescriptor,
375 PSID *pOwner,
376 LPBOOL lpbOwnerDefaulted);
377typedef BOOL (WINAPI * GetSecurityDescriptorGroup_Proc) (
378 PSECURITY_DESCRIPTOR pSecurityDescriptor,
379 PSID *pGroup,
380 LPBOOL lpbGroupDefaulted);
66447e07
EZ
381typedef BOOL (WINAPI *GetSecurityDescriptorDacl_Proc) (
382 PSECURITY_DESCRIPTOR pSecurityDescriptor,
383 LPBOOL lpbDaclPresent,
384 PACL *pDacl,
385 LPBOOL lpbDaclDefaulted);
8aaaec6b
EZ
386typedef BOOL (WINAPI * IsValidSid_Proc) (
387 PSID sid);
7c80d5ec
EZ
388typedef HANDLE (WINAPI * CreateToolhelp32Snapshot_Proc) (
389 DWORD dwFlags,
390 DWORD th32ProcessID);
391typedef BOOL (WINAPI * Process32First_Proc) (
392 HANDLE hSnapshot,
393 LPPROCESSENTRY32 lppe);
394typedef BOOL (WINAPI * Process32Next_Proc) (
395 HANDLE hSnapshot,
396 LPPROCESSENTRY32 lppe);
397typedef BOOL (WINAPI * OpenThreadToken_Proc) (
398 HANDLE ThreadHandle,
399 DWORD DesiredAccess,
400 BOOL OpenAsSelf,
401 PHANDLE TokenHandle);
402typedef BOOL (WINAPI * ImpersonateSelf_Proc) (
403 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel);
404typedef BOOL (WINAPI * RevertToSelf_Proc) (void);
405typedef BOOL (WINAPI * GetProcessMemoryInfo_Proc) (
406 HANDLE Process,
407 PPROCESS_MEMORY_COUNTERS ppsmemCounters,
408 DWORD cb);
409typedef BOOL (WINAPI * GetProcessWorkingSetSize_Proc) (
410 HANDLE hProcess,
cb576b5c
FP
411 PSIZE_T lpMinimumWorkingSetSize,
412 PSIZE_T lpMaximumWorkingSetSize);
7c80d5ec
EZ
413typedef BOOL (WINAPI * GlobalMemoryStatus_Proc) (
414 LPMEMORYSTATUS lpBuffer);
415typedef BOOL (WINAPI * GlobalMemoryStatusEx_Proc) (
b8526f6e 416 LPMEMORY_STATUS_EX lpBuffer);
f8b35b24
EZ
417typedef BOOL (WINAPI * CopySid_Proc) (
418 DWORD nDestinationSidLength,
419 PSID pDestinationSid,
420 PSID pSourceSid);
421typedef BOOL (WINAPI * EqualSid_Proc) (
422 PSID pSid1,
423 PSID pSid2);
424typedef DWORD (WINAPI * GetLengthSid_Proc) (
425 PSID pSid);
ad9e2d54
EZ
426typedef void (WINAPI * GetNativeSystemInfo_Proc) (
427 LPSYSTEM_INFO lpSystemInfo);
428typedef BOOL (WINAPI * GetSystemTimes_Proc) (
429 LPFILETIME lpIdleTime,
430 LPFILETIME lpKernelTime,
431 LPFILETIME lpUserTime);
eeccd06f
EZ
432typedef BOOLEAN (WINAPI *CreateSymbolicLinkW_Proc) (
433 LPCWSTR lpSymlinkFileName,
434 LPCWSTR lpTargetFileName,
435 DWORD dwFlags);
436typedef BOOLEAN (WINAPI *CreateSymbolicLinkA_Proc) (
437 LPCSTR lpSymlinkFileName,
438 LPCSTR lpTargetFileName,
6dad7178 439 DWORD dwFlags);
66447e07
EZ
440typedef BOOL (WINAPI *ConvertStringSecurityDescriptorToSecurityDescriptor_Proc) (
441 LPCTSTR StringSecurityDescriptor,
442 DWORD StringSDRevision,
443 PSECURITY_DESCRIPTOR *SecurityDescriptor,
444 PULONG SecurityDescriptorSize);
445typedef BOOL (WINAPI *ConvertSecurityDescriptorToStringSecurityDescriptor_Proc) (
446 PSECURITY_DESCRIPTOR SecurityDescriptor,
447 DWORD RequestedStringSDRevision,
448 SECURITY_INFORMATION SecurityInformation,
449 LPTSTR *StringSecurityDescriptor,
450 PULONG StringSecurityDescriptorLen);
451typedef BOOL (WINAPI *IsValidSecurityDescriptor_Proc) (PSECURITY_DESCRIPTOR);
3dffe395
EZ
452typedef DWORD (WINAPI *GetAdaptersInfo_Proc) (
453 PIP_ADAPTER_INFO pAdapterInfo,
454 PULONG pOutBufLen);
f8b35b24 455
f60ae425 456 /* ** A utility function ** */
9bfb11f9 457static BOOL
b56ceb92 458is_windows_9x (void)
f60ae425 459{
bedf4aab 460 static BOOL s_b_ret = 0;
f60ae425 461 OSVERSIONINFO os_ver;
9785d95b 462 if (g_b_init_is_windows_9x == 0)
f60ae425 463 {
9785d95b 464 g_b_init_is_windows_9x = 1;
ed3751c8
JB
465 ZeroMemory (&os_ver, sizeof (OSVERSIONINFO));
466 os_ver.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
9785d95b
BK
467 if (GetVersionEx (&os_ver))
468 {
469 s_b_ret = (os_ver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
470 }
f60ae425 471 }
9785d95b 472 return s_b_ret;
f60ae425
BK
473}
474
d35af63c
PE
475static Lisp_Object ltime (ULONGLONG);
476
74258518 477/* Get total user and system times for get-internal-run-time.
d35af63c 478 Returns a list of integers if the times are provided by the OS
74258518
JR
479 (NT derivatives), otherwise it returns the result of current-time. */
480Lisp_Object
b56ceb92 481w32_get_internal_run_time (void)
74258518
JR
482{
483 if (get_process_times_fn)
484 {
485 FILETIME create, exit, kernel, user;
ed3751c8 486 HANDLE proc = GetCurrentProcess ();
74258518
JR
487 if ((*get_process_times_fn) (proc, &create, &exit, &kernel, &user))
488 {
489 LARGE_INTEGER user_int, kernel_int, total;
74258518
JR
490 user_int.LowPart = user.dwLowDateTime;
491 user_int.HighPart = user.dwHighDateTime;
492 kernel_int.LowPart = kernel.dwLowDateTime;
493 kernel_int.HighPart = kernel.dwHighDateTime;
494 total.QuadPart = user_int.QuadPart + kernel_int.QuadPart;
d35af63c 495 return ltime (total.QuadPart);
74258518
JR
496 }
497 }
498
499 return Fcurrent_time ();
500}
501
f60ae425
BK
502 /* ** The wrapper functions ** */
503
bedf4aab
JB
504static BOOL WINAPI
505open_process_token (HANDLE ProcessHandle,
506 DWORD DesiredAccess,
507 PHANDLE TokenHandle)
f60ae425 508{
9785d95b 509 static OpenProcessToken_Proc s_pfn_Open_Process_Token = NULL;
f60ae425
BK
510 HMODULE hm_advapi32 = NULL;
511 if (is_windows_9x () == TRUE)
512 {
513 return FALSE;
514 }
9785d95b
BK
515 if (g_b_init_open_process_token == 0)
516 {
517 g_b_init_open_process_token = 1;
518 hm_advapi32 = LoadLibrary ("Advapi32.dll");
519 s_pfn_Open_Process_Token =
520 (OpenProcessToken_Proc) GetProcAddress (hm_advapi32, "OpenProcessToken");
521 }
522 if (s_pfn_Open_Process_Token == NULL)
f60ae425
BK
523 {
524 return FALSE;
525 }
526 return (
9785d95b 527 s_pfn_Open_Process_Token (
f60ae425
BK
528 ProcessHandle,
529 DesiredAccess,
530 TokenHandle)
531 );
532}
533
bedf4aab
JB
534static BOOL WINAPI
535get_token_information (HANDLE TokenHandle,
536 TOKEN_INFORMATION_CLASS TokenInformationClass,
537 LPVOID TokenInformation,
538 DWORD TokenInformationLength,
539 PDWORD ReturnLength)
f60ae425 540{
9785d95b 541 static GetTokenInformation_Proc s_pfn_Get_Token_Information = NULL;
f60ae425
BK
542 HMODULE hm_advapi32 = NULL;
543 if (is_windows_9x () == TRUE)
544 {
545 return FALSE;
546 }
9785d95b
BK
547 if (g_b_init_get_token_information == 0)
548 {
549 g_b_init_get_token_information = 1;
550 hm_advapi32 = LoadLibrary ("Advapi32.dll");
551 s_pfn_Get_Token_Information =
552 (GetTokenInformation_Proc) GetProcAddress (hm_advapi32, "GetTokenInformation");
553 }
554 if (s_pfn_Get_Token_Information == NULL)
f60ae425
BK
555 {
556 return FALSE;
557 }
558 return (
9785d95b 559 s_pfn_Get_Token_Information (
f60ae425
BK
560 TokenHandle,
561 TokenInformationClass,
562 TokenInformation,
563 TokenInformationLength,
564 ReturnLength)
565 );
566}
567
bedf4aab
JB
568static BOOL WINAPI
569lookup_account_sid (LPCTSTR lpSystemName,
570 PSID Sid,
571 LPTSTR Name,
572 LPDWORD cbName,
573 LPTSTR DomainName,
574 LPDWORD cbDomainName,
575 PSID_NAME_USE peUse)
f60ae425 576{
9785d95b 577 static LookupAccountSid_Proc s_pfn_Lookup_Account_Sid = NULL;
f60ae425
BK
578 HMODULE hm_advapi32 = NULL;
579 if (is_windows_9x () == TRUE)
580 {
581 return FALSE;
582 }
9785d95b
BK
583 if (g_b_init_lookup_account_sid == 0)
584 {
585 g_b_init_lookup_account_sid = 1;
586 hm_advapi32 = LoadLibrary ("Advapi32.dll");
587 s_pfn_Lookup_Account_Sid =
588 (LookupAccountSid_Proc) GetProcAddress (hm_advapi32, LookupAccountSid_Name);
589 }
590 if (s_pfn_Lookup_Account_Sid == NULL)
f60ae425
BK
591 {
592 return FALSE;
593 }
594 return (
9785d95b 595 s_pfn_Lookup_Account_Sid (
f60ae425
BK
596 lpSystemName,
597 Sid,
598 Name,
599 cbName,
600 DomainName,
601 cbDomainName,
602 peUse)
603 );
604}
605
bedf4aab
JB
606static PDWORD WINAPI
607get_sid_sub_authority (PSID pSid, DWORD n)
c617afce
EZ
608{
609 static GetSidSubAuthority_Proc s_pfn_Get_Sid_Sub_Authority = NULL;
6811b9f4 610 static DWORD zero = 0U;
c617afce
EZ
611 HMODULE hm_advapi32 = NULL;
612 if (is_windows_9x () == TRUE)
613 {
6811b9f4 614 return &zero;
c617afce
EZ
615 }
616 if (g_b_init_get_sid_sub_authority == 0)
617 {
618 g_b_init_get_sid_sub_authority = 1;
619 hm_advapi32 = LoadLibrary ("Advapi32.dll");
620 s_pfn_Get_Sid_Sub_Authority =
621 (GetSidSubAuthority_Proc) GetProcAddress (
622 hm_advapi32, "GetSidSubAuthority");
623 }
624 if (s_pfn_Get_Sid_Sub_Authority == NULL)
625 {
6811b9f4 626 return &zero;
c617afce
EZ
627 }
628 return (s_pfn_Get_Sid_Sub_Authority (pSid, n));
629}
630
bedf4aab
JB
631static PUCHAR WINAPI
632get_sid_sub_authority_count (PSID pSid)
c617afce
EZ
633{
634 static GetSidSubAuthorityCount_Proc s_pfn_Get_Sid_Sub_Authority_Count = NULL;
6811b9f4 635 static UCHAR zero = 0U;
c617afce
EZ
636 HMODULE hm_advapi32 = NULL;
637 if (is_windows_9x () == TRUE)
638 {
6811b9f4 639 return &zero;
c617afce
EZ
640 }
641 if (g_b_init_get_sid_sub_authority_count == 0)
642 {
643 g_b_init_get_sid_sub_authority_count = 1;
644 hm_advapi32 = LoadLibrary ("Advapi32.dll");
645 s_pfn_Get_Sid_Sub_Authority_Count =
646 (GetSidSubAuthorityCount_Proc) GetProcAddress (
647 hm_advapi32, "GetSidSubAuthorityCount");
648 }
649 if (s_pfn_Get_Sid_Sub_Authority_Count == NULL)
650 {
6811b9f4 651 return &zero;
c617afce
EZ
652 }
653 return (s_pfn_Get_Sid_Sub_Authority_Count (pSid));
654}
655
6dad7178
EZ
656static DWORD WINAPI
657get_security_info (HANDLE handle,
658 SE_OBJECT_TYPE ObjectType,
659 SECURITY_INFORMATION SecurityInfo,
660 PSID *ppsidOwner,
661 PSID *ppsidGroup,
662 PACL *ppDacl,
663 PACL *ppSacl,
664 PSECURITY_DESCRIPTOR *ppSecurityDescriptor)
665{
666 static GetSecurityInfo_Proc s_pfn_Get_Security_Info = NULL;
667 HMODULE hm_advapi32 = NULL;
668 if (is_windows_9x () == TRUE)
669 {
670 return FALSE;
671 }
672 if (g_b_init_get_security_info == 0)
673 {
674 g_b_init_get_security_info = 1;
675 hm_advapi32 = LoadLibrary ("Advapi32.dll");
676 s_pfn_Get_Security_Info =
677 (GetSecurityInfo_Proc) GetProcAddress (
678 hm_advapi32, "GetSecurityInfo");
679 }
680 if (s_pfn_Get_Security_Info == NULL)
681 {
682 return FALSE;
683 }
684 return (s_pfn_Get_Security_Info (handle, ObjectType, SecurityInfo,
685 ppsidOwner, ppsidGroup, ppDacl, ppSacl,
686 ppSecurityDescriptor));
687}
688
df87c56c
EZ
689static int filename_to_ansi (const char *, char *);
690static int filename_to_utf16 (const char *, wchar_t *);
691
bedf4aab 692static BOOL WINAPI
df87c56c 693get_file_security (const char *lpFileName,
bedf4aab
JB
694 SECURITY_INFORMATION RequestedInformation,
695 PSECURITY_DESCRIPTOR pSecurityDescriptor,
696 DWORD nLength,
697 LPDWORD lpnLengthNeeded)
8aaaec6b 698{
df87c56c
EZ
699 static GetFileSecurityA_Proc s_pfn_Get_File_SecurityA = NULL;
700 static GetFileSecurityW_Proc s_pfn_Get_File_SecurityW = NULL;
8aaaec6b
EZ
701 HMODULE hm_advapi32 = NULL;
702 if (is_windows_9x () == TRUE)
703 {
66447e07 704 errno = ENOTSUP;
8aaaec6b
EZ
705 return FALSE;
706 }
df87c56c 707 if (w32_unicode_filenames)
8aaaec6b 708 {
df87c56c
EZ
709 wchar_t filename_w[MAX_PATH];
710
711 if (g_b_init_get_file_security_w == 0)
712 {
713 g_b_init_get_file_security_w = 1;
714 hm_advapi32 = LoadLibrary ("Advapi32.dll");
715 s_pfn_Get_File_SecurityW =
716 (GetFileSecurityW_Proc) GetProcAddress (hm_advapi32,
717 "GetFileSecurityW");
718 }
719 if (s_pfn_Get_File_SecurityW == NULL)
720 {
721 errno = ENOTSUP;
722 return FALSE;
723 }
724 filename_to_utf16 (lpFileName, filename_w);
725 return (s_pfn_Get_File_SecurityW (filename_w, RequestedInformation,
726 pSecurityDescriptor, nLength,
727 lpnLengthNeeded));
8aaaec6b 728 }
df87c56c 729 else
8aaaec6b 730 {
df87c56c
EZ
731 char filename_a[MAX_PATH];
732
733 if (g_b_init_get_file_security_a == 0)
734 {
735 g_b_init_get_file_security_a = 1;
736 hm_advapi32 = LoadLibrary ("Advapi32.dll");
737 s_pfn_Get_File_SecurityA =
738 (GetFileSecurityA_Proc) GetProcAddress (hm_advapi32,
739 "GetFileSecurityA");
740 }
741 if (s_pfn_Get_File_SecurityA == NULL)
742 {
743 errno = ENOTSUP;
744 return FALSE;
745 }
746 filename_to_ansi (lpFileName, filename_a);
747 return (s_pfn_Get_File_SecurityA (filename_a, RequestedInformation,
748 pSecurityDescriptor, nLength,
749 lpnLengthNeeded));
8aaaec6b 750 }
8aaaec6b
EZ
751}
752
66447e07
EZ
753static BOOL WINAPI
754set_file_security (LPCTSTR lpFileName,
755 SECURITY_INFORMATION SecurityInformation,
756 PSECURITY_DESCRIPTOR pSecurityDescriptor)
757{
758 static SetFileSecurity_Proc s_pfn_Set_File_Security = NULL;
759 HMODULE hm_advapi32 = NULL;
760 if (is_windows_9x () == TRUE)
761 {
762 errno = ENOTSUP;
763 return FALSE;
764 }
765 if (g_b_init_set_file_security == 0)
766 {
767 g_b_init_set_file_security = 1;
768 hm_advapi32 = LoadLibrary ("Advapi32.dll");
769 s_pfn_Set_File_Security =
770 (SetFileSecurity_Proc) GetProcAddress (
df87c56c 771 hm_advapi32, "SetFileSecurityA");
66447e07
EZ
772 }
773 if (s_pfn_Set_File_Security == NULL)
774 {
775 errno = ENOTSUP;
776 return FALSE;
777 }
778 return (s_pfn_Set_File_Security (lpFileName, SecurityInformation,
779 pSecurityDescriptor));
780}
781
bedf4aab
JB
782static BOOL WINAPI
783get_security_descriptor_owner (PSECURITY_DESCRIPTOR pSecurityDescriptor,
784 PSID *pOwner,
785 LPBOOL lpbOwnerDefaulted)
8aaaec6b
EZ
786{
787 static GetSecurityDescriptorOwner_Proc s_pfn_Get_Security_Descriptor_Owner = NULL;
788 HMODULE hm_advapi32 = NULL;
789 if (is_windows_9x () == TRUE)
790 {
66447e07 791 errno = ENOTSUP;
8aaaec6b
EZ
792 return FALSE;
793 }
794 if (g_b_init_get_security_descriptor_owner == 0)
795 {
796 g_b_init_get_security_descriptor_owner = 1;
797 hm_advapi32 = LoadLibrary ("Advapi32.dll");
798 s_pfn_Get_Security_Descriptor_Owner =
799 (GetSecurityDescriptorOwner_Proc) GetProcAddress (
800 hm_advapi32, "GetSecurityDescriptorOwner");
801 }
802 if (s_pfn_Get_Security_Descriptor_Owner == NULL)
803 {
66447e07 804 errno = ENOTSUP;
8aaaec6b
EZ
805 return FALSE;
806 }
807 return (s_pfn_Get_Security_Descriptor_Owner (pSecurityDescriptor, pOwner,
808 lpbOwnerDefaulted));
809}
810
bedf4aab
JB
811static BOOL WINAPI
812get_security_descriptor_group (PSECURITY_DESCRIPTOR pSecurityDescriptor,
813 PSID *pGroup,
814 LPBOOL lpbGroupDefaulted)
8aaaec6b
EZ
815{
816 static GetSecurityDescriptorGroup_Proc s_pfn_Get_Security_Descriptor_Group = NULL;
817 HMODULE hm_advapi32 = NULL;
818 if (is_windows_9x () == TRUE)
819 {
66447e07 820 errno = ENOTSUP;
8aaaec6b
EZ
821 return FALSE;
822 }
823 if (g_b_init_get_security_descriptor_group == 0)
824 {
825 g_b_init_get_security_descriptor_group = 1;
826 hm_advapi32 = LoadLibrary ("Advapi32.dll");
827 s_pfn_Get_Security_Descriptor_Group =
828 (GetSecurityDescriptorGroup_Proc) GetProcAddress (
829 hm_advapi32, "GetSecurityDescriptorGroup");
830 }
831 if (s_pfn_Get_Security_Descriptor_Group == NULL)
832 {
66447e07 833 errno = ENOTSUP;
8aaaec6b
EZ
834 return FALSE;
835 }
836 return (s_pfn_Get_Security_Descriptor_Group (pSecurityDescriptor, pGroup,
837 lpbGroupDefaulted));
838}
839
66447e07
EZ
840static BOOL WINAPI
841get_security_descriptor_dacl (PSECURITY_DESCRIPTOR pSecurityDescriptor,
842 LPBOOL lpbDaclPresent,
843 PACL *pDacl,
844 LPBOOL lpbDaclDefaulted)
845{
846 static GetSecurityDescriptorDacl_Proc s_pfn_Get_Security_Descriptor_Dacl = NULL;
847 HMODULE hm_advapi32 = NULL;
848 if (is_windows_9x () == TRUE)
849 {
850 errno = ENOTSUP;
851 return FALSE;
852 }
853 if (g_b_init_get_security_descriptor_dacl == 0)
854 {
855 g_b_init_get_security_descriptor_dacl = 1;
856 hm_advapi32 = LoadLibrary ("Advapi32.dll");
857 s_pfn_Get_Security_Descriptor_Dacl =
858 (GetSecurityDescriptorDacl_Proc) GetProcAddress (
859 hm_advapi32, "GetSecurityDescriptorDacl");
860 }
861 if (s_pfn_Get_Security_Descriptor_Dacl == NULL)
862 {
863 errno = ENOTSUP;
864 return FALSE;
865 }
866 return (s_pfn_Get_Security_Descriptor_Dacl (pSecurityDescriptor,
867 lpbDaclPresent, pDacl,
868 lpbDaclDefaulted));
869}
870
bedf4aab
JB
871static BOOL WINAPI
872is_valid_sid (PSID sid)
8aaaec6b
EZ
873{
874 static IsValidSid_Proc s_pfn_Is_Valid_Sid = NULL;
875 HMODULE hm_advapi32 = NULL;
876 if (is_windows_9x () == TRUE)
877 {
878 return FALSE;
879 }
880 if (g_b_init_is_valid_sid == 0)
881 {
882 g_b_init_is_valid_sid = 1;
883 hm_advapi32 = LoadLibrary ("Advapi32.dll");
884 s_pfn_Is_Valid_Sid =
885 (IsValidSid_Proc) GetProcAddress (
886 hm_advapi32, "IsValidSid");
887 }
888 if (s_pfn_Is_Valid_Sid == NULL)
889 {
890 return FALSE;
891 }
892 return (s_pfn_Is_Valid_Sid (sid));
893}
894
bedf4aab
JB
895static BOOL WINAPI
896equal_sid (PSID sid1, PSID sid2)
f8b35b24
EZ
897{
898 static EqualSid_Proc s_pfn_Equal_Sid = NULL;
899 HMODULE hm_advapi32 = NULL;
900 if (is_windows_9x () == TRUE)
901 {
902 return FALSE;
903 }
904 if (g_b_init_equal_sid == 0)
905 {
906 g_b_init_equal_sid = 1;
907 hm_advapi32 = LoadLibrary ("Advapi32.dll");
908 s_pfn_Equal_Sid =
909 (EqualSid_Proc) GetProcAddress (
910 hm_advapi32, "EqualSid");
911 }
912 if (s_pfn_Equal_Sid == NULL)
913 {
914 return FALSE;
915 }
916 return (s_pfn_Equal_Sid (sid1, sid2));
917}
918
bedf4aab
JB
919static DWORD WINAPI
920get_length_sid (PSID sid)
f8b35b24
EZ
921{
922 static GetLengthSid_Proc s_pfn_Get_Length_Sid = NULL;
923 HMODULE hm_advapi32 = NULL;
924 if (is_windows_9x () == TRUE)
925 {
926 return 0;
927 }
928 if (g_b_init_get_length_sid == 0)
929 {
930 g_b_init_get_length_sid = 1;
931 hm_advapi32 = LoadLibrary ("Advapi32.dll");
932 s_pfn_Get_Length_Sid =
933 (GetLengthSid_Proc) GetProcAddress (
934 hm_advapi32, "GetLengthSid");
935 }
936 if (s_pfn_Get_Length_Sid == NULL)
937 {
938 return 0;
939 }
940 return (s_pfn_Get_Length_Sid (sid));
941}
942
bedf4aab
JB
943static BOOL WINAPI
944copy_sid (DWORD destlen, PSID dest, PSID src)
f8b35b24
EZ
945{
946 static CopySid_Proc s_pfn_Copy_Sid = NULL;
947 HMODULE hm_advapi32 = NULL;
948 if (is_windows_9x () == TRUE)
949 {
950 return FALSE;
951 }
952 if (g_b_init_copy_sid == 0)
953 {
954 g_b_init_copy_sid = 1;
955 hm_advapi32 = LoadLibrary ("Advapi32.dll");
956 s_pfn_Copy_Sid =
957 (CopySid_Proc) GetProcAddress (
958 hm_advapi32, "CopySid");
959 }
960 if (s_pfn_Copy_Sid == NULL)
961 {
962 return FALSE;
963 }
964 return (s_pfn_Copy_Sid (destlen, dest, src));
965}
966
f60ae425
BK
967/*
968 END: Wrapper functions around OpenProcessToken
969 and other functions in advapi32.dll that are only
970 supported in Windows NT / 2k / XP
971*/
972
bedf4aab
JB
973static void WINAPI
974get_native_system_info (LPSYSTEM_INFO lpSystemInfo)
ad9e2d54
EZ
975{
976 static GetNativeSystemInfo_Proc s_pfn_Get_Native_System_Info = NULL;
977 if (is_windows_9x () != TRUE)
978 {
979 if (g_b_init_get_native_system_info == 0)
980 {
981 g_b_init_get_native_system_info = 1;
982 s_pfn_Get_Native_System_Info =
983 (GetNativeSystemInfo_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
984 "GetNativeSystemInfo");
985 }
986 if (s_pfn_Get_Native_System_Info != NULL)
987 s_pfn_Get_Native_System_Info (lpSystemInfo);
988 }
989 else
990 lpSystemInfo->dwNumberOfProcessors = -1;
991}
992
bedf4aab
JB
993static BOOL WINAPI
994get_system_times (LPFILETIME lpIdleTime,
995 LPFILETIME lpKernelTime,
996 LPFILETIME lpUserTime)
ad9e2d54
EZ
997{
998 static GetSystemTimes_Proc s_pfn_Get_System_times = NULL;
999 if (is_windows_9x () == TRUE)
1000 {
1001 return FALSE;
1002 }
1003 if (g_b_init_get_system_times == 0)
1004 {
1005 g_b_init_get_system_times = 1;
1006 s_pfn_Get_System_times =
1007 (GetSystemTimes_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
1008 "GetSystemTimes");
1009 }
1010 if (s_pfn_Get_System_times == NULL)
1011 return FALSE;
1012 return (s_pfn_Get_System_times (lpIdleTime, lpKernelTime, lpUserTime));
1013}
6dad7178
EZ
1014
1015static BOOLEAN WINAPI
eeccd06f
EZ
1016create_symbolic_link (LPCSTR lpSymlinkFilename,
1017 LPCSTR lpTargetFileName,
6dad7178
EZ
1018 DWORD dwFlags)
1019{
eeccd06f
EZ
1020 static CreateSymbolicLinkW_Proc s_pfn_Create_Symbolic_LinkW = NULL;
1021 static CreateSymbolicLinkA_Proc s_pfn_Create_Symbolic_LinkA = NULL;
6dad7178
EZ
1022 BOOLEAN retval;
1023
1024 if (is_windows_9x () == TRUE)
1025 {
1026 errno = ENOSYS;
1027 return 0;
1028 }
eeccd06f 1029 if (w32_unicode_filenames)
6dad7178 1030 {
eeccd06f
EZ
1031 wchar_t symfn_w[MAX_PATH], tgtfn_w[MAX_PATH];
1032
1033 if (g_b_init_create_symbolic_link_w == 0)
1034 {
1035 g_b_init_create_symbolic_link_w = 1;
1036 s_pfn_Create_Symbolic_LinkW =
1037 (CreateSymbolicLinkW_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
1038 "CreateSymbolicLinkW");
1039 }
1040 if (s_pfn_Create_Symbolic_LinkW == NULL)
1041 {
1042 errno = ENOSYS;
1043 return 0;
1044 }
1045
1046 filename_to_utf16 (lpSymlinkFilename, symfn_w);
1047 filename_to_utf16 (lpTargetFileName, tgtfn_w);
1048 retval = s_pfn_Create_Symbolic_LinkW (symfn_w, tgtfn_w, dwFlags);
1049 /* If we were denied creation of the symlink, try again after
1050 enabling the SeCreateSymbolicLinkPrivilege for our process. */
1051 if (!retval)
1052 {
1053 TOKEN_PRIVILEGES priv_current;
1054
1055 if (enable_privilege (SE_CREATE_SYMBOLIC_LINK_NAME, TRUE,
1056 &priv_current))
1057 {
1058 retval = s_pfn_Create_Symbolic_LinkW (symfn_w, tgtfn_w, dwFlags);
1059 restore_privilege (&priv_current);
1060 revert_to_self ();
1061 }
1062 }
6dad7178 1063 }
eeccd06f 1064 else
6dad7178 1065 {
eeccd06f 1066 char symfn_a[MAX_PATH], tgtfn_a[MAX_PATH];
6dad7178 1067
eeccd06f
EZ
1068 if (g_b_init_create_symbolic_link_a == 0)
1069 {
1070 g_b_init_create_symbolic_link_a = 1;
1071 s_pfn_Create_Symbolic_LinkA =
1072 (CreateSymbolicLinkA_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
1073 "CreateSymbolicLinkA");
1074 }
1075 if (s_pfn_Create_Symbolic_LinkA == NULL)
1076 {
1077 errno = ENOSYS;
1078 return 0;
1079 }
6dad7178 1080
eeccd06f
EZ
1081 filename_to_ansi (lpSymlinkFilename, symfn_a);
1082 filename_to_ansi (lpTargetFileName, tgtfn_a);
1083 retval = s_pfn_Create_Symbolic_LinkA (symfn_a, tgtfn_a, dwFlags);
1084 /* If we were denied creation of the symlink, try again after
1085 enabling the SeCreateSymbolicLinkPrivilege for our process. */
1086 if (!retval)
6dad7178 1087 {
eeccd06f
EZ
1088 TOKEN_PRIVILEGES priv_current;
1089
1090 if (enable_privilege (SE_CREATE_SYMBOLIC_LINK_NAME, TRUE,
1091 &priv_current))
1092 {
1093 retval = s_pfn_Create_Symbolic_LinkA (symfn_a, tgtfn_a, dwFlags);
1094 restore_privilege (&priv_current);
1095 revert_to_self ();
1096 }
6dad7178
EZ
1097 }
1098 }
1099 return retval;
1100}
66447e07
EZ
1101
1102static BOOL WINAPI
1103is_valid_security_descriptor (PSECURITY_DESCRIPTOR pSecurityDescriptor)
1104{
1105 static IsValidSecurityDescriptor_Proc s_pfn_Is_Valid_Security_Descriptor_Proc = NULL;
1106
1107 if (is_windows_9x () == TRUE)
1108 {
1109 errno = ENOTSUP;
1110 return FALSE;
1111 }
1112
1113 if (g_b_init_is_valid_security_descriptor == 0)
1114 {
1115 g_b_init_is_valid_security_descriptor = 1;
1116 s_pfn_Is_Valid_Security_Descriptor_Proc =
1117 (IsValidSecurityDescriptor_Proc)GetProcAddress (GetModuleHandle ("Advapi32.dll"),
1118 "IsValidSecurityDescriptor");
1119 }
1120 if (s_pfn_Is_Valid_Security_Descriptor_Proc == NULL)
1121 {
1122 errno = ENOTSUP;
1123 return FALSE;
1124 }
1125
1126 return s_pfn_Is_Valid_Security_Descriptor_Proc (pSecurityDescriptor);
1127}
1128
1129static BOOL WINAPI
1130convert_sd_to_sddl (PSECURITY_DESCRIPTOR SecurityDescriptor,
1131 DWORD RequestedStringSDRevision,
1132 SECURITY_INFORMATION SecurityInformation,
1133 LPTSTR *StringSecurityDescriptor,
1134 PULONG StringSecurityDescriptorLen)
1135{
1136 static ConvertSecurityDescriptorToStringSecurityDescriptor_Proc s_pfn_Convert_SD_To_SDDL = NULL;
1137 BOOL retval;
1138
1139 if (is_windows_9x () == TRUE)
1140 {
1141 errno = ENOTSUP;
1142 return FALSE;
1143 }
1144
1145 if (g_b_init_convert_sd_to_sddl == 0)
1146 {
1147 g_b_init_convert_sd_to_sddl = 1;
1148#ifdef _UNICODE
1149 s_pfn_Convert_SD_To_SDDL =
1150 (ConvertSecurityDescriptorToStringSecurityDescriptor_Proc)GetProcAddress (GetModuleHandle ("Advapi32.dll"),
1151 "ConvertSecurityDescriptorToStringSecurityDescriptorW");
1152#else
1153 s_pfn_Convert_SD_To_SDDL =
1154 (ConvertSecurityDescriptorToStringSecurityDescriptor_Proc)GetProcAddress (GetModuleHandle ("Advapi32.dll"),
1155 "ConvertSecurityDescriptorToStringSecurityDescriptorA");
1156#endif
1157 }
1158 if (s_pfn_Convert_SD_To_SDDL == NULL)
1159 {
1160 errno = ENOTSUP;
1161 return FALSE;
1162 }
1163
1164 retval = s_pfn_Convert_SD_To_SDDL (SecurityDescriptor,
1165 RequestedStringSDRevision,
1166 SecurityInformation,
1167 StringSecurityDescriptor,
1168 StringSecurityDescriptorLen);
1169
1170 return retval;
1171}
1172
1173static BOOL WINAPI
1174convert_sddl_to_sd (LPCTSTR StringSecurityDescriptor,
1175 DWORD StringSDRevision,
1176 PSECURITY_DESCRIPTOR *SecurityDescriptor,
1177 PULONG SecurityDescriptorSize)
1178{
1179 static ConvertStringSecurityDescriptorToSecurityDescriptor_Proc s_pfn_Convert_SDDL_To_SD = NULL;
1180 BOOL retval;
1181
1182 if (is_windows_9x () == TRUE)
1183 {
1184 errno = ENOTSUP;
1185 return FALSE;
1186 }
1187
1188 if (g_b_init_convert_sddl_to_sd == 0)
1189 {
1190 g_b_init_convert_sddl_to_sd = 1;
1191#ifdef _UNICODE
1192 s_pfn_Convert_SDDL_To_SD =
1193 (ConvertStringSecurityDescriptorToSecurityDescriptor_Proc)GetProcAddress (GetModuleHandle ("Advapi32.dll"),
1194 "ConvertStringSecurityDescriptorToSecurityDescriptorW");
1195#else
1196 s_pfn_Convert_SDDL_To_SD =
1197 (ConvertStringSecurityDescriptorToSecurityDescriptor_Proc)GetProcAddress (GetModuleHandle ("Advapi32.dll"),
1198 "ConvertStringSecurityDescriptorToSecurityDescriptorA");
1199#endif
1200 }
1201 if (s_pfn_Convert_SDDL_To_SD == NULL)
1202 {
1203 errno = ENOTSUP;
1204 return FALSE;
1205 }
1206
1207 retval = s_pfn_Convert_SDDL_To_SD (StringSecurityDescriptor,
1208 StringSDRevision,
1209 SecurityDescriptor,
1210 SecurityDescriptorSize);
1211
1212 return retval;
1213}
1214
3dffe395
EZ
1215static DWORD WINAPI
1216get_adapters_info (PIP_ADAPTER_INFO pAdapterInfo, PULONG pOutBufLen)
1217{
1218 static GetAdaptersInfo_Proc s_pfn_Get_Adapters_Info = NULL;
1219 HMODULE hm_iphlpapi = NULL;
1220
1221 if (is_windows_9x () == TRUE)
1222 return ERROR_NOT_SUPPORTED;
1223
1224 if (g_b_init_get_adapters_info == 0)
1225 {
1226 g_b_init_get_adapters_info = 1;
1227 hm_iphlpapi = LoadLibrary ("Iphlpapi.dll");
1228 if (hm_iphlpapi)
1229 s_pfn_Get_Adapters_Info = (GetAdaptersInfo_Proc)
1230 GetProcAddress (hm_iphlpapi, "GetAdaptersInfo");
1231 }
1232 if (s_pfn_Get_Adapters_Info == NULL)
1233 return ERROR_NOT_SUPPORTED;
1234 return s_pfn_Get_Adapters_Info (pAdapterInfo, pOutBufLen);
1235}
1236
f60ae425 1237\f
18e070ac 1238
ed91b2ad
EZ
1239/* Return 1 if P is a valid pointer to an object of size SIZE. Return
1240 0 if P is NOT a valid pointer. Return -1 if we cannot validate P.
1241
1242 This is called from alloc.c:valid_pointer_p. */
1243int
1244w32_valid_pointer_p (void *p, int size)
1245{
1246 SIZE_T done;
1247 HANDLE h = OpenProcess (PROCESS_VM_READ, FALSE, GetCurrentProcessId ());
1248
1249 if (h)
1250 {
1251 unsigned char *buf = alloca (size);
1252 int retval = ReadProcessMemory (h, p, buf, size, &done);
1253
1254 CloseHandle (h);
1255 return retval;
1256 }
1257 else
1258 return -1;
1259}
1260
03d58cca
EZ
1261\f
1262
c3e9160b
EZ
1263/* Converting file names from UTF-8 to either UTF-16 or the ANSI
1264 codepage defined by file-name-coding-system. */
1265
1266/* Current codepage for encoding file names. */
1267static int file_name_codepage;
1268
1269/* Produce a Windows ANSI codepage suitable for encoding file names.
1270 Return the information about that codepage in CP_INFO. */
1271static int
1272codepage_for_filenames (CPINFO *cp_info)
1273{
1274 /* A simple cache to avoid calling GetCPInfo every time we need to
1275 encode/decode a file name. The file-name encoding is not
1276 supposed to be changed too frequently, if ever. */
1277 static Lisp_Object last_file_name_encoding;
1278 static CPINFO cp;
1279 Lisp_Object current_encoding;
1280
1281 current_encoding = Vfile_name_coding_system;
1282 if (NILP (current_encoding))
1283 current_encoding = Vdefault_file_name_coding_system;
1284
1285 if (!EQ (last_file_name_encoding, current_encoding))
1286 {
1287 /* Default to the current ANSI codepage. */
1288 file_name_codepage = w32_ansi_code_page;
1289
1290 if (NILP (current_encoding))
1291 {
1292 char *cpname = SDATA (SYMBOL_NAME (current_encoding));
1293 char *cp = NULL, *end;
1294 int cpnum;
1295
1296 if (strncmp (cpname, "cp", 2) == 0)
1297 cp = cpname + 2;
1298 else if (strncmp (cpname, "windows-", 8) == 0)
1299 cp = cpname + 8;
1300
1301 if (cp)
1302 {
1303 end = cp;
1304 cpnum = strtol (cp, &end, 10);
1305 if (cpnum && *end == '\0' && end - cp >= 2)
1306 file_name_codepage = cpnum;
1307 }
1308 }
1309
1310 if (!file_name_codepage)
1311 file_name_codepage = CP_ACP; /* CP_ACP = 0, but let's not assume that */
1312
1313 if (!GetCPInfo (file_name_codepage, &cp))
1314 {
1315 file_name_codepage = CP_ACP;
1316 if (!GetCPInfo (file_name_codepage, &cp))
1317 emacs_abort ();
1318 }
1319 }
1320 if (cp_info)
1321 *cp_info = cp;
1322
1323 return file_name_codepage;
1324}
1325
03d58cca
EZ
1326static int
1327filename_to_utf16 (const char *fn_in, wchar_t *fn_out)
1328{
c3e9160b
EZ
1329 int result = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, fn_in, -1,
1330 fn_out, MAX_PATH);
03d58cca
EZ
1331
1332 if (!result)
1333 {
1334 DWORD err = GetLastError ();
1335
1336 switch (err)
1337 {
1338 case ERROR_INVALID_FLAGS:
1339 case ERROR_INVALID_PARAMETER:
1340 errno = EINVAL;
1341 break;
1342 case ERROR_INSUFFICIENT_BUFFER:
1343 case ERROR_NO_UNICODE_TRANSLATION:
1344 default:
1345 errno = ENOENT;
1346 break;
1347 }
1348 return -1;
1349 }
1350 return 0;
1351}
1352
1353static int
1354filename_from_utf16 (const wchar_t *fn_in, char *fn_out)
1355{
c3e9160b
EZ
1356 int result = WideCharToMultiByte (CP_UTF8, 0, fn_in, -1,
1357 fn_out, MAX_UTF8_PATH, NULL, NULL);
1358
1359 if (!result)
1360 {
1361 DWORD err = GetLastError ();
1362
1363 switch (err)
1364 {
1365 case ERROR_INVALID_FLAGS:
1366 case ERROR_INVALID_PARAMETER:
1367 errno = EINVAL;
1368 break;
1369 case ERROR_INSUFFICIENT_BUFFER:
1370 case ERROR_NO_UNICODE_TRANSLATION:
1371 default:
1372 errno = ENOENT;
1373 break;
1374 }
1375 return -1;
1376 }
1377 return 0;
03d58cca
EZ
1378}
1379
1380static int
1381filename_to_ansi (const char *fn_in, char *fn_out)
1382{
80fcb9d2 1383 wchar_t fn_utf16[MAX_PATH];
03d58cca
EZ
1384
1385 if (filename_to_utf16 (fn_in, fn_utf16) == 0)
1386 {
c3e9160b
EZ
1387 int result;
1388 int codepage = codepage_for_filenames (NULL);
03d58cca 1389
c3e9160b 1390 result = WideCharToMultiByte (codepage, 0, fn_utf16, -1,
80fcb9d2 1391 fn_out, MAX_PATH, NULL, NULL);
03d58cca
EZ
1392 if (!result)
1393 {
1394 DWORD err = GetLastError ();
1395
1396 switch (err)
1397 {
1398 case ERROR_INVALID_FLAGS:
1399 case ERROR_INVALID_PARAMETER:
1400 errno = EINVAL;
1401 break;
1402 case ERROR_INSUFFICIENT_BUFFER:
1403 case ERROR_NO_UNICODE_TRANSLATION:
1404 default:
1405 errno = ENOENT;
1406 break;
1407 }
1408 return -1;
1409 }
1410 return 0;
1411 }
c3e9160b 1412 return -1;
03d58cca
EZ
1413}
1414
1fd201bb 1415int
03d58cca
EZ
1416filename_from_ansi (const char *fn_in, char *fn_out)
1417{
80fcb9d2 1418 wchar_t fn_utf16[MAX_PATH];
c3e9160b
EZ
1419 int codepage = codepage_for_filenames (NULL);
1420 int result = MultiByteToWideChar (codepage, MB_ERR_INVALID_CHARS, fn_in, -1,
1421 fn_utf16, MAX_PATH);
1422
1423 if (!result)
1424 {
1425 DWORD err = GetLastError ();
1426
1427 switch (err)
1428 {
1429 case ERROR_INVALID_FLAGS:
1430 case ERROR_INVALID_PARAMETER:
1431 errno = EINVAL;
1432 break;
1433 case ERROR_INSUFFICIENT_BUFFER:
1434 case ERROR_NO_UNICODE_TRANSLATION:
1435 default:
1436 errno = ENOENT;
1437 break;
1438 }
1439 return -1;
1440 }
1441 return filename_from_utf16 (fn_utf16, fn_out);
03d58cca
EZ
1442}
1443
1444\f
1445
5c4a19a9 1446/* The directory where we started, in UTF-8. */
03d58cca 1447static char startup_dir[MAX_UTF8_PATH];
00b3b7b3 1448
95ed0025 1449/* Get the current working directory. */
480b0c5b 1450char *
ec84768f 1451getcwd (char *dir, int dirsize)
95ed0025 1452{
9239d970
PE
1453 if (!dirsize)
1454 {
1455 errno = EINVAL;
1456 return NULL;
1457 }
1458 if (dirsize <= strlen (startup_dir))
1459 {
1460 errno = ERANGE;
1461 return NULL;
1462 }
76b3903d 1463#if 0
480b0c5b
GV
1464 if (GetCurrentDirectory (MAXPATHLEN, dir) > 0)
1465 return dir;
1466 return NULL;
76b3903d 1467#else
8d38f461
EZ
1468 /* Emacs doesn't actually change directory itself, it stays in the
1469 same directory where it was started. */
76b3903d
GV
1470 strcpy (dir, startup_dir);
1471 return dir;
1472#endif
95ed0025
RS
1473}
1474
95ed0025 1475/* Emulate getloadavg. */
ad9e2d54
EZ
1476
1477struct load_sample {
1478 time_t sample_time;
1479 ULONGLONG idle;
1480 ULONGLONG kernel;
1481 ULONGLONG user;
1482};
1483
1484/* Number of processors on this machine. */
1485static unsigned num_of_processors;
1486
1487/* We maintain 1-sec samples for the last 16 minutes in a circular buffer. */
1488static struct load_sample samples[16*60];
1489static int first_idx = -1, last_idx = -1;
1490static int max_idx = sizeof (samples) / sizeof (samples[0]);
1491
1492static int
1493buf_next (int from)
1494{
1495 int next_idx = from + 1;
1496
1497 if (next_idx >= max_idx)
1498 next_idx = 0;
1499
1500 return next_idx;
1501}
1502
1503static int
1504buf_prev (int from)
1505{
1506 int prev_idx = from - 1;
1507
1508 if (prev_idx < 0)
1509 prev_idx = max_idx - 1;
1510
1511 return prev_idx;
1512}
1513
1514static void
1515sample_system_load (ULONGLONG *idle, ULONGLONG *kernel, ULONGLONG *user)
1516{
1517 SYSTEM_INFO sysinfo;
1518 FILETIME ft_idle, ft_user, ft_kernel;
1519
1520 /* Initialize the number of processors on this machine. */
1521 if (num_of_processors <= 0)
1522 {
1523 get_native_system_info (&sysinfo);
1524 num_of_processors = sysinfo.dwNumberOfProcessors;
1525 if (num_of_processors <= 0)
1526 {
1527 GetSystemInfo (&sysinfo);
1528 num_of_processors = sysinfo.dwNumberOfProcessors;
1529 }
1530 if (num_of_processors <= 0)
1531 num_of_processors = 1;
1532 }
1533
1534 /* TODO: Take into account threads that are ready to run, by
1535 sampling the "\System\Processor Queue Length" performance
1536 counter. The code below accounts only for threads that are
1537 actually running. */
1538
1539 if (get_system_times (&ft_idle, &ft_kernel, &ft_user))
1540 {
1541 ULARGE_INTEGER uidle, ukernel, uuser;
1542
1543 memcpy (&uidle, &ft_idle, sizeof (ft_idle));
1544 memcpy (&ukernel, &ft_kernel, sizeof (ft_kernel));
1545 memcpy (&uuser, &ft_user, sizeof (ft_user));
1546 *idle = uidle.QuadPart;
1547 *kernel = ukernel.QuadPart;
1548 *user = uuser.QuadPart;
1549 }
1550 else
1551 {
1552 *idle = 0;
1553 *kernel = 0;
1554 *user = 0;
1555 }
1556}
1557
1558/* Produce the load average for a given time interval, using the
1559 samples in the samples[] array. WHICH can be 0, 1, or 2, meaning
1560 1-minute, 5-minute, or 15-minute average, respectively. */
1561static double
1562getavg (int which)
1563{
1564 double retval = -1.0;
1565 double tdiff;
1566 int idx;
1567 double span = (which == 0 ? 1.0 : (which == 1 ? 5.0 : 15.0)) * 60;
1568 time_t now = samples[last_idx].sample_time;
1569
1570 if (first_idx != last_idx)
1571 {
1572 for (idx = buf_prev (last_idx); ; idx = buf_prev (idx))
1573 {
1574 tdiff = difftime (now, samples[idx].sample_time);
1575 if (tdiff >= span - 2*DBL_EPSILON*now)
1576 {
1577 long double sys =
1578 samples[last_idx].kernel + samples[last_idx].user
1579 - (samples[idx].kernel + samples[idx].user);
1580 long double idl = samples[last_idx].idle - samples[idx].idle;
1581
1582 retval = (1.0 - idl / sys) * num_of_processors;
1583 break;
1584 }
1585 if (idx == first_idx)
1586 break;
1587 }
1588 }
1589
1590 return retval;
1591}
1592
95ed0025
RS
1593int
1594getloadavg (double loadavg[], int nelem)
1595{
ad9e2d54
EZ
1596 int elem;
1597 ULONGLONG idle, kernel, user;
1598 time_t now = time (NULL);
1599
1600 /* Store another sample. We ignore samples that are less than 1 sec
1601 apart. */
1602 if (difftime (now, samples[last_idx].sample_time) >= 1.0 - 2*DBL_EPSILON*now)
1603 {
1604 sample_system_load (&idle, &kernel, &user);
1605 last_idx = buf_next (last_idx);
1606 samples[last_idx].sample_time = now;
1607 samples[last_idx].idle = idle;
1608 samples[last_idx].kernel = kernel;
1609 samples[last_idx].user = user;
1610 /* If the buffer has more that 15 min worth of samples, discard
1611 the old ones. */
1612 if (first_idx == -1)
1613 first_idx = last_idx;
1614 while (first_idx != last_idx
1615 && (difftime (now, samples[first_idx].sample_time)
1616 >= 15.0*60 + 2*DBL_EPSILON*now))
1617 first_idx = buf_next (first_idx);
1618 }
95ed0025 1619
ad9e2d54 1620 for (elem = 0; elem < nelem; elem++)
95ed0025 1621 {
ad9e2d54
EZ
1622 double avg = getavg (elem);
1623
1624 if (avg < 0)
1625 break;
1626 loadavg[elem] = avg;
95ed0025 1627 }
ad9e2d54
EZ
1628
1629 return elem;
95ed0025
RS
1630}
1631
480b0c5b 1632/* Emulate getpwuid, getpwnam and others. */
95ed0025 1633
051fe60d
GV
1634#define PASSWD_FIELD_SIZE 256
1635
07f7980a
EZ
1636static char dflt_passwd_name[PASSWD_FIELD_SIZE];
1637static char dflt_passwd_passwd[PASSWD_FIELD_SIZE];
1638static char dflt_passwd_gecos[PASSWD_FIELD_SIZE];
5c4a19a9
EZ
1639static char dflt_passwd_dir[MAX_UTF8_PATH];
1640static char dflt_passwd_shell[MAX_UTF8_PATH];
95ed0025 1641
07f7980a 1642static struct passwd dflt_passwd =
95ed0025 1643{
07f7980a
EZ
1644 dflt_passwd_name,
1645 dflt_passwd_passwd,
95ed0025
RS
1646 0,
1647 0,
1648 0,
07f7980a
EZ
1649 dflt_passwd_gecos,
1650 dflt_passwd_dir,
1651 dflt_passwd_shell,
95ed0025
RS
1652};
1653
07f7980a
EZ
1654static char dflt_group_name[GNLEN+1];
1655
1656static struct group dflt_group =
3d19b645 1657{
07f7980a
EZ
1658 /* When group information is not available, we return this as the
1659 group for all files. */
1660 dflt_group_name,
1661 0,
3d19b645
LH
1662};
1663
22749e9a 1664unsigned
b56ceb92 1665getuid (void)
177c0ea7 1666{
07f7980a 1667 return dflt_passwd.pw_uid;
480b0c5b
GV
1668}
1669
22749e9a 1670unsigned
b56ceb92 1671geteuid (void)
177c0ea7 1672{
480b0c5b
GV
1673 /* I could imagine arguing for checking to see whether the user is
1674 in the Administrators group and returning a UID of 0 for that
1675 case, but I don't know how wise that would be in the long run. */
177c0ea7 1676 return getuid ();
480b0c5b
GV
1677}
1678
22749e9a 1679unsigned
b56ceb92 1680getgid (void)
177c0ea7 1681{
07f7980a 1682 return dflt_passwd.pw_gid;
480b0c5b
GV
1683}
1684
22749e9a 1685unsigned
b56ceb92 1686getegid (void)
177c0ea7 1687{
480b0c5b
GV
1688 return getgid ();
1689}
1690
95ed0025 1691struct passwd *
22749e9a 1692getpwuid (unsigned uid)
95ed0025 1693{
07f7980a
EZ
1694 if (uid == dflt_passwd.pw_uid)
1695 return &dflt_passwd;
480b0c5b 1696 return NULL;
95ed0025
RS
1697}
1698
3d19b645
LH
1699struct group *
1700getgrgid (gid_t gid)
1701{
07f7980a 1702 return &dflt_group;
3d19b645
LH
1703}
1704
95ed0025
RS
1705struct passwd *
1706getpwnam (char *name)
1707{
1708 struct passwd *pw;
177c0ea7 1709
95ed0025
RS
1710 pw = getpwuid (getuid ());
1711 if (!pw)
1712 return pw;
1713
05131107 1714 if (xstrcasecmp (name, pw->pw_name))
95ed0025
RS
1715 return NULL;
1716
1717 return pw;
1718}
1719
bedf4aab 1720static void
b56ceb92 1721init_user_info (void)
95ed0025 1722{
480b0c5b
GV
1723 /* Find the user's real name by opening the process token and
1724 looking up the name associated with the user-sid in that token.
1725
1726 Use the relative portion of the identifier authority value from
1727 the user-sid as the user id value (same for group id using the
1728 primary group sid from the process token). */
1729
07f7980a 1730 char uname[UNLEN+1], gname[GNLEN+1], domain[1025];
32cef06e 1731 DWORD ulength = sizeof (uname), dlength = sizeof (domain), needed;
07f7980a 1732 DWORD glength = sizeof (gname);
10aabbf9
JB
1733 HANDLE token = NULL;
1734 SID_NAME_USE user_type;
32cef06e
EZ
1735 unsigned char *buf = NULL;
1736 DWORD blen = 0;
634d3003
EZ
1737 TOKEN_USER user_token;
1738 TOKEN_PRIMARY_GROUP group_token;
32cef06e 1739 BOOL result;
10aabbf9 1740
32cef06e
EZ
1741 result = open_process_token (GetCurrentProcess (), TOKEN_QUERY, &token);
1742 if (result)
1743 {
1744 result = get_token_information (token, TokenUser, NULL, 0, &blen);
1745 if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER)
1746 {
1747 buf = xmalloc (blen);
1748 result = get_token_information (token, TokenUser,
1749 (LPVOID)buf, blen, &needed);
1750 if (result)
1751 {
1752 memcpy (&user_token, buf, sizeof (user_token));
1753 result = lookup_account_sid (NULL, user_token.User.Sid,
1754 uname, &ulength,
1755 domain, &dlength, &user_type);
1756 }
1757 }
1758 else
1759 result = FALSE;
1760 }
1761 if (result)
d1c1c3d2 1762 {
07f7980a 1763 strcpy (dflt_passwd.pw_name, uname);
c617afce 1764 /* Determine a reasonable uid value. */
05131107 1765 if (xstrcasecmp ("administrator", uname) == 0)
480b0c5b 1766 {
07f7980a
EZ
1767 dflt_passwd.pw_uid = 500; /* well-known Administrator uid */
1768 dflt_passwd.pw_gid = 513; /* well-known None gid */
480b0c5b
GV
1769 }
1770 else
1771 {
ce0ee994
EZ
1772 /* Use the last sub-authority value of the RID, the relative
1773 portion of the SID, as user/group ID. */
8aaaec6b 1774 dflt_passwd.pw_uid = get_rid (user_token.User.Sid);
480b0c5b 1775
8aaaec6b 1776 /* Get group id and name. */
32cef06e
EZ
1777 result = get_token_information (token, TokenPrimaryGroup,
1778 (LPVOID)buf, blen, &needed);
1779 if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER)
1780 {
1781 buf = xrealloc (buf, blen = needed);
1782 result = get_token_information (token, TokenPrimaryGroup,
1783 (LPVOID)buf, blen, &needed);
1784 }
1785 if (result)
480b0c5b 1786 {
634d3003 1787 memcpy (&group_token, buf, sizeof (group_token));
8aaaec6b 1788 dflt_passwd.pw_gid = get_rid (group_token.PrimaryGroup);
07f7980a 1789 dlength = sizeof (domain);
32cef06e
EZ
1790 /* If we can get at the real Primary Group name, use that.
1791 Otherwise, the default group name was already set to
1792 "None" in globals_of_w32. */
07f7980a
EZ
1793 if (lookup_account_sid (NULL, group_token.PrimaryGroup,
1794 gname, &glength, NULL, &dlength,
1795 &user_type))
1796 strcpy (dflt_group_name, gname);
480b0c5b
GV
1797 }
1798 else
07f7980a 1799 dflt_passwd.pw_gid = dflt_passwd.pw_uid;
480b0c5b
GV
1800 }
1801 }
1802 /* If security calls are not supported (presumably because we
32cef06e 1803 are running under Windows 9X), fallback to this: */
07f7980a 1804 else if (GetUserName (uname, &ulength))
480b0c5b 1805 {
07f7980a 1806 strcpy (dflt_passwd.pw_name, uname);
05131107 1807 if (xstrcasecmp ("administrator", uname) == 0)
07f7980a 1808 dflt_passwd.pw_uid = 0;
480b0c5b 1809 else
07f7980a
EZ
1810 dflt_passwd.pw_uid = 123;
1811 dflt_passwd.pw_gid = dflt_passwd.pw_uid;
480b0c5b
GV
1812 }
1813 else
1814 {
07f7980a
EZ
1815 strcpy (dflt_passwd.pw_name, "unknown");
1816 dflt_passwd.pw_uid = 123;
1817 dflt_passwd.pw_gid = 123;
d1c1c3d2 1818 }
07f7980a 1819 dflt_group.gr_gid = dflt_passwd.pw_gid;
95ed0025 1820
480b0c5b 1821 /* Set dir and shell from environment variables. */
5c4a19a9
EZ
1822 if (w32_unicode_filenames)
1823 {
1824 wchar_t *home = _wgetenv (L"HOME");
1825 wchar_t *shell = _wgetenv (L"SHELL");
1826
1827 /* Ensure HOME and SHELL are defined. */
1828 if (home == NULL)
1829 emacs_abort ();
1830 if (shell == NULL)
1831 emacs_abort ();
1832 filename_from_utf16 (home, dflt_passwd.pw_dir);
1833 filename_from_utf16 (shell, dflt_passwd.pw_shell);
1834 }
1835 else
1836 {
1837 char *home = getenv ("HOME");
1838 char *shell = getenv ("SHELL");
1839
1840 if (home == NULL)
1841 emacs_abort ();
1842 if (shell == NULL)
1843 emacs_abort ();
1844 filename_from_ansi (home, dflt_passwd.pw_dir);
1845 filename_from_ansi (shell, dflt_passwd.pw_shell);
1846 }
bd4a449f 1847
32cef06e 1848 xfree (buf);
480b0c5b
GV
1849 if (token)
1850 CloseHandle (token);
95ed0025
RS
1851}
1852
95ed0025 1853int
b56ceb92 1854random (void)
95ed0025 1855{
480b0c5b
GV
1856 /* rand () on NT gives us 15 random bits...hack together 30 bits. */
1857 return ((rand () << 15) | rand ());
95ed0025
RS
1858}
1859
95ed0025 1860void
480b0c5b 1861srandom (int seed)
95ed0025 1862{
480b0c5b 1863 srand (seed);
95ed0025
RS
1864}
1865
6d2851de
EZ
1866/* Return the maximum length in bytes of a multibyte character
1867 sequence encoded in the current ANSI codepage. This is required to
1868 correctly walk the encoded file names one character at a time. */
1869static int
1870max_filename_mbslen (void)
1871{
c3e9160b 1872 CPINFO cp_info;
6d2851de 1873
c3e9160b
EZ
1874 codepage_for_filenames (&cp_info);
1875 return cp_info.MaxCharSize;
6d2851de 1876}
76b3903d 1877
1fd201bb
EZ
1878/* Normalize filename by converting in-place all of its path
1879 separators to the separator specified by PATH_SEP. */
cbe39279
RS
1880
1881static void
1fd201bb 1882normalize_filename (register char *fp, char path_sep)
cbe39279 1883{
1fd201bb 1884 char *p2;
e7ac588e 1885
5162ffce
MB
1886 /* Always lower-case drive letters a-z, even if the filesystem
1887 preserves case in filenames.
1888 This is so filenames can be compared by string comparison
1889 functions that are case-sensitive. Even case-preserving filesystems
1890 do not distinguish case in drive letters. */
1fd201bb 1891 p2 = fp + 1;
6d2851de
EZ
1892
1893 if (*p2 == ':' && *fp >= 'A' && *fp <= 'Z')
5162ffce
MB
1894 {
1895 *fp += 'a' - 'A';
1896 fp += 2;
1897 }
1898
1fd201bb 1899 while (*fp)
cbe39279 1900 {
1fd201bb
EZ
1901 if (*fp == '/' || *fp == '\\')
1902 *fp = path_sep;
1903 fp++;
cbe39279 1904 }
cbe39279
RS
1905}
1906
1fd201bb 1907/* Destructively turn backslashes into slashes. */
95ed0025 1908void
1fd201bb 1909dostounix_filename (register char *p)
95ed0025 1910{
1fd201bb 1911 normalize_filename (p, '/');
95ed0025
RS
1912}
1913
480b0c5b 1914/* Destructively turn slashes into backslashes. */
95ed0025 1915void
b56ceb92 1916unixtodos_filename (register char *p)
95ed0025 1917{
1fd201bb 1918 normalize_filename (p, '\\');
95ed0025
RS
1919}
1920
480b0c5b
GV
1921/* Remove all CR's that are followed by a LF.
1922 (From msdos.c...probably should figure out a way to share it,
1923 although this code isn't going to ever change.) */
bedf4aab 1924static int
b56ceb92 1925crlf_to_lf (register int n, register unsigned char *buf)
35f0d482 1926{
480b0c5b
GV
1927 unsigned char *np = buf;
1928 unsigned char *startp = buf;
1929 unsigned char *endp = buf + n;
35f0d482 1930
480b0c5b
GV
1931 if (n == 0)
1932 return n;
1933 while (buf < endp - 1)
95ed0025 1934 {
480b0c5b
GV
1935 if (*buf == 0x0d)
1936 {
1937 if (*(++buf) != 0x0a)
1938 *np++ = 0x0d;
1939 }
1940 else
1941 *np++ = *buf++;
95ed0025 1942 }
480b0c5b
GV
1943 if (buf < endp)
1944 *np++ = *buf++;
1945 return np - startp;
95ed0025
RS
1946}
1947
76b3903d
GV
1948/* Parse the root part of file name, if present. Return length and
1949 optionally store pointer to char after root. */
1950static int
f5441ba4 1951parse_root (const char * name, const char ** pPath)
76b3903d 1952{
f5441ba4 1953 const char * start = name;
76b3903d
GV
1954
1955 if (name == NULL)
1956 return 0;
1957
1958 /* find the root name of the volume if given */
1959 if (isalpha (name[0]) && name[1] == ':')
1960 {
1961 /* skip past drive specifier */
1962 name += 2;
1963 if (IS_DIRECTORY_SEP (name[0]))
1964 name++;
1965 }
1966 else if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1]))
1967 {
1968 int slashes = 2;
806fed21 1969
76b3903d
GV
1970 name += 2;
1971 do
1972 {
1973 if (IS_DIRECTORY_SEP (*name) && --slashes == 0)
1974 break;
1fd201bb 1975 name++;
76b3903d
GV
1976 }
1977 while ( *name );
1978 if (IS_DIRECTORY_SEP (name[0]))
1979 name++;
1980 }
1981
1982 if (pPath)
1983 *pPath = name;
1984
1985 return name - start;
1986}
1987
1988/* Get long base name for name; name is assumed to be absolute. */
1989static int
1990get_long_basename (char * name, char * buf, int size)
1991{
76b3903d 1992 HANDLE dir_handle;
1fd201bb 1993 char fname_utf8[MAX_UTF8_PATH];
76b3903d 1994 int len = 0;
1fd201bb 1995 int cstatus;
76b3903d 1996
1fd201bb
EZ
1997 /* Must be valid filename, no wild cards or other invalid characters. */
1998 if (strpbrk (name, "*?|<>\""))
bb1584c8
RS
1999 return 0;
2000
1fd201bb 2001 if (w32_unicode_filenames)
76b3903d 2002 {
1fd201bb
EZ
2003 wchar_t fname_utf16[MAX_PATH];
2004 WIN32_FIND_DATAW find_data_wide;
2005
2006 filename_to_utf16 (name, fname_utf16);
2007 dir_handle = FindFirstFileW (fname_utf16, &find_data_wide);
2008 if (dir_handle != INVALID_HANDLE_VALUE)
2009 cstatus = filename_from_utf16 (find_data_wide.cFileName, fname_utf8);
2010 }
2011 else
2012 {
2013 char fname_ansi[MAX_PATH];
2014 WIN32_FIND_DATAA find_data_ansi;
2015
2016 filename_to_ansi (name, fname_ansi);
2017 dir_handle = FindFirstFileA (fname_ansi, &find_data_ansi);
2018 if (dir_handle != INVALID_HANDLE_VALUE)
2019 cstatus = filename_from_ansi (find_data_ansi.cFileName, fname_utf8);
76b3903d 2020 }
1fd201bb
EZ
2021
2022 if (cstatus == 0 && (len = strlen (fname_utf8)) < size)
2023 memcpy (buf, fname_utf8, len + 1);
2024 else
2025 len = 0;
2026
2027 if (dir_handle != INVALID_HANDLE_VALUE)
2028 FindClose (dir_handle);
2029
76b3903d
GV
2030 return len;
2031}
2032
2033/* Get long name for file, if possible (assumed to be absolute). */
2034BOOL
2035w32_get_long_filename (char * name, char * buf, int size)
2036{
2037 char * o = buf;
2038 char * p;
f5441ba4 2039 const char * q;
1fd201bb 2040 char full[ MAX_UTF8_PATH ];
76b3903d
GV
2041 int len;
2042
2043 len = strlen (name);
1fd201bb 2044 if (len >= MAX_UTF8_PATH)
76b3903d
GV
2045 return FALSE;
2046
2047 /* Use local copy for destructive modification. */
2048 memcpy (full, name, len+1);
2049 unixtodos_filename (full);
2050
2051 /* Copy root part verbatim. */
f5441ba4 2052 len = parse_root (full, (const char **)&p);
76b3903d
GV
2053 memcpy (o, full, len);
2054 o += len;
4f8ac0b2 2055 *o = '\0';
76b3903d
GV
2056 size -= len;
2057
4f8ac0b2 2058 while (p != NULL && *p)
76b3903d
GV
2059 {
2060 q = p;
1fd201bb 2061 p = strchr (q, '\\');
76b3903d
GV
2062 if (p) *p = '\0';
2063 len = get_long_basename (full, o, size);
2064 if (len > 0)
2065 {
2066 o += len;
2067 size -= len;
2068 if (p != NULL)
2069 {
2070 *p++ = '\\';
2071 if (size < 2)
2072 return FALSE;
2073 *o++ = '\\';
2074 size--;
2075 *o = '\0';
2076 }
2077 }
2078 else
2079 return FALSE;
2080 }
76b3903d
GV
2081
2082 return TRUE;
2083}
2084
1fd201bb
EZ
2085unsigned int
2086w32_get_short_filename (char * name, char * buf, int size)
2087{
2088 if (w32_unicode_filenames)
2089 {
2090 wchar_t name_utf16[MAX_PATH], short_name[MAX_PATH];
2091 unsigned int retval;
2092
2093 filename_to_utf16 (name, name_utf16);
2094 retval = GetShortPathNameW (name_utf16, short_name, size);
2095 if (retval && retval < size)
2096 filename_from_utf16 (short_name, buf);
2097 return retval;
2098 }
2099 else
2100 {
2101 char name_ansi[MAX_PATH];
2102
2103 filename_to_ansi (name, name_ansi);
2104 return GetShortPathNameA (name_ansi, buf, size);
2105 }
2106}
2107
bedf4aab 2108static int
9d3355d1
GV
2109is_unc_volume (const char *filename)
2110{
2111 const char *ptr = filename;
2112
2113 if (!IS_DIRECTORY_SEP (ptr[0]) || !IS_DIRECTORY_SEP (ptr[1]) || !ptr[2])
2114 return 0;
2115
b1846422 2116 if (strpbrk (ptr + 2, "*?|<>\"\\/"))
9d3355d1
GV
2117 return 0;
2118
2119 return 1;
2120}
76b3903d 2121
75ceee05
EZ
2122/* Emulate the Posix unsetenv. */
2123int
2124unsetenv (const char *name)
2125{
2126 char *var;
2127 size_t name_len;
2128 int retval;
2129
2130 if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
2131 {
2132 errno = EINVAL;
2133 return -1;
2134 }
2135 name_len = strlen (name);
2136 /* MS docs says an environment variable cannot be longer than 32K. */
2137 if (name_len > 32767)
2138 {
2139 errno = ENOMEM;
a16e75cd 2140 return 0;
75ceee05
EZ
2141 }
2142 /* It is safe to use 'alloca' with 32K size, since the stack is at
2143 least 2MB, and we set it to 8MB in the link command line. */
2144 var = alloca (name_len + 2);
a7f11948 2145 strncpy (var, name, name_len);
75ceee05
EZ
2146 var[name_len++] = '=';
2147 var[name_len] = '\0';
2148 return _putenv (var);
2149}
2150
2151/* MS _putenv doesn't support removing a variable when the argument
2152 does not include the '=' character, so we fix that here. */
2153int
2154sys_putenv (char *str)
2155{
2156 const char *const name_end = strchr (str, '=');
2157
2158 if (name_end == NULL)
2159 {
2160 /* Remove the variable from the environment. */
2161 return unsetenv (str);
2162 }
2163
2164 return _putenv (str);
2165}
2166
480b0c5b 2167#define REG_ROOT "SOFTWARE\\GNU\\Emacs"
f332b293 2168
177c0ea7 2169LPBYTE
b56ceb92 2170w32_get_resource (char *key, LPDWORD lpdwtype)
f332b293
GV
2171{
2172 LPBYTE lpvalue;
2173 HKEY hrootkey = NULL;
2174 DWORD cbData;
177c0ea7
JB
2175
2176 /* Check both the current user and the local machine to see if
f332b293 2177 we have any resources. */
177c0ea7 2178
f332b293
GV
2179 if (RegOpenKeyEx (HKEY_CURRENT_USER, REG_ROOT, 0, KEY_READ, &hrootkey) == ERROR_SUCCESS)
2180 {
2181 lpvalue = NULL;
2182
177c0ea7 2183 if (RegQueryValueEx (hrootkey, key, NULL, NULL, NULL, &cbData) == ERROR_SUCCESS
23f86fce 2184 && (lpvalue = xmalloc (cbData)) != NULL
f332b293
GV
2185 && RegQueryValueEx (hrootkey, key, NULL, lpdwtype, lpvalue, &cbData) == ERROR_SUCCESS)
2186 {
4da4d9bb 2187 RegCloseKey (hrootkey);
f332b293
GV
2188 return (lpvalue);
2189 }
2190
70fdbb46 2191 xfree (lpvalue);
177c0ea7 2192
f332b293 2193 RegCloseKey (hrootkey);
177c0ea7
JB
2194 }
2195
f332b293
GV
2196 if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, REG_ROOT, 0, KEY_READ, &hrootkey) == ERROR_SUCCESS)
2197 {
2198 lpvalue = NULL;
177c0ea7 2199
76b3903d 2200 if (RegQueryValueEx (hrootkey, key, NULL, NULL, NULL, &cbData) == ERROR_SUCCESS
23f86fce 2201 && (lpvalue = xmalloc (cbData)) != NULL
76b3903d 2202 && RegQueryValueEx (hrootkey, key, NULL, lpdwtype, lpvalue, &cbData) == ERROR_SUCCESS)
f332b293 2203 {
4da4d9bb 2204 RegCloseKey (hrootkey);
f332b293
GV
2205 return (lpvalue);
2206 }
177c0ea7 2207
70fdbb46 2208 xfree (lpvalue);
177c0ea7 2209
f332b293 2210 RegCloseKey (hrootkey);
177c0ea7
JB
2211 }
2212
f332b293
GV
2213 return (NULL);
2214}
2215
2216void
aa7b87b0 2217init_environment (char ** argv)
f332b293 2218{
b3308d2e
KH
2219 static const char * const tempdirs[] = {
2220 "$TMPDIR", "$TEMP", "$TMP", "c:/"
2221 };
2d5324c5 2222
b3308d2e 2223 int i;
2d5324c5 2224
b3308d2e
KH
2225 const int imax = sizeof (tempdirs) / sizeof (tempdirs[0]);
2226
b1846422
EZ
2227 /* Implementation note: This function explicitly works with ANSI
2228 file names, not with UTF-8 encoded file names. This is because
2229 this function pushes variables into the Emacs's environment, and
2230 the environment variables are always assumed to be in the
2231 locale-specific encoding. Do NOT call any functions that accept
2232 UTF-8 file names from this function! */
2233
b3308d2e
KH
2234 /* Make sure they have a usable $TMPDIR. Many Emacs functions use
2235 temporary files and assume "/tmp" if $TMPDIR is unset, which
2236 will break on DOS/Windows. Refuse to work if we cannot find
2237 a directory, not even "c:/", usable for that purpose. */
2238 for (i = 0; i < imax ; i++)
2239 {
2240 const char *tmp = tempdirs[i];
2241
2242 if (*tmp == '$')
2243 tmp = getenv (tmp + 1);
2244 /* Note that `access' can lie to us if the directory resides on a
2245 read-only filesystem, like CD-ROM or a write-protected floppy.
2246 The only way to be really sure is to actually create a file and
2247 see if it succeeds. But I think that's too much to ask. */
6dad7178 2248
b1846422
EZ
2249 /* MSVCRT's _access crashes with D_OK, so we use our replacement. */
2250 if (tmp && sys_access (tmp, D_OK) == 0)
b3308d2e
KH
2251 {
2252 char * var = alloca (strlen (tmp) + 8);
2253 sprintf (var, "TMPDIR=%s", tmp);
aca583b2 2254 _putenv (strdup (var));
b3308d2e
KH
2255 break;
2256 }
2257 }
2258 if (i >= imax)
2259 cmd_error_internal
2260 (Fcons (Qerror,
2261 Fcons (build_string ("no usable temporary directories found!!"),
2262 Qnil)),
2263 "While setting TMPDIR: ");
2264
ca149beb
AI
2265 /* Check for environment variables and use registry settings if they
2266 don't exist. Fallback on default values where applicable. */
f332b293 2267 {
480b0c5b
GV
2268 int i;
2269 LPBYTE lpval;
2270 DWORD dwType;
69fb0241 2271 char locale_name[32];
2d5324c5 2272 char default_home[MAX_PATH];
fdc5744d 2273 int appdata = 0;
f332b293 2274
e00b99c8 2275 static const struct env_entry
ca149beb
AI
2276 {
2277 char * name;
2278 char * def_value;
e00b99c8 2279 } dflt_envvars[] =
ca149beb 2280 {
76151e2c
EZ
2281 /* If the default value is NULL, we will use the value from the
2282 outside environment or the Registry, but will not push the
2283 variable into the Emacs environment if it is defined neither
2284 in the Registry nor in the outside environment. */
ca149beb
AI
2285 {"HOME", "C:/"},
2286 {"PRELOAD_WINSOCK", NULL},
2287 {"emacs_dir", "C:/emacs"},
76151e2c 2288 {"EMACSLOADPATH", NULL},
7d9fb4de 2289 {"SHELL", "cmdproxy.exe"}, /* perhaps it is somewhere on PATH */
76151e2c
EZ
2290 {"EMACSDATA", NULL},
2291 {"EMACSPATH", NULL},
2292 {"INFOPATH", NULL},
2293 {"EMACSDOC", NULL},
69fb0241
JR
2294 {"TERM", "cmd"},
2295 {"LANG", NULL},
480b0c5b
GV
2296 };
2297
ed3751c8 2298#define N_ENV_VARS sizeof (dflt_envvars)/sizeof (dflt_envvars[0])
e00b99c8
EZ
2299
2300 /* We need to copy dflt_envvars[] and work on the copy because we
2301 don't want the dumped Emacs to inherit the values of
2302 environment variables we saw during dumping (which could be on
2303 a different system). The defaults above must be left intact. */
2304 struct env_entry env_vars[N_ENV_VARS];
2305
2306 for (i = 0; i < N_ENV_VARS; i++)
2307 env_vars[i] = dflt_envvars[i];
2308
2d5324c5
JR
2309 /* For backwards compatibility, check if a .emacs file exists in C:/
2310 If not, then we can try to default to the appdata directory under the
2311 user's profile, which is more likely to be writable. */
b1846422 2312 if (sys_access ("C:/.emacs", F_OK) != 0)
94eab1c8
JB
2313 {
2314 HRESULT profile_result;
2315 /* Dynamically load ShGetFolderPath, as it won't exist on versions
2316 of Windows 95 and NT4 that have not been updated to include
2317 MSIE 5. */
2318 ShGetFolderPath_fn get_folder_path;
2319 get_folder_path = (ShGetFolderPath_fn)
2320 GetProcAddress (GetModuleHandle ("shell32.dll"), "SHGetFolderPathA");
2321
2322 if (get_folder_path != NULL)
2323 {
2324 profile_result = get_folder_path (NULL, CSIDL_APPDATA, NULL,
2325 0, default_home);
2d5324c5 2326
94eab1c8
JB
2327 /* If we can't get the appdata dir, revert to old behavior. */
2328 if (profile_result == S_OK)
fdc5744d
JB
2329 {
2330 env_vars[0].def_value = default_home;
2331 appdata = 1;
2332 }
94eab1c8
JB
2333 }
2334 }
2d5324c5 2335
69fb0241
JR
2336 /* Get default locale info and use it for LANG. */
2337 if (GetLocaleInfo (LOCALE_USER_DEFAULT,
2338 LOCALE_SABBREVLANGNAME | LOCALE_USE_CP_ACP,
2339 locale_name, sizeof (locale_name)))
2340 {
e00b99c8 2341 for (i = 0; i < N_ENV_VARS; i++)
69fb0241
JR
2342 {
2343 if (strcmp (env_vars[i].name, "LANG") == 0)
2344 {
2345 env_vars[i].def_value = locale_name;
2346 break;
2347 }
2348 }
2349 }
2350
ca149beb
AI
2351#define SET_ENV_BUF_SIZE (4 * MAX_PATH) /* to cover EMACSLOADPATH */
2352
2353 /* Treat emacs_dir specially: set it unconditionally based on our
76151e2c 2354 location. */
ca149beb
AI
2355 {
2356 char *p;
2357 char modname[MAX_PATH];
2358
2359 if (!GetModuleFileName (NULL, modname, MAX_PATH))
1088b922 2360 emacs_abort ();
6ee4509a 2361 if ((p = _mbsrchr (modname, '\\')) == NULL)
1088b922 2362 emacs_abort ();
ca149beb
AI
2363 *p = 0;
2364
7d9fb4de
EZ
2365 if ((p = _mbsrchr (modname, '\\'))
2366 /* From bin means installed Emacs, from src means uninstalled. */
2367 && (xstrcasecmp (p, "\\bin") == 0 || xstrcasecmp (p, "\\src") == 0))
ca149beb
AI
2368 {
2369 char buf[SET_ENV_BUF_SIZE];
7d9fb4de 2370 int within_build_tree = xstrcasecmp (p, "\\src") == 0;
ca149beb
AI
2371
2372 *p = 0;
6ee4509a 2373 for (p = modname; *p; p = CharNext (p))
ca149beb 2374 if (*p == '\\') *p = '/';
177c0ea7 2375
ed3751c8 2376 _snprintf (buf, sizeof (buf)-1, "emacs_dir=%s", modname);
a302c7ae 2377 _putenv (strdup (buf));
7d9fb4de
EZ
2378 /* If we are running from the Posix-like build tree, define
2379 SHELL to point to our own cmdproxy. The loop below will
2380 then disregard PATH_EXEC and the default value. */
2381 if (within_build_tree)
2382 {
2383 _snprintf (buf, sizeof (buf) - 1,
2384 "SHELL=%s/nt/cmdproxy.exe", modname);
2385 _putenv (strdup (buf));
2386 }
ca149beb
AI
2387 }
2388 }
2389
e00b99c8 2390 for (i = 0; i < N_ENV_VARS; i++)
f332b293 2391 {
ca149beb 2392 if (!getenv (env_vars[i].name))
480b0c5b 2393 {
ca149beb 2394 int dont_free = 0;
7d9fb4de 2395 char bufc[SET_ENV_BUF_SIZE];
480b0c5b 2396
aa5ee2a3
JB
2397 if ((lpval = w32_get_resource (env_vars[i].name, &dwType)) == NULL
2398 /* Also ignore empty environment variables. */
2399 || *lpval == 0)
ca149beb 2400 {
70fdbb46 2401 xfree (lpval);
ca149beb 2402 dont_free = 1;
7d9fb4de
EZ
2403 if (strcmp (env_vars[i].name, "SHELL") == 0)
2404 {
2405 /* Look for cmdproxy.exe in every directory in
2406 PATH_EXEC. FIXME: This does not find cmdproxy
2407 in nt/ when we run uninstalled. */
2408 char fname[MAX_PATH];
2409 const char *pstart = PATH_EXEC, *pend;
2410
2411 do {
2412 pend = _mbschr (pstart, ';');
2413 if (!pend)
2414 pend = pstart + strlen (pstart);
2415 /* Be defensive against series of ;;; characters. */
2416 if (pend > pstart)
2417 {
2418 strncpy (fname, pstart, pend - pstart);
2419 fname[pend - pstart] = '/';
2420 strcpy (&fname[pend - pstart + 1], "cmdproxy.exe");
2421 ExpandEnvironmentStrings ((LPSTR) fname, bufc,
2422 sizeof (bufc));
b1846422 2423 if (sys_access (bufc, F_OK) == 0)
7d9fb4de
EZ
2424 {
2425 lpval = bufc;
2426 dwType = REG_SZ;
2427 break;
2428 }
2429 }
2430 if (*pend)
2431 pstart = pend + 1;
2432 else
2433 pstart = pend;
2434 if (!*pstart)
2435 {
2436 /* If not found in any directory, use the
2437 default as the last resort. */
2438 lpval = env_vars[i].def_value;
2439 dwType = REG_EXPAND_SZ;
2440 }
2441 } while (*pstart);
2442 }
2443 else
2444 {
2445 lpval = env_vars[i].def_value;
2446 dwType = REG_EXPAND_SZ;
2447 }
2448 if (strcmp (env_vars[i].name, "HOME") == 0 && !appdata)
3438fe21
EZ
2449 Vdelayed_warnings_list
2450 = Fcons (listn (CONSTYPE_HEAP, 2,
073c88c2 2451 intern ("initialization"),
694b6c97 2452 build_string ("Setting HOME to C:\\ by default is deprecated")),
073c88c2 2453 Vdelayed_warnings_list);
480b0c5b 2454 }
ca149beb
AI
2455
2456 if (lpval)
480b0c5b 2457 {
892eb237 2458 char buf1[SET_ENV_BUF_SIZE], buf2[SET_ENV_BUF_SIZE];
ca149beb 2459
892eb237 2460 if (dwType == REG_EXPAND_SZ)
ed3751c8 2461 ExpandEnvironmentStrings ((LPSTR) lpval, buf1, sizeof (buf1));
ca149beb 2462 else if (dwType == REG_SZ)
892eb237
EZ
2463 strcpy (buf1, lpval);
2464 if (dwType == REG_EXPAND_SZ || dwType == REG_SZ)
ca149beb 2465 {
ed3751c8 2466 _snprintf (buf2, sizeof (buf2)-1, "%s=%s", env_vars[i].name,
892eb237
EZ
2467 buf1);
2468 _putenv (strdup (buf2));
ca149beb 2469 }
f332b293 2470
ca149beb
AI
2471 if (!dont_free)
2472 xfree (lpval);
2473 }
480b0c5b
GV
2474 }
2475 }
2476 }
2477
75b08edb
GV
2478 /* Rebuild system configuration to reflect invoking system. */
2479 Vsystem_configuration = build_string (EMACS_CONFIGURATION);
2480
76b3903d
GV
2481 /* Another special case: on NT, the PATH variable is actually named
2482 "Path" although cmd.exe (perhaps NT itself) arranges for
2483 environment variable lookup and setting to be case insensitive.
2484 However, Emacs assumes a fully case sensitive environment, so we
2485 need to change "Path" to "PATH" to match the expectations of
2486 various elisp packages. We do this by the sneaky method of
2487 modifying the string in the C runtime environ entry.
2488
2489 The same applies to COMSPEC. */
2490 {
2491 char ** envp;
2492
2493 for (envp = environ; *envp; envp++)
2494 if (_strnicmp (*envp, "PATH=", 5) == 0)
2495 memcpy (*envp, "PATH=", 5);
2496 else if (_strnicmp (*envp, "COMSPEC=", 8) == 0)
2497 memcpy (*envp, "COMSPEC=", 8);
2498 }
2499
9239d970 2500 /* Remember the initial working directory for getcwd. */
6dad7178
EZ
2501 /* FIXME: Do we need to resolve possible symlinks in startup_dir?
2502 Does it matter anywhere in Emacs? */
5c4a19a9
EZ
2503 if (w32_unicode_filenames)
2504 {
2505 wchar_t wstartup_dir[MAX_PATH];
2506
2507 if (!GetCurrentDirectoryW (MAX_PATH, wstartup_dir))
2508 emacs_abort ();
2509 filename_from_utf16 (wstartup_dir, startup_dir);
2510 }
2511 else
2512 {
2513 char astartup_dir[MAX_PATH];
2514
2515 if (!GetCurrentDirectoryA (MAX_PATH, astartup_dir))
2516 emacs_abort ();
2517 filename_from_ansi (astartup_dir, startup_dir);
2518 }
76b3903d
GV
2519
2520 {
aa7b87b0 2521 static char modname[MAX_PATH];
76b3903d
GV
2522
2523 if (!GetModuleFileName (NULL, modname, MAX_PATH))
1088b922 2524 emacs_abort ();
aa7b87b0 2525 argv[0] = modname;
76b3903d
GV
2526 }
2527
20af4831
JR
2528 /* Determine if there is a middle mouse button, to allow parse_button
2529 to decide whether right mouse events should be mouse-2 or
2530 mouse-3. */
e0c181dd 2531 w32_num_mouse_buttons = GetSystemMetrics (SM_CMOUSEBUTTONS);
20af4831 2532
480b0c5b
GV
2533 init_user_info ();
2534}
2535
6dad7178
EZ
2536/* Called from expand-file-name when default-directory is not a string. */
2537
bf794306
EZ
2538char *
2539emacs_root_dir (void)
2540{
b1846422 2541 static char root_dir[MAX_UTF8_PATH];
bf794306
EZ
2542 const char *p;
2543
2544 p = getenv ("emacs_dir");
2545 if (p == NULL)
1088b922 2546 emacs_abort ();
b1846422 2547 filename_from_ansi (p, root_dir);
bf794306 2548 root_dir[parse_root (root_dir, NULL)] = '\0';
1fd201bb 2549 dostounix_filename (root_dir);
bf794306
EZ
2550 return root_dir;
2551}
2552
35f0d482
KH
2553#include <sys/timeb.h>
2554
2555/* Emulate gettimeofday (Ulrich Leodolter, 1/11/95). */
97dababa 2556int
e2d8a6f0 2557gettimeofday (struct timeval *__restrict tv, struct timezone *__restrict tz)
35f0d482 2558{
6e602566 2559 struct _timeb tb;
35f0d482
KH
2560 _ftime (&tb);
2561
2562 tv->tv_sec = tb.time;
2563 tv->tv_usec = tb.millitm * 1000L;
97a93095
EZ
2564 /* Implementation note: _ftime sometimes doesn't update the dstflag
2565 according to the new timezone when the system timezone is
2566 changed. We could fix that by using GetSystemTime and
2567 GetTimeZoneInformation, but that doesn't seem necessary, since
2568 Emacs always calls gettimeofday with the 2nd argument NULL (see
e9a9ae03 2569 current_emacs_time). */
177c0ea7 2570 if (tz)
35f0d482
KH
2571 {
2572 tz->tz_minuteswest = tb.timezone; /* minutes west of Greenwich */
2573 tz->tz_dsttime = tb.dstflag; /* type of dst correction */
2574 }
97dababa 2575 return 0;
35f0d482 2576}
35f0d482 2577
388cdec0
EZ
2578/* Emulate fdutimens. */
2579
2580/* Set the access and modification time stamps of FD (a.k.a. FILE) to be
2581 TIMESPEC[0] and TIMESPEC[1], respectively.
2582 FD must be either negative -- in which case it is ignored --
2583 or a file descriptor that is open on FILE.
2584 If FD is nonnegative, then FILE can be NULL, which means
2585 use just futimes instead of utimes.
2586 If TIMESPEC is null, FAIL.
2587 Return 0 on success, -1 (setting errno) on failure. */
2588
2589int
2590fdutimens (int fd, char const *file, struct timespec const timespec[2])
2591{
388cdec0
EZ
2592 if (!timespec)
2593 {
2594 errno = ENOSYS;
2595 return -1;
2596 }
2597 if (fd < 0 && !file)
2598 {
2599 errno = EBADF;
2600 return -1;
2601 }
b34454d0
EZ
2602 /* _futime's prototype defines 2nd arg as having the type 'struct
2603 _utimbuf', while utime needs to accept 'struct utimbuf' for
2604 compatibility with Posix. So we need to use 2 different (but
2605 equivalent) types to avoid compiler warnings, sigh. */
388cdec0 2606 if (fd >= 0)
b34454d0
EZ
2607 {
2608 struct _utimbuf _ut;
2609
2610 _ut.actime = timespec[0].tv_sec;
2611 _ut.modtime = timespec[1].tv_sec;
2612 return _futime (fd, &_ut);
2613 }
388cdec0 2614 else
b34454d0
EZ
2615 {
2616 struct utimbuf ut;
2617
2618 ut.actime = timespec[0].tv_sec;
2619 ut.modtime = timespec[1].tv_sec;
2620 /* Call 'utime', which is implemented below, not the MS library
2621 function, which fails on directories. */
2622 return utime (file, &ut);
2623 }
388cdec0
EZ
2624}
2625
2626
480b0c5b 2627/* ------------------------------------------------------------------------- */
b46a6a83 2628/* IO support and wrapper functions for the Windows API. */
480b0c5b 2629/* ------------------------------------------------------------------------- */
95ed0025 2630
480b0c5b 2631/* Place a wrapper around the MSVC version of ctime. It returns NULL
177c0ea7 2632 on network directories, so we handle that case here.
480b0c5b
GV
2633 (Ulrich Leodolter, 1/11/95). */
2634char *
2635sys_ctime (const time_t *t)
2636{
2637 char *str = (char *) ctime (t);
2638 return (str ? str : "Sun Jan 01 00:00:00 1970");
2639}
2640
2641/* Emulate sleep...we could have done this with a define, but that
2642 would necessitate including windows.h in the files that used it.
2643 This is much easier. */
2644void
2645sys_sleep (int seconds)
2646{
2647 Sleep (seconds * 1000);
2648}
2649
76b3903d 2650/* Internal MSVC functions for low-level descriptor munging */
480b0c5b
GV
2651extern int __cdecl _set_osfhnd (int fd, long h);
2652extern int __cdecl _free_osfhnd (int fd);
2653
2654/* parallel array of private info on file handles */
2655filedesc fd_info [ MAXDESC ];
2656
76b3903d
GV
2657typedef struct volume_info_data {
2658 struct volume_info_data * next;
2659
2660 /* time when info was obtained */
2661 DWORD timestamp;
2662
2663 /* actual volume info */
2664 char * root_dir;
480b0c5b
GV
2665 DWORD serialnum;
2666 DWORD maxcomp;
2667 DWORD flags;
76b3903d
GV
2668 char * name;
2669 char * type;
2670} volume_info_data;
2671
2672/* Global referenced by various functions. */
2673static volume_info_data volume_info;
2674
2675/* Vector to indicate which drives are local and fixed (for which cached
2676 data never expires). */
2677static BOOL fixed_drives[26];
2678
2679/* Consider cached volume information to be stale if older than 10s,
2680 at least for non-local drives. Info for fixed drives is never stale. */
2681#define DRIVE_INDEX( c ) ( (c) <= 'Z' ? (c) - 'A' : (c) - 'a' )
2682#define VOLINFO_STILL_VALID( root_dir, info ) \
2683 ( ( isalpha (root_dir[0]) && \
2684 fixed_drives[ DRIVE_INDEX (root_dir[0]) ] ) \
2685 || GetTickCount () - info->timestamp < 10000 )
2686
2687/* Cache support functions. */
2688
2689/* Simple linked list with linear search is sufficient. */
2690static volume_info_data *volume_cache = NULL;
2691
2692static volume_info_data *
2693lookup_volume_info (char * root_dir)
2694{
2695 volume_info_data * info;
2696
2697 for (info = volume_cache; info; info = info->next)
05131107 2698 if (xstrcasecmp (info->root_dir, root_dir) == 0)
76b3903d
GV
2699 break;
2700 return info;
2701}
2702
2703static void
2704add_volume_info (char * root_dir, volume_info_data * info)
2705{
a302c7ae 2706 info->root_dir = xstrdup (root_dir);
f5441ba4 2707 unixtodos_filename (info->root_dir);
76b3903d
GV
2708 info->next = volume_cache;
2709 volume_cache = info;
2710}
2711
2712
2713/* Wrapper for GetVolumeInformation, which uses caching to avoid
2714 performance penalty (~2ms on 486 for local drives, 7.5ms for local
2715 cdrom drive, ~5-10ms or more for remote drives on LAN). */
bedf4aab 2716static volume_info_data *
76b3903d
GV
2717GetCachedVolumeInformation (char * root_dir)
2718{
2719 volume_info_data * info;
f5441ba4
EZ
2720 char default_root[ MAX_UTF8_PATH ];
2721 char name[MAX_PATH+1];
2722 char type[MAX_PATH+1];
76b3903d
GV
2723
2724 /* NULL for root_dir means use root from current directory. */
2725 if (root_dir == NULL)
2726 {
f5441ba4
EZ
2727 if (w32_unicode_filenames)
2728 {
2729 wchar_t curdirw[MAX_PATH];
2730
2731 if (GetCurrentDirectoryW (MAX_PATH, curdirw) == 0)
2732 return NULL;
2733 filename_from_utf16 (curdirw, default_root);
2734 }
2735 else
2736 {
2737 char curdira[MAX_PATH];
2738
2739 if (GetCurrentDirectoryA (MAX_PATH, curdira) == 0)
2740 return NULL;
2741 filename_from_ansi (curdira, default_root);
2742 }
2743 parse_root (default_root, (const char **)&root_dir);
76b3903d
GV
2744 *root_dir = 0;
2745 root_dir = default_root;
2746 }
2747
2748 /* Local fixed drives can be cached permanently. Removable drives
2749 cannot be cached permanently, since the volume name and serial
2750 number (if nothing else) can change. Remote drives should be
2751 treated as if they are removable, since there is no sure way to
2752 tell whether they are or not. Also, the UNC association of drive
2753 letters mapped to remote volumes can be changed at any time (even
2754 by other processes) without notice.
177c0ea7 2755
76b3903d
GV
2756 As a compromise, so we can benefit from caching info for remote
2757 volumes, we use a simple expiry mechanism to invalidate cache
2758 entries that are more than ten seconds old. */
2759
2760#if 0
2761 /* No point doing this, because WNetGetConnection is even slower than
2762 GetVolumeInformation, consistently taking ~50ms on a 486 (FWIW,
2763 GetDriveType is about the only call of this type which does not
2764 involve network access, and so is extremely quick). */
2765
2766 /* Map drive letter to UNC if remote. */
ed3751c8 2767 if (isalpha (root_dir[0]) && !fixed[DRIVE_INDEX (root_dir[0])])
76b3903d
GV
2768 {
2769 char remote_name[ 256 ];
2770 char drive[3] = { root_dir[0], ':' };
2771
2772 if (WNetGetConnection (drive, remote_name, sizeof (remote_name))
2773 == NO_ERROR)
2774 /* do something */ ;
2775 }
2776#endif
2777
2778 info = lookup_volume_info (root_dir);
2779
2780 if (info == NULL || ! VOLINFO_STILL_VALID (root_dir, info))
94eab1c8 2781 {
94eab1c8
JB
2782 DWORD serialnum;
2783 DWORD maxcomp;
2784 DWORD flags;
94eab1c8
JB
2785
2786 /* Info is not cached, or is stale. */
f5441ba4
EZ
2787 if (w32_unicode_filenames)
2788 {
2789 wchar_t root_w[MAX_PATH];
2790 wchar_t name_w[MAX_PATH+1];
2791 wchar_t type_w[MAX_PATH+1];
2792
2793 filename_to_utf16 (root_dir, root_w);
2794 if (!GetVolumeInformationW (root_w,
2795 name_w, sizeof (name_w),
2796 &serialnum,
2797 &maxcomp,
2798 &flags,
2799 type_w, sizeof (type_w)))
2800 return NULL;
2801 /* Hmm... not really 100% correct, as these 2 are not file
2802 names... */
2803 filename_from_utf16 (name_w, name);
2804 filename_from_utf16 (type_w, type);
2805 }
2806 else
2807 {
2808 char root_a[MAX_PATH];
2809 char name_a[MAX_PATH+1];
2810 char type_a[MAX_PATH+1];
2811
2812 filename_to_ansi (root_dir, root_a);
2813 if (!GetVolumeInformationA (root_a,
2814 name_a, sizeof (name_a),
2815 &serialnum,
2816 &maxcomp,
2817 &flags,
2818 type_a, sizeof (type_a)))
2819 return NULL;
2820 filename_from_ansi (name_a, name);
2821 filename_from_ansi (type_a, type);
2822 }
76b3903d 2823
94eab1c8
JB
2824 /* Cache the volume information for future use, overwriting existing
2825 entry if present. */
2826 if (info == NULL)
2827 {
23f86fce 2828 info = xmalloc (sizeof (volume_info_data));
94eab1c8
JB
2829 add_volume_info (root_dir, info);
2830 }
2831 else
2832 {
2833 xfree (info->name);
2834 xfree (info->type);
2835 }
2836
2837 info->name = xstrdup (name);
f5441ba4 2838 unixtodos_filename (info->name);
94eab1c8
JB
2839 info->serialnum = serialnum;
2840 info->maxcomp = maxcomp;
2841 info->flags = flags;
2842 info->type = xstrdup (type);
2843 info->timestamp = GetTickCount ();
2844 }
76b3903d
GV
2845
2846 return info;
2847}
480b0c5b 2848
6dad7178
EZ
2849/* Get information on the volume where NAME is held; set path pointer to
2850 start of pathname in NAME (past UNC header\volume header if present),
2851 if pPath is non-NULL.
2852
2853 Note: if NAME includes symlinks, the information is for the volume
2854 of the symlink, not of its target. That's because, even though
2855 GetVolumeInformation returns information about the symlink target
2856 of its argument, we only pass the root directory to
2857 GetVolumeInformation, not the full NAME. */
bedf4aab 2858static int
480b0c5b 2859get_volume_info (const char * name, const char ** pPath)
95ed0025 2860{
f5441ba4 2861 char temp[MAX_UTF8_PATH];
480b0c5b 2862 char *rootname = NULL; /* default to current volume */
76b3903d 2863 volume_info_data * info;
f5441ba4 2864 int root_len = parse_root (name, pPath);
480b0c5b
GV
2865
2866 if (name == NULL)
2867 return FALSE;
2868
f5441ba4
EZ
2869 /* Copy the root name of the volume, if given. */
2870 if (root_len)
95ed0025 2871 {
f5441ba4
EZ
2872 strncpy (temp, name, root_len);
2873 temp[root_len] = '\0';
2874 unixtodos_filename (temp);
480b0c5b 2875 rootname = temp;
95ed0025 2876 }
480b0c5b 2877
76b3903d
GV
2878 info = GetCachedVolumeInformation (rootname);
2879 if (info != NULL)
95ed0025 2880 {
76b3903d
GV
2881 /* Set global referenced by other functions. */
2882 volume_info = *info;
480b0c5b 2883 return TRUE;
95ed0025 2884 }
480b0c5b
GV
2885 return FALSE;
2886}
2887
2888/* Determine if volume is FAT format (ie. only supports short 8.3
6dad7178
EZ
2889 names); also set path pointer to start of pathname in name, if
2890 pPath is non-NULL. */
bedf4aab 2891static int
480b0c5b
GV
2892is_fat_volume (const char * name, const char ** pPath)
2893{
2894 if (get_volume_info (name, pPath))
2895 return (volume_info.maxcomp == 12);
2896 return FALSE;
2897}
2898
f5441ba4
EZ
2899/* Convert all slashes in a filename to backslashes, and map filename
2900 to a valid 8.3 name if necessary. The result is a pointer to a
2901 static buffer, so CAVEAT EMPTOR! */
480b0c5b 2902const char *
fbd6baed 2903map_w32_filename (const char * name, const char ** pPath)
480b0c5b 2904{
f5441ba4 2905 static char shortname[MAX_UTF8_PATH];
480b0c5b
GV
2906 char * str = shortname;
2907 char c;
480b0c5b 2908 char * path;
76b3903d 2909 const char * save_name = name;
480b0c5b 2910
f5441ba4 2911 if (strlen (name) >= sizeof (shortname))
ca149beb
AI
2912 {
2913 /* Return a filename which will cause callers to fail. */
2914 strcpy (shortname, "?");
2915 return shortname;
2916 }
2917
a302c7ae 2918 if (is_fat_volume (name, (const char **)&path)) /* truncate to 8.3 */
95ed0025 2919 {
480b0c5b
GV
2920 register int left = 8; /* maximum number of chars in part */
2921 register int extn = 0; /* extension added? */
2922 register int dots = 2; /* maximum number of dots allowed */
2923
2924 while (name < path)
2925 *str++ = *name++; /* skip past UNC header */
2926
2927 while ((c = *name++))
2928 {
2929 switch ( c )
2930 {
6dad7178 2931 case ':':
480b0c5b
GV
2932 case '\\':
2933 case '/':
6dad7178 2934 *str++ = (c == ':' ? ':' : '\\');
480b0c5b
GV
2935 extn = 0; /* reset extension flags */
2936 dots = 2; /* max 2 dots */
2937 left = 8; /* max length 8 for main part */
2938 break;
2939 case '.':
2940 if ( dots )
2941 {
2942 /* Convert path components of the form .xxx to _xxx,
2943 but leave . and .. as they are. This allows .emacs
2944 to be read as _emacs, for example. */
2945
2946 if (! *name ||
2947 *name == '.' ||
2948 IS_DIRECTORY_SEP (*name))
2949 {
2950 *str++ = '.';
2951 dots--;
2952 }
2953 else
2954 {
2955 *str++ = '_';
2956 left--;
2957 dots = 0;
2958 }
2959 }
2960 else if ( !extn )
2961 {
2962 *str++ = '.';
2963 extn = 1; /* we've got an extension */
2964 left = 3; /* 3 chars in extension */
2965 }
2966 else
2967 {
2968 /* any embedded dots after the first are converted to _ */
2969 *str++ = '_';
2970 }
2971 break;
2972 case '~':
2973 case '#': /* don't lose these, they're important */
2974 if ( ! left )
2975 str[-1] = c; /* replace last character of part */
2976 /* FALLTHRU */
2977 default:
f5441ba4 2978 if ( left && 'A' <= c && c <= 'Z' )
480b0c5b
GV
2979 {
2980 *str++ = tolower (c); /* map to lower case (looks nicer) */
2981 left--;
2982 dots = 0; /* started a path component */
2983 }
2984 break;
2985 }
2986 }
2987 *str = '\0';
fc85cb29
RS
2988 }
2989 else
2990 {
2991 strcpy (shortname, name);
2992 unixtodos_filename (shortname);
95ed0025 2993 }
480b0c5b
GV
2994
2995 if (pPath)
76b3903d 2996 *pPath = shortname + (path - save_name);
480b0c5b 2997
fc85cb29 2998 return shortname;
480b0c5b
GV
2999}
3000
b3308d2e
KH
3001static int
3002is_exec (const char * name)
3003{
3004 char * p = strrchr (name, '.');
3005 return
3006 (p != NULL
05131107
JR
3007 && (xstrcasecmp (p, ".exe") == 0 ||
3008 xstrcasecmp (p, ".com") == 0 ||
3009 xstrcasecmp (p, ".bat") == 0 ||
3010 xstrcasecmp (p, ".cmd") == 0));
b3308d2e
KH
3011}
3012
0b9de7cd
EZ
3013/* Emulate the Unix directory procedures opendir, closedir, and
3014 readdir. We rename them to sys_* names because some versions of
3015 MinGW startup code call opendir and readdir to glob wildcards, and
3016 the code that calls them doesn't grok UTF-8 encoded file names we
3017 produce in dirent->d_name[]. */
76b3903d 3018
95ef7787 3019struct dirent dir_static; /* simulated directory contents */
76b3903d
GV
3020static HANDLE dir_find_handle = INVALID_HANDLE_VALUE;
3021static int dir_is_fat;
0b9de7cd
EZ
3022static char dir_pathname[MAX_UTF8_PATH];
3023static WIN32_FIND_DATAW dir_find_data_w;
3024static WIN32_FIND_DATAA dir_find_data_a;
df87c56c
EZ
3025#define DIR_FIND_DATA_W 1
3026#define DIR_FIND_DATA_A 2
3027static int last_dir_find_data = -1;
76b3903d 3028
9d3355d1
GV
3029/* Support shares on a network resource as subdirectories of a read-only
3030 root directory. */
3031static HANDLE wnet_enum_handle = INVALID_HANDLE_VALUE;
bedf4aab 3032static HANDLE open_unc_volume (const char *);
0b9de7cd 3033static void *read_unc_volume (HANDLE, wchar_t *, char *, int);
bedf4aab 3034static void close_unc_volume (HANDLE);
9d3355d1 3035
76b3903d 3036DIR *
0b9de7cd 3037sys_opendir (const char *filename)
76b3903d
GV
3038{
3039 DIR *dirp;
3040
3041 /* Opening is done by FindFirstFile. However, a read is inherent to
3042 this operation, so we defer the open until read time. */
3043
76b3903d
GV
3044 if (dir_find_handle != INVALID_HANDLE_VALUE)
3045 return NULL;
9d3355d1
GV
3046 if (wnet_enum_handle != INVALID_HANDLE_VALUE)
3047 return NULL;
3048
6dad7178
EZ
3049 /* Note: We don't support traversal of UNC volumes via symlinks.
3050 Doing so would mean punishing 99.99% of use cases by resolving
3051 all the possible symlinks in FILENAME, recursively. */
9d3355d1
GV
3052 if (is_unc_volume (filename))
3053 {
3054 wnet_enum_handle = open_unc_volume (filename);
3055 if (wnet_enum_handle == INVALID_HANDLE_VALUE)
3056 return NULL;
3057 }
3058
3059 if (!(dirp = (DIR *) malloc (sizeof (DIR))))
3060 return NULL;
76b3903d
GV
3061
3062 dirp->dd_fd = 0;
3063 dirp->dd_loc = 0;
3064 dirp->dd_size = 0;
3065
0b9de7cd
EZ
3066 strncpy (dir_pathname, map_w32_filename (filename, NULL), MAX_UTF8_PATH - 1);
3067 dir_pathname[MAX_UTF8_PATH - 1] = '\0';
6dad7178
EZ
3068 /* Note: We don't support symlinks to file names on FAT volumes.
3069 Doing so would mean punishing 99.99% of use cases by resolving
3070 all the possible symlinks in FILENAME, recursively. */
76b3903d
GV
3071 dir_is_fat = is_fat_volume (filename, NULL);
3072
3073 return dirp;
3074}
3075
3076void
0b9de7cd 3077sys_closedir (DIR *dirp)
76b3903d
GV
3078{
3079 /* If we have a find-handle open, close it. */
3080 if (dir_find_handle != INVALID_HANDLE_VALUE)
3081 {
3082 FindClose (dir_find_handle);
3083 dir_find_handle = INVALID_HANDLE_VALUE;
3084 }
9d3355d1
GV
3085 else if (wnet_enum_handle != INVALID_HANDLE_VALUE)
3086 {
3087 close_unc_volume (wnet_enum_handle);
3088 wnet_enum_handle = INVALID_HANDLE_VALUE;
3089 }
76b3903d
GV
3090 xfree ((char *) dirp);
3091}
3092
95ef7787 3093struct dirent *
0b9de7cd 3094sys_readdir (DIR *dirp)
76b3903d 3095{
b07103dc
EZ
3096 int downcase = !NILP (Vw32_downcase_file_names);
3097
9d3355d1
GV
3098 if (wnet_enum_handle != INVALID_HANDLE_VALUE)
3099 {
177c0ea7 3100 if (!read_unc_volume (wnet_enum_handle,
0b9de7cd
EZ
3101 dir_find_data_w.cFileName,
3102 dir_find_data_a.cFileName,
59eb0929 3103 MAX_PATH))
9d3355d1
GV
3104 return NULL;
3105 }
76b3903d 3106 /* If we aren't dir_finding, do a find-first, otherwise do a find-next. */
9d3355d1 3107 else if (dir_find_handle == INVALID_HANDLE_VALUE)
76b3903d 3108 {
0b9de7cd 3109 char filename[MAX_UTF8_PATH + 2];
76b3903d
GV
3110 int ln;
3111
3112 strcpy (filename, dir_pathname);
3113 ln = strlen (filename) - 1;
0b9de7cd
EZ
3114 if (!IS_DIRECTORY_SEP (filename[ln]))
3115 strcat (filename, "\\");
3116 strcat (filename, "*");
3117
3118 /* Note: No need to resolve symlinks in FILENAME, because
3119 FindFirst opens the directory that is the target of a
3120 symlink. */
3121 if (w32_unicode_filenames)
806fed21 3122 {
0b9de7cd
EZ
3123 wchar_t fnw[MAX_PATH];
3124
3125 filename_to_utf16 (filename, fnw);
3126 dir_find_handle = FindFirstFileW (fnw, &dir_find_data_w);
806fed21
EZ
3127 }
3128 else
3129 {
0b9de7cd 3130 char fna[MAX_PATH];
806fed21 3131
0b9de7cd 3132 filename_to_ansi (filename, fna);
df87c56c
EZ
3133 /* If FILENAME is not representable by the current ANSI
3134 codepage, we don't want FindFirstFileA to interpret the
3135 '?' characters as a wildcard. */
3136 if (_mbspbrk (fna, "?"))
3137 dir_find_handle = INVALID_HANDLE_VALUE;
3138 else
3139 dir_find_handle = FindFirstFileA (fna, &dir_find_data_a);
806fed21 3140 }
76b3903d
GV
3141
3142 if (dir_find_handle == INVALID_HANDLE_VALUE)
3143 return NULL;
3144 }
0b9de7cd
EZ
3145 else if (w32_unicode_filenames)
3146 {
3147 if (!FindNextFileW (dir_find_handle, &dir_find_data_w))
3148 return NULL;
3149 }
76b3903d
GV
3150 else
3151 {
0b9de7cd 3152 if (!FindNextFileA (dir_find_handle, &dir_find_data_a))
76b3903d
GV
3153 return NULL;
3154 }
177c0ea7 3155
76b3903d
GV
3156 /* Emacs never uses this value, so don't bother making it match
3157 value returned by stat(). */
3158 dir_static.d_ino = 1;
177c0ea7 3159
0b9de7cd 3160 if (w32_unicode_filenames)
b07103dc 3161 {
0b9de7cd
EZ
3162 if (downcase || dir_is_fat)
3163 {
3164 wchar_t tem[MAX_PATH];
177c0ea7 3165
0b9de7cd
EZ
3166 wcscpy (tem, dir_find_data_w.cFileName);
3167 CharLowerW (tem);
3168 filename_from_utf16 (tem, dir_static.d_name);
3169 }
3170 else
3171 filename_from_utf16 (dir_find_data_w.cFileName, dir_static.d_name);
df87c56c 3172 last_dir_find_data = DIR_FIND_DATA_W;
192788d7
EZ
3173 }
3174 else
76b3903d 3175 {
0b9de7cd
EZ
3176 char tem[MAX_PATH];
3177
3178 /* If the file name in cFileName[] includes `?' characters, it
3179 means the original file name used characters that cannot be
3180 represented by the current ANSI codepage. To avoid total
3181 lossage, retrieve the short 8+3 alias of the long file
3182 name. */
3183 if (_mbspbrk (dir_find_data_a.cFileName, "?"))
6d2851de 3184 {
0b9de7cd
EZ
3185 strcpy (tem, dir_find_data_a.cAlternateFileName);
3186 /* 8+3 aliases are returned in all caps, which could break
3187 various alists that look at filenames' extensions. */
3188 downcase = 1;
3189 }
3190 else if (downcase || dir_is_fat)
3191 strcpy (tem, dir_find_data_a.cFileName);
3192 else
3193 filename_from_ansi (dir_find_data_a.cFileName, dir_static.d_name);
3194 if (downcase || dir_is_fat)
3195 {
3196 _mbslwr (tem);
3197 filename_from_ansi (tem, dir_static.d_name);
6d2851de 3198 }
df87c56c 3199 last_dir_find_data = DIR_FIND_DATA_A;
76b3903d 3200 }
177c0ea7 3201
0b9de7cd
EZ
3202 dir_static.d_namlen = strlen (dir_static.d_name);
3203 dir_static.d_reclen = sizeof (struct dirent) - MAX_UTF8_PATH + 3 +
3204 dir_static.d_namlen - dir_static.d_namlen % 4;
3205
76b3903d
GV
3206 return &dir_static;
3207}
3208
bedf4aab 3209static HANDLE
e0c181dd 3210open_unc_volume (const char *path)
9d3355d1 3211{
0b9de7cd
EZ
3212 const char *fn = map_w32_filename (path, NULL);
3213 DWORD result;
9d3355d1 3214 HANDLE henum;
9d3355d1 3215
0b9de7cd
EZ
3216 if (w32_unicode_filenames)
3217 {
3218 NETRESOURCEW nrw;
3219 wchar_t fnw[MAX_PATH];
3220
3221 nrw.dwScope = RESOURCE_GLOBALNET;
3222 nrw.dwType = RESOURCETYPE_DISK;
3223 nrw.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER;
3224 nrw.dwUsage = RESOURCEUSAGE_CONTAINER;
3225 nrw.lpLocalName = NULL;
3226 filename_to_utf16 (fn, fnw);
3227 nrw.lpRemoteName = fnw;
3228 nrw.lpComment = NULL;
3229 nrw.lpProvider = NULL;
9d3355d1 3230
0b9de7cd
EZ
3231 result = WNetOpenEnumW (RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
3232 RESOURCEUSAGE_CONNECTABLE, &nrw, &henum);
3233 }
3234 else
3235 {
3236 NETRESOURCEA nra;
3237 char fna[MAX_PATH];
9d3355d1 3238
0b9de7cd
EZ
3239 nra.dwScope = RESOURCE_GLOBALNET;
3240 nra.dwType = RESOURCETYPE_DISK;
3241 nra.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER;
3242 nra.dwUsage = RESOURCEUSAGE_CONTAINER;
3243 nra.lpLocalName = NULL;
3244 filename_to_ansi (fn, fna);
3245 nra.lpRemoteName = fna;
3246 nra.lpComment = NULL;
3247 nra.lpProvider = NULL;
3248
3249 result = WNetOpenEnumA (RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
3250 RESOURCEUSAGE_CONNECTABLE, &nra, &henum);
3251 }
9d3355d1
GV
3252 if (result == NO_ERROR)
3253 return henum;
3254 else
3255 return INVALID_HANDLE_VALUE;
3256}
3257
0b9de7cd
EZ
3258static void *
3259read_unc_volume (HANDLE henum, wchar_t *fname_w, char *fname_a, int size)
9d3355d1 3260{
a302c7ae 3261 DWORD count;
9d3355d1 3262 int result;
9d3355d1 3263 char *buffer;
0b9de7cd
EZ
3264 DWORD bufsize = 512;
3265 void *retval;
9d3355d1
GV
3266
3267 count = 1;
0b9de7cd
EZ
3268 if (w32_unicode_filenames)
3269 {
3270 wchar_t *ptrw;
9d3355d1 3271
0b9de7cd
EZ
3272 bufsize *= 2;
3273 buffer = alloca (bufsize);
3274 result = WNetEnumResourceW (henum, &count, buffer, &bufsize);
3275 if (result != NO_ERROR)
3276 return NULL;
3277 /* WNetEnumResource returns \\resource\share...skip forward to "share". */
3278 ptrw = ((LPNETRESOURCEW) buffer)->lpRemoteName;
3279 ptrw += 2;
3280 while (*ptrw && *ptrw != L'/' && *ptrw != L'\\') ptrw++;
3281 ptrw++;
3282 wcsncpy (fname_w, ptrw, size);
3283 retval = fname_w;
3284 }
806fed21
EZ
3285 else
3286 {
0b9de7cd
EZ
3287 int dbcs_p = max_filename_mbslen () > 1;
3288 char *ptra;
3289
3290 buffer = alloca (bufsize);
3291 result = WNetEnumResourceA (henum, &count, buffer, &bufsize);
3292 if (result != NO_ERROR)
3293 return NULL;
3294 ptra = ((LPNETRESOURCEA) buffer)->lpRemoteName;
3295 ptra += 2;
3296 if (!dbcs_p)
3297 while (*ptra && !IS_DIRECTORY_SEP (*ptra)) ptra++;
3298 else
3299 {
3300 while (*ptra && !IS_DIRECTORY_SEP (*ptra))
3301 ptra = CharNextExA (file_name_codepage, ptra, 0);
3302 }
3303 ptra++;
3304 strncpy (fname_a, ptra, size);
3305 retval = fname_a;
806fed21 3306 }
9d3355d1 3307
0b9de7cd 3308 return retval;
9d3355d1
GV
3309}
3310
bedf4aab 3311static void
9d3355d1
GV
3312close_unc_volume (HANDLE henum)
3313{
3314 if (henum != INVALID_HANDLE_VALUE)
3315 WNetCloseEnum (henum);
3316}
3317
bedf4aab 3318static DWORD
e0c181dd 3319unc_volume_file_attributes (const char *path)
9d3355d1
GV
3320{
3321 HANDLE henum;
3322 DWORD attrs;
3323
3324 henum = open_unc_volume (path);
3325 if (henum == INVALID_HANDLE_VALUE)
3326 return -1;
3327
3328 attrs = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_DIRECTORY;
3329
3330 close_unc_volume (henum);
3331
3332 return attrs;
3333}
3334
302d7d54
JR
3335/* Ensure a network connection is authenticated. */
3336static void
3337logon_network_drive (const char *path)
3338{
0b9de7cd 3339 char share[MAX_UTF8_PATH];
806fed21 3340 int n_slashes;
40a339c8 3341 char drive[4];
be4c6380 3342 UINT drvtype;
806fed21 3343 char *p;
0b9de7cd 3344 DWORD val;
40a339c8 3345
be4c6380
EZ
3346 if (IS_DIRECTORY_SEP (path[0]) && IS_DIRECTORY_SEP (path[1]))
3347 drvtype = DRIVE_REMOTE;
3348 else if (path[0] == '\0' || path[1] != ':')
3349 drvtype = GetDriveType (NULL);
3350 else
3351 {
3352 drive[0] = path[0];
3353 drive[1] = ':';
3354 drive[2] = '\\';
3355 drive[3] = '\0';
3356 drvtype = GetDriveType (drive);
3357 }
302d7d54
JR
3358
3359 /* Only logon to networked drives. */
be4c6380 3360 if (drvtype != DRIVE_REMOTE)
302d7d54 3361 return;
40a339c8 3362
302d7d54 3363 n_slashes = 2;
0b9de7cd 3364 strncpy (share, path, MAX_UTF8_PATH);
302d7d54 3365 /* Truncate to just server and share name. */
0b9de7cd 3366 for (p = share + 2; *p && p < share + MAX_UTF8_PATH; p++)
302d7d54 3367 {
806fed21 3368 if (IS_DIRECTORY_SEP (*p) && ++n_slashes > 3)
302d7d54 3369 {
806fed21 3370 *p = '\0';
302d7d54
JR
3371 break;
3372 }
3373 }
3374
0b9de7cd
EZ
3375 if (w32_unicode_filenames)
3376 {
3377 NETRESOURCEW resourcew;
3378 wchar_t share_w[MAX_PATH];
3379
3380 resourcew.dwScope = RESOURCE_GLOBALNET;
3381 resourcew.dwType = RESOURCETYPE_DISK;
3382 resourcew.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE;
3383 resourcew.dwUsage = RESOURCEUSAGE_CONTAINER;
3384 resourcew.lpLocalName = NULL;
3385 filename_to_utf16 (share, share_w);
3386 resourcew.lpRemoteName = share_w;
3387 resourcew.lpProvider = NULL;
3388
3389 val = WNetAddConnection2W (&resourcew, NULL, NULL, CONNECT_INTERACTIVE);
3390 }
3391 else
3392 {
3393 NETRESOURCEA resourcea;
3394 char share_a[MAX_PATH];
3395
3396 resourcea.dwScope = RESOURCE_GLOBALNET;
3397 resourcea.dwType = RESOURCETYPE_DISK;
3398 resourcea.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE;
3399 resourcea.dwUsage = RESOURCEUSAGE_CONTAINER;
3400 resourcea.lpLocalName = NULL;
3401 filename_to_ansi (share, share_a);
3402 resourcea.lpRemoteName = share_a;
3403 resourcea.lpProvider = NULL;
302d7d54 3404
0b9de7cd
EZ
3405 val = WNetAddConnection2A (&resourcea, NULL, NULL, CONNECT_INTERACTIVE);
3406 }
3407
3408 switch (val)
3409 {
3410 case NO_ERROR:
3411 case ERROR_ALREADY_ASSIGNED:
3412 break;
3413 case ERROR_ACCESS_DENIED:
3414 case ERROR_LOGON_FAILURE:
3415 errno = EACCES;
3416 break;
3417 case ERROR_BUSY:
3418 errno = EAGAIN;
3419 break;
3420 case ERROR_BAD_NET_NAME:
3421 case ERROR_NO_NET_OR_BAD_PATH:
3422 case ERROR_NO_NETWORK:
3423 case ERROR_CANCELLED:
3424 default:
3425 errno = ENOENT;
3426 break;
3427 }
302d7d54 3428}
480b0c5b 3429
14f20728 3430/* Emulate faccessat(2). */
480b0c5b 3431int
14f20728 3432faccessat (int dirfd, const char * path, int mode, int flags)
480b0c5b 3433{
b3308d2e
KH
3434 DWORD attributes;
3435
14f20728
EZ
3436 if (dirfd != AT_FDCWD
3437 && !(IS_DIRECTORY_SEP (path[0])
3438 || IS_DEVICE_SEP (path[1])))
73dcdb9f
PE
3439 {
3440 errno = EBADF;
3441 return -1;
3442 }
3443
8d38f461
EZ
3444 /* MSVCRT implementation of 'access' doesn't recognize D_OK, and its
3445 newer versions blow up when passed D_OK. */
b3308d2e 3446 path = map_w32_filename (path, NULL);
6dad7178
EZ
3447 /* If the last element of PATH is a symlink, we need to resolve it
3448 to get the attributes of its target file. Note: any symlinks in
3449 PATH elements other than the last one are transparently resolved
3450 by GetFileAttributes below. */
14f20728
EZ
3451 if ((volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) != 0
3452 && (flags & AT_SYMLINK_NOFOLLOW) == 0)
6dad7178
EZ
3453 path = chase_symlinks (path);
3454
e75e4a89
EZ
3455 if (w32_unicode_filenames)
3456 {
3457 wchar_t path_w[MAX_PATH];
3458
3459 filename_to_utf16 (path, path_w);
3460 attributes = GetFileAttributesW (path_w);
3461 }
3462 else
3463 {
3464 char path_a[MAX_PATH];
3465
3466 filename_to_ansi (path, path_a);
3467 attributes = GetFileAttributesA (path_a);
3468 }
3469
3470 if (attributes == -1)
b3308d2e 3471 {
8d38f461
EZ
3472 DWORD w32err = GetLastError ();
3473
3474 switch (w32err)
3475 {
6dad7178
EZ
3476 case ERROR_INVALID_NAME:
3477 case ERROR_BAD_PATHNAME:
3478 if (is_unc_volume (path))
3479 {
3480 attributes = unc_volume_file_attributes (path);
3481 if (attributes == -1)
3482 {
3483 errno = EACCES;
3484 return -1;
3485 }
3486 break;
3487 }
3488 /* FALLTHROUGH */
8d38f461 3489 case ERROR_FILE_NOT_FOUND:
6dad7178 3490 case ERROR_BAD_NETPATH:
8d38f461
EZ
3491 errno = ENOENT;
3492 break;
3493 default:
3494 errno = EACCES;
3495 break;
3496 }
b3308d2e
KH
3497 return -1;
3498 }
6ad30855
EZ
3499 if ((mode & X_OK) != 0
3500 && !(is_exec (path) || (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0))
b3308d2e
KH
3501 {
3502 errno = EACCES;
3503 return -1;
3504 }
3505 if ((mode & W_OK) != 0 && (attributes & FILE_ATTRIBUTE_READONLY) != 0)
3506 {
3507 errno = EACCES;
3508 return -1;
3509 }
3510 if ((mode & D_OK) != 0 && (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
3511 {
3512 errno = EACCES;
3513 return -1;
3514 }
3515 return 0;
480b0c5b
GV
3516}
3517
b1846422
EZ
3518/* A version of 'access' to be used locally with file names in
3519 locale-specific encoding. Does not resolve symlinks and does not
3520 support file names on FAT12 and FAT16 volumes, but that's OK, since
3521 we only invoke this function for files inside the Emacs source or
3522 installation tree, on directories (so any symlinks should have the
3523 directory bit set), and on short file names such as "C:/.emacs". */
3524static int
3525sys_access (const char *fname, int mode)
3526{
3527 char fname_copy[MAX_PATH], *p;
3528 DWORD attributes;
3529
3530 strcpy (fname_copy, fname);
3531 /* Do the equivalent of unixtodos_filename. */
3532 for (p = fname_copy; *p; p = CharNext (p))
3533 if (*p == '/')
3534 *p = '\\';
3535
3536 if ((attributes = GetFileAttributesA (fname_copy)) == -1)
3537 {
3538 DWORD w32err = GetLastError ();
3539
3540 switch (w32err)
3541 {
3542 case ERROR_INVALID_NAME:
3543 case ERROR_BAD_PATHNAME:
3544 case ERROR_FILE_NOT_FOUND:
3545 case ERROR_BAD_NETPATH:
3546 errno = ENOENT;
3547 break;
3548 default:
3549 errno = EACCES;
3550 break;
3551 }
3552 return -1;
3553 }
3554 if ((mode & X_OK) != 0
3555 && !(is_exec (fname_copy)
3556 || (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0))
3557 {
3558 errno = EACCES;
3559 return -1;
3560 }
3561 if ((mode & W_OK) != 0 && (attributes & FILE_ATTRIBUTE_READONLY) != 0)
3562 {
3563 errno = EACCES;
3564 return -1;
3565 }
3566 if ((mode & D_OK) != 0 && (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
3567 {
3568 errno = EACCES;
3569 return -1;
3570 }
3571 return 0;
3572}
3573
14f20728
EZ
3574/* Shadow some MSVC runtime functions to map requests for long filenames
3575 to reasonable short names if necessary. This was originally added to
3576 permit running Emacs on NT 3.1 on a FAT partition, which doesn't support
3577 long file names. */
3578
480b0c5b
GV
3579int
3580sys_chdir (const char * path)
3581{
e75e4a89 3582 path = map_w32_filename (path, NULL);
03d58cca
EZ
3583 if (w32_unicode_filenames)
3584 {
e75e4a89 3585 wchar_t newdir_w[MAX_PATH];
03d58cca 3586
e75e4a89
EZ
3587 if (filename_to_utf16 (path, newdir_w) == 0)
3588 return _wchdir (newdir_w);
03d58cca
EZ
3589 return -1;
3590 }
3591 else
3592 {
e75e4a89 3593 char newdir_a[MAX_PATH];
03d58cca 3594
e75e4a89
EZ
3595 if (filename_to_ansi (path, newdir_a) == 0)
3596 return _chdir (newdir_a);
03d58cca
EZ
3597 return -1;
3598 }
480b0c5b
GV
3599}
3600
3601int
3602sys_chmod (const char * path, int mode)
3603{
6dad7178 3604 path = chase_symlinks (map_w32_filename (path, NULL));
e75e4a89
EZ
3605 if (w32_unicode_filenames)
3606 {
3607 wchar_t path_w[MAX_PATH];
3608
3609 filename_to_utf16 (path, path_w);
3610 return _wchmod (path_w, mode);
3611 }
3612 else
3613 {
3614 char path_a[MAX_PATH];
3615
3616 filename_to_ansi (path, path_a);
3617 return _chmod (path_a, mode);
3618 }
480b0c5b
GV
3619}
3620
3621int
3622sys_creat (const char * path, int mode)
3623{
e75e4a89
EZ
3624 path = map_w32_filename (path, NULL);
3625 if (w32_unicode_filenames)
3626 {
3627 wchar_t path_w[MAX_PATH];
3628
3629 filename_to_utf16 (path, path_w);
3630 return _wcreat (path_w, mode);
3631 }
3632 else
3633 {
3634 char path_a[MAX_PATH];
3635
3636 filename_to_ansi (path, path_a);
3637 return _creat (path_a, mode);
3638 }
480b0c5b
GV
3639}
3640
3641FILE *
b56ceb92 3642sys_fopen (const char * path, const char * mode)
480b0c5b
GV
3643{
3644 int fd;
3645 int oflag;
3646 const char * mode_save = mode;
3647
3648 /* Force all file handles to be non-inheritable. This is necessary to
3649 ensure child processes don't unwittingly inherit handles that might
3650 prevent future file access. */
3651
3652 if (mode[0] == 'r')
3653 oflag = O_RDONLY;
3654 else if (mode[0] == 'w' || mode[0] == 'a')
3655 oflag = O_WRONLY | O_CREAT | O_TRUNC;
95ed0025 3656 else
480b0c5b
GV
3657 return NULL;
3658
3659 /* Only do simplistic option parsing. */
3660 while (*++mode)
3661 if (mode[0] == '+')
3662 {
3663 oflag &= ~(O_RDONLY | O_WRONLY);
3664 oflag |= O_RDWR;
3665 }
3666 else if (mode[0] == 'b')
3667 {
3668 oflag &= ~O_TEXT;
3669 oflag |= O_BINARY;
3670 }
3671 else if (mode[0] == 't')
3672 {
3673 oflag &= ~O_BINARY;
3674 oflag |= O_TEXT;
3675 }
3676 else break;
3677
e75e4a89
EZ
3678 path = map_w32_filename (path, NULL);
3679 if (w32_unicode_filenames)
3680 {
3681 wchar_t path_w[MAX_PATH];
3682
3683 filename_to_utf16 (path, path_w);
3684 fd = _wopen (path_w, oflag | _O_NOINHERIT, 0644);
3685 }
3686 else
3687 {
3688 char path_a[MAX_PATH];
3689
3690 filename_to_ansi (path, path_a);
3691 fd = _open (path_a, oflag | _O_NOINHERIT, 0644);
3692 }
480b0c5b
GV
3693 if (fd < 0)
3694 return NULL;
3695
76b3903d 3696 return _fdopen (fd, mode_save);
95ed0025 3697}
480b0c5b 3698
76b3903d 3699/* This only works on NTFS volumes, but is useful to have. */
480b0c5b 3700int
76b3903d 3701sys_link (const char * old, const char * new)
480b0c5b 3702{
76b3903d
GV
3703 HANDLE fileh;
3704 int result = -1;
6349f70a
EZ
3705 char oldname[MAX_UTF8_PATH], newname[MAX_UTF8_PATH];
3706 wchar_t oldname_w[MAX_PATH];
3707 char oldname_a[MAX_PATH];
76b3903d
GV
3708
3709 if (old == NULL || new == NULL)
3710 {
3711 errno = ENOENT;
3712 return -1;
3713 }
3714
3715 strcpy (oldname, map_w32_filename (old, NULL));
3716 strcpy (newname, map_w32_filename (new, NULL));
3717
6349f70a
EZ
3718 if (w32_unicode_filenames)
3719 {
3720 filename_to_utf16 (oldname, oldname_w);
3721 fileh = CreateFileW (oldname_w, 0, 0, NULL, OPEN_EXISTING,
3722 FILE_FLAG_BACKUP_SEMANTICS, NULL);
3723 }
3724 else
3725 {
3726 filename_to_ansi (oldname, oldname_a);
3727 fileh = CreateFileA (oldname_a, 0, 0, NULL, OPEN_EXISTING,
3728 FILE_FLAG_BACKUP_SEMANTICS, NULL);
3729 }
76b3903d
GV
3730 if (fileh != INVALID_HANDLE_VALUE)
3731 {
3732 int wlen;
3733
3734 /* Confusingly, the "alternate" stream name field does not apply
3735 when restoring a hard link, and instead contains the actual
3736 stream data for the link (ie. the name of the link to create).
3737 The WIN32_STREAM_ID structure before the cStreamName field is
3738 the stream header, which is then immediately followed by the
3739 stream data. */
3740
3741 struct {
3742 WIN32_STREAM_ID wid;
3743 WCHAR wbuffer[MAX_PATH]; /* extra space for link name */
3744 } data;
3745
6349f70a
EZ
3746 /* We used to pass MB_PRECOMPOSED as the 2nd arg here, but MSDN
3747 indicates that flag is unsupported for CP_UTF8, and OTOH says
3748 it is the default anyway. */
3749 wlen = MultiByteToWideChar (CP_UTF8, 0, newname, -1,
76b3903d
GV
3750 data.wid.cStreamName, MAX_PATH);
3751 if (wlen > 0)
3752 {
3753 LPVOID context = NULL;
3754 DWORD wbytes = 0;
3755
3756 data.wid.dwStreamId = BACKUP_LINK;
3757 data.wid.dwStreamAttributes = 0;
ed3751c8 3758 data.wid.Size.LowPart = wlen * sizeof (WCHAR);
76b3903d
GV
3759 data.wid.Size.HighPart = 0;
3760 data.wid.dwStreamNameSize = 0;
3761
3762 if (BackupWrite (fileh, (LPBYTE)&data,
3763 offsetof (WIN32_STREAM_ID, cStreamName)
3764 + data.wid.Size.LowPart,
3765 &wbytes, FALSE, FALSE, &context)
3766 && BackupWrite (fileh, NULL, 0, &wbytes, TRUE, FALSE, &context))
3767 {
3768 /* succeeded */
3769 result = 0;
3770 }
3771 else
3772 {
6349f70a
EZ
3773 DWORD err = GetLastError ();
3774 DWORD attributes;
3775
3776 switch (err)
3777 {
3778 case ERROR_ACCESS_DENIED:
3779 /* This is what happens when OLDNAME is a directory,
3780 since Windows doesn't support hard links to
3781 directories. Posix says to set errno to EPERM in
3782 that case. */
3783 if (w32_unicode_filenames)
3784 attributes = GetFileAttributesW (oldname_w);
3785 else
3786 attributes = GetFileAttributesA (oldname_a);
3787 if (attributes != -1
3788 && (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
3789 errno = EPERM;
3790 else if (attributes == -1
3791 && is_unc_volume (oldname)
3792 && unc_volume_file_attributes (oldname) != -1)
3793 errno = EPERM;
3794 else
3795 errno = EACCES;
3796 break;
3797 case ERROR_TOO_MANY_LINKS:
3798 errno = EMLINK;
3799 break;
3800 case ERROR_NOT_SAME_DEVICE:
3801 errno = EXDEV;
3802 break;
3803 default:
3804 errno = EINVAL;
3805 break;
3806 }
76b3903d
GV
3807 }
3808 }
3809
3810 CloseHandle (fileh);
3811 }
3812 else
3813 errno = ENOENT;
3814
3815 return result;
480b0c5b
GV
3816}
3817
3818int
3819sys_mkdir (const char * path)
3820{
44b32251
EZ
3821 path = map_w32_filename (path, NULL);
3822
3823 if (w32_unicode_filenames)
3824 {
3825 wchar_t path_w[MAX_PATH];
3826
3827 filename_to_utf16 (path, path_w);
3828 return _wmkdir (path_w);
3829 }
3830 else
3831 {
3832 char path_a[MAX_PATH];
3833
3834 filename_to_ansi (path, path_a);
3835 return _mkdir (path_a);
3836 }
480b0c5b
GV
3837}
3838
63f5c6c2
EZ
3839int
3840sys_open (const char * path, int oflag, int mode)
3841{
3842 const char* mpath = map_w32_filename (path, NULL);
3843 int res = -1;
3844
44b32251
EZ
3845 if (w32_unicode_filenames)
3846 {
3847 wchar_t mpath_w[MAX_PATH];
3848
3849 filename_to_utf16 (mpath, mpath_w);
3850 /* If possible, try to open file without _O_CREAT, to be able to
3851 write to existing hidden and system files. Force all file
3852 handles to be non-inheritable. */
3853 if ((oflag & (_O_CREAT | _O_EXCL)) != (_O_CREAT | _O_EXCL))
3854 res = _wopen (mpath_w, (oflag & ~_O_CREAT) | _O_NOINHERIT, mode);
3855 if (res < 0)
3856 res = _wopen (mpath_w, oflag | _O_NOINHERIT, mode);
3857 }
3858 else
3859 {
3860 char mpath_a[MAX_PATH];
3861
3862 filename_to_ansi (mpath, mpath_a);
3863 if ((oflag & (_O_CREAT | _O_EXCL)) != (_O_CREAT | _O_EXCL))
3864 res = _open (mpath_a, (oflag & ~_O_CREAT) | _O_NOINHERIT, mode);
3865 if (res < 0)
3866 res = _open (mpath_a, oflag | _O_NOINHERIT, mode);
3867 }
63f5c6c2
EZ
3868
3869 return res;
3870}
3871
3872/* Implementation of mkostemp for MS-Windows, to avoid race conditions
3873 when using mktemp.
3874
3875 Standard algorithm for generating a temporary file name seems to be
3876 use pid or tid with a letter on the front (in place of the 6 X's)
3877 and cycle through the letters to find a unique name. We extend
3878 that to allow any reasonable character as the first of the 6 X's,
3879 so that the number of simultaneously used temporary files will be
3880 greater. */
3881
3882int
3883mkostemp (char * template, int flags)
480b0c5b 3884{
9d1778b1 3885 char * p;
63f5c6c2 3886 int i, fd = -1;
9d1778b1 3887 unsigned uid = GetCurrentThreadId ();
63f5c6c2 3888 int save_errno = errno;
9d1778b1
RS
3889 static char first_char[] = "abcdefghijklmnopqrstuvwyz0123456789!%-_@#";
3890
63f5c6c2 3891 errno = EINVAL;
9d1778b1 3892 if (template == NULL)
63f5c6c2
EZ
3893 return -1;
3894
9d1778b1
RS
3895 p = template + strlen (template);
3896 i = 5;
3897 /* replace up to the last 5 X's with uid in decimal */
3898 while (--p >= template && p[0] == 'X' && --i >= 0)
3899 {
3900 p[0] = '0' + uid % 10;
3901 uid /= 10;
3902 }
3903
3904 if (i < 0 && p[0] == 'X')
3905 {
3906 i = 0;
3907 do
3908 {
9d1778b1 3909 p[0] = first_char[i];
63f5c6c2
EZ
3910 if ((fd = sys_open (template,
3911 flags | _O_CREAT | _O_EXCL | _O_RDWR,
3912 S_IRUSR | S_IWUSR)) >= 0
3913 || errno != EEXIST)
9d1778b1 3914 {
63f5c6c2
EZ
3915 if (fd >= 0)
3916 errno = save_errno;
3917 return fd;
9d1778b1
RS
3918 }
3919 }
3920 while (++i < sizeof (first_char));
3921 }
3922
63f5c6c2
EZ
3923 /* Template is badly formed or else we can't generate a unique name. */
3924 return -1;
480b0c5b
GV
3925}
3926
3927int
70743157
PE
3928fchmod (int fd, mode_t mode)
3929{
3930 return 0;
3931}
3932
3933int
3934sys_rename_replace (const char *oldname, const char *newname, BOOL force)
480b0c5b 3935{
cfb5e855 3936 BOOL result;
f5c287f4 3937 char temp[MAX_UTF8_PATH], temp_a[MAX_PATH];;
069d2b50
L
3938 int newname_dev;
3939 int oldname_dev;
f5c287f4 3940 bool have_temp_a = false;
480b0c5b 3941
e9e23e23 3942 /* MoveFile on Windows 95 doesn't correctly change the short file name
5162ffce
MB
3943 alias in a number of circumstances (it is not easy to predict when
3944 just by looking at oldname and newname, unfortunately). In these
3945 cases, renaming through a temporary name avoids the problem.
3946
e9e23e23 3947 A second problem on Windows 95 is that renaming through a temp name when
5162ffce
MB
3948 newname is uppercase fails (the final long name ends up in
3949 lowercase, although the short alias might be uppercase) UNLESS the
3950 long temp name is not 8.3.
3951
e9e23e23 3952 So, on Windows 95 we always rename through a temp name, and we make sure
5162ffce 3953 the temp name has a long extension to ensure correct renaming. */
480b0c5b 3954
fbd6baed 3955 strcpy (temp, map_w32_filename (oldname, NULL));
480b0c5b 3956
069d2b50
L
3957 /* volume_info is set indirectly by map_w32_filename. */
3958 oldname_dev = volume_info.serialnum;
3959
417a7a0e 3960 if (os_subtype == OS_9X)
480b0c5b 3961 {
b3308d2e 3962 char * o;
480b0c5b 3963 char * p;
b3308d2e 3964 int i = 0;
f5c287f4 3965 char oldname_a[MAX_PATH];
b3308d2e
KH
3966
3967 oldname = map_w32_filename (oldname, NULL);
f5c287f4
EZ
3968 filename_to_ansi (oldname, oldname_a);
3969 filename_to_ansi (temp, temp_a);
3970 if ((o = strrchr (oldname_a, '\\')))
b3308d2e
KH
3971 o++;
3972 else
f5c287f4 3973 o = (char *) oldname_a;
480b0c5b 3974
f5c287f4 3975 if ((p = strrchr (temp_a, '\\')))
480b0c5b
GV
3976 p++;
3977 else
f5c287f4 3978 p = temp_a;
b3308d2e
KH
3979
3980 do
3981 {
3982 /* Force temp name to require a manufactured 8.3 alias - this
3983 seems to make the second rename work properly. */
f313ee82 3984 sprintf (p, "_.%s.%u", o, i);
b3308d2e 3985 i++;
f5c287f4 3986 result = rename (oldname_a, temp_a);
b3308d2e
KH
3987 }
3988 /* This loop must surely terminate! */
cfb5e855 3989 while (result < 0 && errno == EEXIST);
58f0cb7e 3990 if (result < 0)
480b0c5b 3991 return -1;
f5c287f4 3992 have_temp_a = true;
480b0c5b
GV
3993 }
3994
70743157 3995 /* If FORCE, emulate Unix behavior - newname is deleted if it already exists
5162ffce 3996 (at least if it is a file; don't do this for directories).
76b3903d 3997
b3308d2e
KH
3998 Since we mustn't do this if we are just changing the case of the
3999 file name (we would end up deleting the file we are trying to
4000 rename!), we let rename detect if the destination file already
4001 exists - that way we avoid the possible pitfalls of trying to
4002 determine ourselves whether two names really refer to the same
4003 file, which is not always possible in the general case. (Consider
4004 all the permutations of shared or subst'd drives, etc.) */
4005
4006 newname = map_w32_filename (newname, NULL);
069d2b50
L
4007
4008 /* volume_info is set indirectly by map_w32_filename. */
4009 newname_dev = volume_info.serialnum;
4010
f5c287f4 4011 if (w32_unicode_filenames)
069d2b50 4012 {
f5c287f4 4013 wchar_t temp_w[MAX_PATH], newname_w[MAX_PATH];
069d2b50 4014
f5c287f4
EZ
4015 filename_to_utf16 (temp, temp_w);
4016 filename_to_utf16 (newname, newname_w);
4017 result = _wrename (temp_w, newname_w);
4018 if (result < 0 && force)
069d2b50 4019 {
f5c287f4
EZ
4020 DWORD w32err = GetLastError ();
4021
4022 if (errno == EACCES
4023 && newname_dev != oldname_dev)
4024 {
4025 /* The implementation of `rename' on Windows does not return
4026 errno = EXDEV when you are moving a directory to a
4027 different storage device (ex. logical disk). It returns
4028 EACCES instead. So here we handle such situations and
4029 return EXDEV. */
4030 DWORD attributes;
4031
4032 if ((attributes = GetFileAttributesW (temp_w)) != -1
4033 && (attributes & FILE_ATTRIBUTE_DIRECTORY))
4034 errno = EXDEV;
4035 }
4036 else if (errno == EEXIST)
4037 {
4038 if (_wchmod (newname_w, 0666) != 0)
4039 return result;
4040 if (_wunlink (newname_w) != 0)
4041 return result;
4042 result = _wrename (temp_w, newname_w);
4043 }
4044 else if (w32err == ERROR_PRIVILEGE_NOT_HELD
4045 && is_symlink (temp))
4046 {
4047 /* This is Windows prohibiting the user from creating a
4048 symlink in another place, since that requires
4049 privileges. */
4050 errno = EPERM;
4051 }
069d2b50 4052 }
f5c287f4
EZ
4053 }
4054 else
4055 {
4056 char newname_a[MAX_PATH];
4057
4058 if (!have_temp_a)
4059 filename_to_ansi (temp, temp_a);
4060 filename_to_ansi (newname, newname_a);
4061 result = rename (temp_a, newname_a);
4062 if (result < 0 && force)
6dad7178 4063 {
f5c287f4
EZ
4064 DWORD w32err = GetLastError ();
4065
4066 if (errno == EACCES
4067 && newname_dev != oldname_dev)
4068 {
4069 DWORD attributes;
4070
4071 if ((attributes = GetFileAttributesA (temp_a)) != -1
4072 && (attributes & FILE_ATTRIBUTE_DIRECTORY))
4073 errno = EXDEV;
4074 }
4075 else if (errno == EEXIST)
4076 {
4077 if (_chmod (newname_a, 0666) != 0)
4078 return result;
4079 if (_unlink (newname_a) != 0)
4080 return result;
4081 result = rename (temp_a, newname_a);
4082 }
4083 else if (w32err == ERROR_PRIVILEGE_NOT_HELD
4084 && is_symlink (temp))
4085 errno = EPERM;
6dad7178 4086 }
069d2b50 4087 }
480b0c5b 4088
eb9ea53f 4089 return result;
480b0c5b
GV
4090}
4091
70743157
PE
4092int
4093sys_rename (char const *old, char const *new)
4094{
4095 return sys_rename_replace (old, new, TRUE);
4096}
4097
480b0c5b
GV
4098int
4099sys_rmdir (const char * path)
4100{
8dc689ce
EZ
4101 path = map_w32_filename (path, NULL);
4102
4103 if (w32_unicode_filenames)
4104 {
4105 wchar_t path_w[MAX_PATH];
4106
4107 filename_to_utf16 (path, path_w);
4108 return _wrmdir (path_w);
4109 }
4110 else
4111 {
4112 char path_a[MAX_PATH];
4113
4114 filename_to_ansi (path, path_a);
4115 return _rmdir (path_a);
4116 }
480b0c5b
GV
4117}
4118
4119int
4120sys_unlink (const char * path)
4121{
16bb7578
GV
4122 path = map_w32_filename (path, NULL);
4123
8dc689ce
EZ
4124 if (w32_unicode_filenames)
4125 {
4126 wchar_t path_w[MAX_PATH];
4127
4128 filename_to_utf16 (path, path_w);
4129 /* On Unix, unlink works without write permission. */
4130 _wchmod (path_w, 0666);
4131 return _wunlink (path_w);
4132 }
4133 else
4134 {
4135 char path_a[MAX_PATH];
4136
4137 filename_to_ansi (path, path_a);
4138 _chmod (path_a, 0666);
4139 return _unlink (path_a);
4140 }
480b0c5b
GV
4141}
4142
4143static FILETIME utc_base_ft;
5da9424d 4144static ULONGLONG utc_base; /* In 100ns units */
480b0c5b
GV
4145static int init = 0;
4146
5da9424d
JB
4147#define FILETIME_TO_U64(result, ft) \
4148 do { \
4149 ULARGE_INTEGER uiTemp; \
4150 uiTemp.LowPart = (ft).dwLowDateTime; \
4151 uiTemp.HighPart = (ft).dwHighDateTime; \
4152 result = uiTemp.QuadPart; \
4153 } while (0)
4154
4155static void
b56ceb92 4156initialize_utc_base (void)
7c80d5ec 4157{
5da9424d
JB
4158 /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */
4159 SYSTEMTIME st;
4160
4161 st.wYear = 1970;
4162 st.wMonth = 1;
4163 st.wDay = 1;
4164 st.wHour = 0;
4165 st.wMinute = 0;
4166 st.wSecond = 0;
4167 st.wMilliseconds = 0;
4168
4169 SystemTimeToFileTime (&st, &utc_base_ft);
4170 FILETIME_TO_U64 (utc_base, utc_base_ft);
7c80d5ec
EZ
4171}
4172
480b0c5b
GV
4173static time_t
4174convert_time (FILETIME ft)
4175{
5da9424d 4176 ULONGLONG tmp;
480b0c5b
GV
4177
4178 if (!init)
4179 {
9d4f32e8 4180 initialize_utc_base ();
480b0c5b
GV
4181 init = 1;
4182 }
4183
4184 if (CompareFileTime (&ft, &utc_base_ft) < 0)
4185 return 0;
4186
5da9424d
JB
4187 FILETIME_TO_U64 (tmp, ft);
4188 return (time_t) ((tmp - utc_base) / 10000000L);
480b0c5b
GV
4189}
4190
bedf4aab 4191static void
480b0c5b
GV
4192convert_from_time_t (time_t time, FILETIME * pft)
4193{
5da9424d 4194 ULARGE_INTEGER tmp;
480b0c5b
GV
4195
4196 if (!init)
4197 {
5da9424d 4198 initialize_utc_base ();
480b0c5b
GV
4199 init = 1;
4200 }
4201
4202 /* time in 100ns units since 1-Jan-1601 */
5da9424d
JB
4203 tmp.QuadPart = (ULONGLONG) time * 10000000L + utc_base;
4204 pft->dwHighDateTime = tmp.HighPart;
4205 pft->dwLowDateTime = tmp.LowPart;
480b0c5b 4206}
480b0c5b 4207
8aaaec6b 4208static PSECURITY_DESCRIPTOR
6dad7178
EZ
4209get_file_security_desc_by_handle (HANDLE h)
4210{
4211 PSECURITY_DESCRIPTOR psd = NULL;
4212 DWORD err;
4213 SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION
4214 | GROUP_SECURITY_INFORMATION /* | DACL_SECURITY_INFORMATION */ ;
4215
4216 err = get_security_info (h, SE_FILE_OBJECT, si,
4217 NULL, NULL, NULL, NULL, &psd);
4218 if (err != ERROR_SUCCESS)
4219 return NULL;
4220
4221 return psd;
4222}
4223
4224static PSECURITY_DESCRIPTOR
4225get_file_security_desc_by_name (const char *fname)
8aaaec6b
EZ
4226{
4227 PSECURITY_DESCRIPTOR psd = NULL;
4228 DWORD sd_len, err;
4229 SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION
4230 | GROUP_SECURITY_INFORMATION /* | DACL_SECURITY_INFORMATION */ ;
4231
4232 if (!get_file_security (fname, si, psd, 0, &sd_len))
4233 {
4234 err = GetLastError ();
4235 if (err != ERROR_INSUFFICIENT_BUFFER)
4236 return NULL;
4237 }
4238
4239 psd = xmalloc (sd_len);
4240 if (!get_file_security (fname, si, psd, sd_len, &sd_len))
4241 {
4242 xfree (psd);
4243 return NULL;
4244 }
4245
4246 return psd;
4247}
4248
4249static DWORD
4250get_rid (PSID sid)
4251{
4252 unsigned n_subauthorities;
4253
4254 /* Use the last sub-authority value of the RID, the relative
4255 portion of the SID, as user/group ID. */
4256 n_subauthorities = *get_sid_sub_authority_count (sid);
4257 if (n_subauthorities < 1)
4258 return 0; /* the "World" RID */
4259 return *get_sid_sub_authority (sid, n_subauthorities - 1);
4260}
4261
f8b35b24
EZ
4262/* Caching SID and account values for faster lokup. */
4263
f8b35b24 4264struct w32_id {
22749e9a 4265 unsigned rid;
f8b35b24
EZ
4266 struct w32_id *next;
4267 char name[GNLEN+1];
4268 unsigned char sid[FLEXIBLE_ARRAY_MEMBER];
4269};
4270
4271static struct w32_id *w32_idlist;
4272
4273static int
22749e9a 4274w32_cached_id (PSID sid, unsigned *id, char *name)
f8b35b24
EZ
4275{
4276 struct w32_id *tail, *found;
4277
4278 for (found = NULL, tail = w32_idlist; tail; tail = tail->next)
4279 {
4280 if (equal_sid ((PSID)tail->sid, sid))
4281 {
4282 found = tail;
4283 break;
4284 }
4285 }
4286 if (found)
4287 {
4288 *id = found->rid;
4289 strcpy (name, found->name);
4290 return 1;
4291 }
4292 else
4293 return 0;
4294}
4295
4296static void
22749e9a 4297w32_add_to_cache (PSID sid, unsigned id, char *name)
f8b35b24
EZ
4298{
4299 DWORD sid_len;
4300 struct w32_id *new_entry;
4301
4302 /* We don't want to leave behind stale cache from when Emacs was
4303 dumped. */
4304 if (initialized)
4305 {
4306 sid_len = get_length_sid (sid);
4307 new_entry = xmalloc (offsetof (struct w32_id, sid) + sid_len);
4308 if (new_entry)
4309 {
4310 new_entry->rid = id;
4311 strcpy (new_entry->name, name);
4312 copy_sid (sid_len, (PSID)new_entry->sid, sid);
4313 new_entry->next = w32_idlist;
4314 w32_idlist = new_entry;
4315 }
4316 }
4317}
4318
8aaaec6b
EZ
4319#define UID 1
4320#define GID 2
4321
4322static int
92340ec7 4323get_name_and_id (PSECURITY_DESCRIPTOR psd, unsigned *id, char *nm, int what)
8aaaec6b
EZ
4324{
4325 PSID sid = NULL;
8aaaec6b
EZ
4326 BOOL dflt;
4327 SID_NAME_USE ignore;
4328 char name[UNLEN+1];
4329 DWORD name_len = sizeof (name);
4330 char domain[1024];
ed3751c8 4331 DWORD domain_len = sizeof (domain);
8aaaec6b
EZ
4332 int use_dflt = 0;
4333 int result;
4334
4335 if (what == UID)
4336 result = get_security_descriptor_owner (psd, &sid, &dflt);
4337 else if (what == GID)
4338 result = get_security_descriptor_group (psd, &sid, &dflt);
4339 else
4340 result = 0;
4341
4342 if (!result || !is_valid_sid (sid))
4343 use_dflt = 1;
f8b35b24 4344 else if (!w32_cached_id (sid, id, nm))
8aaaec6b 4345 {
92340ec7 4346 if (!lookup_account_sid (NULL, sid, name, &name_len,
8aaaec6b
EZ
4347 domain, &domain_len, &ignore)
4348 || name_len > UNLEN+1)
4349 use_dflt = 1;
4350 else
4351 {
4352 *id = get_rid (sid);
4353 strcpy (nm, name);
f8b35b24 4354 w32_add_to_cache (sid, *id, name);
8aaaec6b
EZ
4355 }
4356 }
4357 return use_dflt;
4358}
4359
4360static void
92340ec7 4361get_file_owner_and_group (PSECURITY_DESCRIPTOR psd, struct stat *st)
8aaaec6b
EZ
4362{
4363 int dflt_usr = 0, dflt_grp = 0;
4364
4365 if (!psd)
4366 {
4367 dflt_usr = 1;
4368 dflt_grp = 1;
4369 }
4370 else
4371 {
92340ec7 4372 if (get_name_and_id (psd, &st->st_uid, st->st_uname, UID))
8aaaec6b 4373 dflt_usr = 1;
92340ec7 4374 if (get_name_and_id (psd, &st->st_gid, st->st_gname, GID))
8aaaec6b
EZ
4375 dflt_grp = 1;
4376 }
4377 /* Consider files to belong to current user/group, if we cannot get
4378 more accurate information. */
4379 if (dflt_usr)
4380 {
4381 st->st_uid = dflt_passwd.pw_uid;
4382 strcpy (st->st_uname, dflt_passwd.pw_name);
4383 }
4384 if (dflt_grp)
4385 {
4386 st->st_gid = dflt_passwd.pw_gid;
4387 strcpy (st->st_gname, dflt_group.gr_name);
4388 }
4389}
4390
be4c6380
EZ
4391/* Return non-zero if NAME is a potentially slow filesystem. */
4392int
4393is_slow_fs (const char *name)
4394{
4395 char drive_root[4];
4396 UINT devtype;
4397
4398 if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1]))
4399 devtype = DRIVE_REMOTE; /* assume UNC name is remote */
4400 else if (!(strlen (name) >= 2 && IS_DEVICE_SEP (name[1])))
4401 devtype = GetDriveType (NULL); /* use root of current drive */
4402 else
4403 {
4404 /* GetDriveType needs the root directory of the drive. */
4405 strncpy (drive_root, name, 2);
4406 drive_root[2] = '\\';
4407 drive_root[3] = '\0';
4408 devtype = GetDriveType (drive_root);
4409 }
4410 return !(devtype == DRIVE_FIXED || devtype == DRIVE_RAMDISK);
4411}
4412
5c207910 4413/* If this is non-zero, the caller wants accurate information about
df87c56c
EZ
4414 file's owner and group, which could be expensive to get. dired.c
4415 uses this flag when needed for the job at hand. */
5c207910
EZ
4416int w32_stat_get_owner_group;
4417
480b0c5b
GV
4418/* MSVC stat function can't cope with UNC names and has other bugs, so
4419 replace it with our own. This also allows us to calculate consistent
df87c56c
EZ
4420 inode values and owner/group without hacks in the main Emacs code,
4421 and support file names encoded in UTF-8. */
6dad7178
EZ
4422
4423static int
4424stat_worker (const char * path, struct stat * buf, int follow_symlinks)
480b0c5b 4425{
6dad7178 4426 char *name, *save_name, *r;
df87c56c
EZ
4427 WIN32_FIND_DATAW wfd_w;
4428 WIN32_FIND_DATAA wfd_a;
480b0c5b 4429 HANDLE fh;
6dad7178 4430 unsigned __int64 fake_inode = 0;
480b0c5b
GV
4431 int permission;
4432 int len;
4433 int rootdir = FALSE;
8aaaec6b 4434 PSECURITY_DESCRIPTOR psd = NULL;
6dad7178
EZ
4435 int is_a_symlink = 0;
4436 DWORD file_flags = FILE_FLAG_BACKUP_SEMANTICS;
4437 DWORD access_rights = 0;
4438 DWORD fattrs = 0, serialnum = 0, fs_high = 0, fs_low = 0, nlinks = 1;
4439 FILETIME ctime, atime, wtime;
df87c56c
EZ
4440 wchar_t name_w[MAX_PATH];
4441 char name_a[MAX_PATH];
480b0c5b
GV
4442
4443 if (path == NULL || buf == NULL)
4444 {
4445 errno = EFAULT;
4446 return -1;
4447 }
4448
6dad7178 4449 save_name = name = (char *) map_w32_filename (path, &path);
22189f79 4450 /* Must be valid filename, no wild cards or other invalid
df87c56c
EZ
4451 characters. */
4452 if (strpbrk (name, "*?|<>\""))
480b0c5b
GV
4453 {
4454 errno = ENOENT;
4455 return -1;
4456 }
4457
4458 /* Remove trailing directory separator, unless name is the root
4459 directory of a drive or UNC volume in which case ensure there
4460 is a trailing separator. */
4461 len = strlen (name);
480b0c5b
GV
4462 name = strcpy (alloca (len + 2), name);
4463
6dad7178
EZ
4464 /* Avoid a somewhat costly call to is_symlink if the filesystem
4465 doesn't support symlinks. */
4466 if ((volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) != 0)
4467 is_a_symlink = is_symlink (name);
4468
4469 /* Plan A: Open the file and get all the necessary information via
4470 the resulting handle. This solves several issues in one blow:
4471
4472 . retrieves attributes for the target of a symlink, if needed
4473 . gets attributes of root directories and symlinks pointing to
4474 root directories, thus avoiding the need for special-casing
4475 these and detecting them by examining the file-name format
4476 . retrieves more accurate attributes (e.g., non-zero size for
4477 some directories, esp. directories that are junction points)
4478 . correctly resolves "c:/..", "/.." and similar file names
4479 . avoids run-time penalties for 99% of use cases
4480
4481 Plan A is always tried first, unless the user asked not to (but
4482 if the file is a symlink and we need to follow links, we try Plan
4483 A even if the user asked not to).
4484
4485 If Plan A fails, we go to Plan B (below), where various
4486 potentially expensive techniques must be used to handle "special"
4487 files such as UNC volumes etc. */
8aaaec6b 4488 if (!(NILP (Vw32_get_true_file_attributes)
19ced600 4489 || (EQ (Vw32_get_true_file_attributes, Qlocal) && is_slow_fs (name)))
6dad7178
EZ
4490 /* Following symlinks requires getting the info by handle. */
4491 || (is_a_symlink && follow_symlinks))
480b0c5b 4492 {
6dad7178
EZ
4493 BY_HANDLE_FILE_INFORMATION info;
4494
4495 if (is_a_symlink && !follow_symlinks)
4496 file_flags |= FILE_FLAG_OPEN_REPARSE_POINT;
4497 /* READ_CONTROL access rights are required to get security info
4498 by handle. But if the OS doesn't support security in the
4499 first place, we don't need to try. */
4500 if (is_windows_9x () != TRUE)
4501 access_rights |= READ_CONTROL;
4502
df87c56c
EZ
4503 if (w32_unicode_filenames)
4504 {
4505 filename_to_utf16 (name, name_w);
4506 fh = CreateFileW (name_w, access_rights, 0, NULL, OPEN_EXISTING,
4507 file_flags, NULL);
4508 /* If CreateFile fails with READ_CONTROL, try again with
4509 zero as access rights. */
4510 if (fh == INVALID_HANDLE_VALUE && access_rights)
4511 fh = CreateFileW (name_w, 0, 0, NULL, OPEN_EXISTING,
4512 file_flags, NULL);
4513 }
4514 else
4515 {
4516 filename_to_ansi (name, name_a);
4517 fh = CreateFileA (name_a, access_rights, 0, NULL, OPEN_EXISTING,
4518 file_flags, NULL);
4519 if (fh == INVALID_HANDLE_VALUE && access_rights)
4520 fh = CreateFileA (name_a, 0, 0, NULL, OPEN_EXISTING,
4521 file_flags, NULL);
4522 }
6dad7178
EZ
4523 if (fh == INVALID_HANDLE_VALUE)
4524 goto no_true_file_attributes;
4525
3ed8598c 4526 /* This is more accurate in terms of getting the correct number
aa5ee2a3 4527 of links, but is quite slow (it is noticeable when Emacs is
480b0c5b 4528 making a list of file name completions). */
480b0c5b
GV
4529 if (GetFileInformationByHandle (fh, &info))
4530 {
6dad7178 4531 nlinks = info.nNumberOfLinks;
76b3903d
GV
4532 /* Might as well use file index to fake inode values, but this
4533 is not guaranteed to be unique unless we keep a handle open
4534 all the time (even then there are situations where it is
4535 not unique). Reputedly, there are at most 48 bits of info
4536 (on NTFS, presumably less on FAT). */
e3b88685
EZ
4537 fake_inode = info.nFileIndexHigh;
4538 fake_inode <<= 32;
4539 fake_inode += info.nFileIndexLow;
6dad7178
EZ
4540 serialnum = info.dwVolumeSerialNumber;
4541 fs_high = info.nFileSizeHigh;
4542 fs_low = info.nFileSizeLow;
4543 ctime = info.ftCreationTime;
4544 atime = info.ftLastAccessTime;
4545 wtime = info.ftLastWriteTime;
4546 fattrs = info.dwFileAttributes;
480b0c5b
GV
4547 }
4548 else
4549 {
6dad7178
EZ
4550 /* We don't go to Plan B here, because it's not clear that
4551 it's a good idea. The only known use case where
4552 CreateFile succeeds, but GetFileInformationByHandle fails
4553 (with ERROR_INVALID_FUNCTION) is for character devices
4554 such as NUL, PRN, etc. For these, switching to Plan B is
4555 a net loss, because we lose the character device
4556 attribute returned by GetFileType below (FindFirstFile
4557 doesn't set that bit in the attributes), and the other
4558 fields don't make sense for character devices anyway.
4559 Emacs doesn't really care for non-file entities in the
4560 context of l?stat, so neither do we. */
4561
4562 /* w32err is assigned so one could put a breakpoint here and
4563 examine its value, when GetFileInformationByHandle
4564 fails. */
4565 DWORD w32err = GetLastError ();
4566
4567 switch (w32err)
4568 {
4569 case ERROR_FILE_NOT_FOUND: /* can this ever happen? */
4570 errno = ENOENT;
4571 return -1;
4572 }
01f31dfb
AI
4573 }
4574
6dad7178
EZ
4575 /* Test for a symlink before testing for a directory, since
4576 symlinks to directories have the directory bit set, but we
4577 don't want them to appear as directories. */
4578 if (is_a_symlink && !follow_symlinks)
4579 buf->st_mode = S_IFLNK;
4580 else if (fattrs & FILE_ATTRIBUTE_DIRECTORY)
4581 buf->st_mode = S_IFDIR;
93e0f0da
JR
4582 else
4583 {
6dad7178
EZ
4584 DWORD ftype = GetFileType (fh);
4585
4586 switch (ftype)
93e0f0da
JR
4587 {
4588 case FILE_TYPE_DISK:
e3b88685 4589 buf->st_mode = S_IFREG;
93e0f0da
JR
4590 break;
4591 case FILE_TYPE_PIPE:
e3b88685 4592 buf->st_mode = S_IFIFO;
93e0f0da
JR
4593 break;
4594 case FILE_TYPE_CHAR:
4595 case FILE_TYPE_UNKNOWN:
4596 default:
e3b88685 4597 buf->st_mode = S_IFCHR;
93e0f0da 4598 }
480b0c5b 4599 }
6dad7178
EZ
4600 /* We produce the fallback owner and group data, based on the
4601 current user that runs Emacs, in the following cases:
4602
5c207910 4603 . caller didn't request owner and group info
6dad7178
EZ
4604 . this is Windows 9X
4605 . getting security by handle failed, and we need to produce
4606 information for the target of a symlink (this is better
4607 than producing a potentially misleading info about the
4608 symlink itself)
4609
4610 If getting security by handle fails, and we don't need to
4611 resolve symlinks, we try getting security by name. */
5c207910 4612 if (!w32_stat_get_owner_group || is_windows_9x () == TRUE)
92340ec7 4613 get_file_owner_and_group (NULL, buf);
5c207910 4614 else
6dad7178 4615 {
5c207910
EZ
4616 psd = get_file_security_desc_by_handle (fh);
4617 if (psd)
4618 {
a4b0cca1 4619 get_file_owner_and_group (psd, buf);
5c207910
EZ
4620 LocalFree (psd);
4621 }
4622 else if (!(is_a_symlink && follow_symlinks))
4623 {
4624 psd = get_file_security_desc_by_name (name);
a4b0cca1 4625 get_file_owner_and_group (psd, buf);
5c207910
EZ
4626 xfree (psd);
4627 }
4628 else
a4b0cca1 4629 get_file_owner_and_group (NULL, buf);
6dad7178 4630 }
01f31dfb 4631 CloseHandle (fh);
76b3903d
GV
4632 }
4633 else
4634 {
6dad7178
EZ
4635 no_true_file_attributes:
4636 /* Plan B: Either getting a handle on the file failed, or the
4637 caller explicitly asked us to not bother making this
4638 information more accurate.
4639
4640 Implementation note: In Plan B, we never bother to resolve
4641 symlinks, even if we got here because we tried Plan A and
4642 failed. That's because, even if the caller asked for extra
4643 precision by setting Vw32_get_true_file_attributes to t,
4644 resolving symlinks requires acquiring a file handle to the
4645 symlink, which we already know will fail. And if the user
4646 did not ask for extra precision, resolving symlinks will fly
4647 in the face of that request, since the user then wants the
4648 lightweight version of the code. */
4649 rootdir = (path >= save_name + len - 1
4650 && (IS_DIRECTORY_SEP (*path) || *path == 0));
4651
4652 /* If name is "c:/.." or "/.." then stat "c:/" or "/". */
4653 r = IS_DEVICE_SEP (name[1]) ? &name[2] : name;
4654 if (IS_DIRECTORY_SEP (r[0])
4655 && r[1] == '.' && r[2] == '.' && r[3] == '\0')
4656 r[1] = r[2] = '\0';
4657
4658 /* Note: If NAME is a symlink to the root of a UNC volume
4659 (i.e. "\\SERVER"), we will not detect that here, and we will
4660 return data about the symlink as result of FindFirst below.
4661 This is unfortunate, but that marginal use case does not
4662 justify a call to chase_symlinks which would impose a penalty
4663 on all the other use cases. (We get here for symlinks to
4664 roots of UNC volumes because CreateFile above fails for them,
4665 unlike with symlinks to root directories X:\ of drives.) */
4666 if (is_unc_volume (name))
4667 {
4668 fattrs = unc_volume_file_attributes (name);
4669 if (fattrs == -1)
4670 return -1;
4671
4672 ctime = atime = wtime = utc_base_ft;
4673 }
4674 else if (rootdir)
4675 {
df87c56c
EZ
4676 if (!IS_DIRECTORY_SEP (name[len-1]))
4677 strcat (name, "\\");
6dad7178
EZ
4678 if (GetDriveType (name) < 2)
4679 {
4680 errno = ENOENT;
4681 return -1;
4682 }
4683
4684 fattrs = FILE_ATTRIBUTE_DIRECTORY;
4685 ctime = atime = wtime = utc_base_ft;
4686 }
4687 else
4688 {
df87c56c 4689 int have_wfd = -1;
806fed21 4690
df87c56c
EZ
4691 if (IS_DIRECTORY_SEP (name[len-1]))
4692 name[len - 1] = 0;
6dad7178
EZ
4693
4694 /* (This is hacky, but helps when doing file completions on
4695 network drives.) Optimize by using information available from
4696 active readdir if possible. */
4697 len = strlen (dir_pathname);
df87c56c
EZ
4698 if (IS_DIRECTORY_SEP (dir_pathname[len-1]))
4699 len--;
6dad7178 4700 if (dir_find_handle != INVALID_HANDLE_VALUE
df87c56c 4701 && last_dir_find_data != -1
6dad7178 4702 && !(is_a_symlink && follow_symlinks)
df87c56c
EZ
4703 /* The 2 file-name comparisons below support only ASCII
4704 characters, and will lose (compare not equal) when
4705 the file names include non-ASCII charcaters that are
4706 the same but for the case. However, doing this
4707 properly involves: (a) converting both file names to
4708 UTF-16, (b) lower-casing both names using CharLowerW,
4709 and (c) comparing the results; this would be quite a
4710 bit slower, whereas Plan B is for users who want
4711 lightweight albeit inaccurate version of 'stat'. */
4712 && c_strncasecmp (save_name, dir_pathname, len) == 0
6dad7178
EZ
4713 && IS_DIRECTORY_SEP (name[len])
4714 && xstrcasecmp (name + len + 1, dir_static.d_name) == 0)
4715 {
df87c56c 4716 have_wfd = last_dir_find_data;
6dad7178 4717 /* This was the last entry returned by readdir. */
df87c56c
EZ
4718 if (last_dir_find_data == DIR_FIND_DATA_W)
4719 wfd_w = dir_find_data_w;
4720 else
4721 wfd_a = dir_find_data_a;
6dad7178
EZ
4722 }
4723 else
4724 {
4725 logon_network_drive (name);
4726
df87c56c
EZ
4727 if (w32_unicode_filenames)
4728 {
4729 filename_to_utf16 (name, name_w);
4730 fh = FindFirstFileW (name_w, &wfd_w);
4731 have_wfd = DIR_FIND_DATA_W;
4732 }
4733 else
4734 {
4735 filename_to_ansi (name, name_a);
4736 /* If NAME includes characters not representable by
4737 the current ANSI codepage, filename_to_ansi
4738 usually replaces them with a '?'. We don't want
4739 to let FindFirstFileA interpret those as widlcards,
4740 and "succeed", returning us data from some random
4741 file in the same directory. */
4742 if (_mbspbrk (name_a, "?"))
4743 fh = INVALID_HANDLE_VALUE;
4744 else
4745 fh = FindFirstFileA (name_a, &wfd_a);
4746 have_wfd = DIR_FIND_DATA_A;
4747 }
6dad7178
EZ
4748 if (fh == INVALID_HANDLE_VALUE)
4749 {
4750 errno = ENOENT;
4751 return -1;
4752 }
4753 FindClose (fh);
4754 }
4755 /* Note: if NAME is a symlink, the information we get from
4756 FindFirstFile is for the symlink, not its target. */
df87c56c
EZ
4757 if (have_wfd == DIR_FIND_DATA_W)
4758 {
4759 fattrs = wfd_w.dwFileAttributes;
4760 ctime = wfd_w.ftCreationTime;
4761 atime = wfd_w.ftLastAccessTime;
4762 wtime = wfd_w.ftLastWriteTime;
4763 fs_high = wfd_w.nFileSizeHigh;
4764 fs_low = wfd_w.nFileSizeLow;
4765 }
4766 else
4767 {
4768 fattrs = wfd_a.dwFileAttributes;
4769 ctime = wfd_a.ftCreationTime;
4770 atime = wfd_a.ftLastAccessTime;
4771 wtime = wfd_a.ftLastWriteTime;
4772 fs_high = wfd_a.nFileSizeHigh;
4773 fs_low = wfd_a.nFileSizeLow;
4774 }
6dad7178
EZ
4775 fake_inode = 0;
4776 nlinks = 1;
4777 serialnum = volume_info.serialnum;
4778 }
4779 if (is_a_symlink && !follow_symlinks)
4780 buf->st_mode = S_IFLNK;
4781 else if (fattrs & FILE_ATTRIBUTE_DIRECTORY)
4782 buf->st_mode = S_IFDIR;
4783 else
4784 buf->st_mode = S_IFREG;
8aaaec6b 4785
92340ec7 4786 get_file_owner_and_group (NULL, buf);
76b3903d
GV
4787 }
4788
6dad7178 4789 buf->st_ino = fake_inode;
480b0c5b 4790
6dad7178
EZ
4791 buf->st_dev = serialnum;
4792 buf->st_rdev = serialnum;
480b0c5b 4793
6dad7178 4794 buf->st_size = fs_high;
8aaaec6b 4795 buf->st_size <<= 32;
6dad7178
EZ
4796 buf->st_size += fs_low;
4797 buf->st_nlink = nlinks;
480b0c5b
GV
4798
4799 /* Convert timestamps to Unix format. */
6dad7178
EZ
4800 buf->st_mtime = convert_time (wtime);
4801 buf->st_atime = convert_time (atime);
480b0c5b 4802 if (buf->st_atime == 0) buf->st_atime = buf->st_mtime;
6dad7178 4803 buf->st_ctime = convert_time (ctime);
480b0c5b
GV
4804 if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime;
4805
4806 /* determine rwx permissions */
6dad7178
EZ
4807 if (is_a_symlink && !follow_symlinks)
4808 permission = S_IREAD | S_IWRITE | S_IEXEC; /* Posix expectations */
480b0c5b 4809 else
6dad7178
EZ
4810 {
4811 if (fattrs & FILE_ATTRIBUTE_READONLY)
4812 permission = S_IREAD;
4813 else
4814 permission = S_IREAD | S_IWRITE;
177c0ea7 4815
6dad7178
EZ
4816 if (fattrs & FILE_ATTRIBUTE_DIRECTORY)
4817 permission |= S_IEXEC;
4818 else if (is_exec (name))
4819 permission |= S_IEXEC;
4820 }
480b0c5b
GV
4821
4822 buf->st_mode |= permission | (permission >> 3) | (permission >> 6);
4823
4824 return 0;
4825}
4826
6dad7178
EZ
4827int
4828stat (const char * path, struct stat * buf)
4829{
4830 return stat_worker (path, buf, 1);
4831}
4832
4833int
4834lstat (const char * path, struct stat * buf)
4835{
4836 return stat_worker (path, buf, 0);
4837}
4838
8654f9d7
PE
4839int
4840fstatat (int fd, char const *name, struct stat *st, int flags)
4841{
4842 /* Rely on a hack: an open directory is modeled as file descriptor 0.
4843 This is good enough for the current usage in Emacs, but is fragile.
4844
d9c287e5 4845 FIXME: Add proper support for fdopendir, fstatat, readlinkat.
8654f9d7 4846 Gnulib does this and can serve as a model. */
df87c56c 4847 char fullname[MAX_UTF8_PATH];
8654f9d7
PE
4848
4849 if (fd != AT_FDCWD)
4850 {
4851 if (_snprintf (fullname, sizeof fullname, "%s/%s", dir_pathname, name)
4852 < 0)
4853 {
4854 errno = ENAMETOOLONG;
4855 return -1;
4856 }
4857 name = fullname;
4858 }
4859
4860 return stat_worker (name, st, ! (flags & AT_SYMLINK_NOFOLLOW));
4861}
4862
16bb7578
GV
4863/* Provide fstat and utime as well as stat for consistent handling of
4864 file timestamps. */
4865int
4866fstat (int desc, struct stat * buf)
4867{
4868 HANDLE fh = (HANDLE) _get_osfhandle (desc);
4869 BY_HANDLE_FILE_INFORMATION info;
e3b88685 4870 unsigned __int64 fake_inode;
16bb7578
GV
4871 int permission;
4872
4873 switch (GetFileType (fh) & ~FILE_TYPE_REMOTE)
4874 {
4875 case FILE_TYPE_DISK:
e3b88685 4876 buf->st_mode = S_IFREG;
16bb7578
GV
4877 if (!GetFileInformationByHandle (fh, &info))
4878 {
4879 errno = EACCES;
4880 return -1;
4881 }
4882 break;
4883 case FILE_TYPE_PIPE:
e3b88685 4884 buf->st_mode = S_IFIFO;
16bb7578
GV
4885 goto non_disk;
4886 case FILE_TYPE_CHAR:
4887 case FILE_TYPE_UNKNOWN:
4888 default:
e3b88685 4889 buf->st_mode = S_IFCHR;
16bb7578
GV
4890 non_disk:
4891 memset (&info, 0, sizeof (info));
4892 info.dwFileAttributes = 0;
4893 info.ftCreationTime = utc_base_ft;
4894 info.ftLastAccessTime = utc_base_ft;
4895 info.ftLastWriteTime = utc_base_ft;
4896 }
4897
4898 if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
e3b88685 4899 buf->st_mode = S_IFDIR;
93e0f0da
JR
4900
4901 buf->st_nlink = info.nNumberOfLinks;
4902 /* Might as well use file index to fake inode values, but this
4903 is not guaranteed to be unique unless we keep a handle open
4904 all the time (even then there are situations where it is
4905 not unique). Reputedly, there are at most 48 bits of info
4906 (on NTFS, presumably less on FAT). */
e3b88685
EZ
4907 fake_inode = info.nFileIndexHigh;
4908 fake_inode <<= 32;
4909 fake_inode += info.nFileIndexLow;
16bb7578
GV
4910
4911 /* MSVC defines _ino_t to be short; other libc's might not. */
4912 if (sizeof (buf->st_ino) == 2)
4913 buf->st_ino = fake_inode ^ (fake_inode >> 16);
4914 else
4915 buf->st_ino = fake_inode;
4916
76e9f7b9
EZ
4917 /* If the caller so requested, get the true file owner and group.
4918 Otherwise, consider the file to belong to the current user. */
4919 if (!w32_stat_get_owner_group || is_windows_9x () == TRUE)
4920 get_file_owner_and_group (NULL, buf);
4921 else
4922 {
4923 PSECURITY_DESCRIPTOR psd = NULL;
4924
4925 psd = get_file_security_desc_by_handle (fh);
4926 if (psd)
4927 {
4928 get_file_owner_and_group (psd, buf);
4929 LocalFree (psd);
4930 }
4931 else
4932 get_file_owner_and_group (NULL, buf);
4933 }
16bb7578
GV
4934
4935 buf->st_dev = info.dwVolumeSerialNumber;
4936 buf->st_rdev = info.dwVolumeSerialNumber;
4937
8aaaec6b
EZ
4938 buf->st_size = info.nFileSizeHigh;
4939 buf->st_size <<= 32;
4940 buf->st_size += info.nFileSizeLow;
16bb7578
GV
4941
4942 /* Convert timestamps to Unix format. */
4943 buf->st_mtime = convert_time (info.ftLastWriteTime);
4944 buf->st_atime = convert_time (info.ftLastAccessTime);
4945 if (buf->st_atime == 0) buf->st_atime = buf->st_mtime;
4946 buf->st_ctime = convert_time (info.ftCreationTime);
4947 if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime;
4948
4949 /* determine rwx permissions */
4950 if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
e3b88685 4951 permission = S_IREAD;
16bb7578 4952 else
e3b88685 4953 permission = S_IREAD | S_IWRITE;
177c0ea7 4954
16bb7578 4955 if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
e3b88685 4956 permission |= S_IEXEC;
16bb7578
GV
4957 else
4958 {
4959#if 0 /* no way of knowing the filename */
4960 char * p = strrchr (name, '.');
4961 if (p != NULL &&
05131107
JR
4962 (xstrcasecmp (p, ".exe") == 0 ||
4963 xstrcasecmp (p, ".com") == 0 ||
4964 xstrcasecmp (p, ".bat") == 0 ||
4965 xstrcasecmp (p, ".cmd") == 0))
e3b88685 4966 permission |= S_IEXEC;
16bb7578
GV
4967#endif
4968 }
4969
4970 buf->st_mode |= permission | (permission >> 3) | (permission >> 6);
4971
4972 return 0;
4973}
4974
b34454d0
EZ
4975/* A version of 'utime' which handles directories as well as
4976 files. */
4977
16bb7578 4978int
86e93460 4979utime (const char *name, struct utimbuf *times)
16bb7578 4980{
86e93460 4981 struct utimbuf deftime;
16bb7578
GV
4982 HANDLE fh;
4983 FILETIME mtime;
4984 FILETIME atime;
4985
4986 if (times == NULL)
4987 {
4988 deftime.modtime = deftime.actime = time (NULL);
4989 times = &deftime;
4990 }
4991
80fcb9d2
EZ
4992 if (w32_unicode_filenames)
4993 {
4994 wchar_t name_utf16[MAX_PATH];
4995
4996 if (filename_to_utf16 (name, name_utf16) != 0)
4997 return -1; /* errno set by filename_to_utf16 */
4998
4999 /* Need write access to set times. */
5000 fh = CreateFileW (name_utf16, FILE_WRITE_ATTRIBUTES,
5001 /* If NAME specifies a directory, FILE_SHARE_DELETE
5002 allows other processes to delete files inside it,
5003 while we have the directory open. */
5004 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
5005 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
5006 }
5007 else
5008 {
5009 char name_ansi[MAX_PATH];
5010
5011 if (filename_to_ansi (name, name_ansi) != 0)
5012 return -1; /* errno set by filename_to_ansi */
5013
5014 fh = CreateFileA (name_ansi, FILE_WRITE_ATTRIBUTES,
5015 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
5016 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
5017 }
7604f298 5018 if (fh != INVALID_HANDLE_VALUE)
16bb7578
GV
5019 {
5020 convert_from_time_t (times->actime, &atime);
5021 convert_from_time_t (times->modtime, &mtime);
5022 if (!SetFileTime (fh, NULL, &atime, &mtime))
5023 {
5024 CloseHandle (fh);
5025 errno = EACCES;
5026 return -1;
5027 }
5028 CloseHandle (fh);
5029 }
5030 else
5031 {
80fcb9d2
EZ
5032 DWORD err = GetLastError ();
5033
5034 switch (err)
5035 {
5036 case ERROR_FILE_NOT_FOUND:
5037 case ERROR_PATH_NOT_FOUND:
5038 case ERROR_INVALID_DRIVE:
5039 case ERROR_BAD_NETPATH:
5040 case ERROR_DEV_NOT_EXIST:
5041 /* ERROR_INVALID_NAME is the error CreateFile sets when the
5042 file name includes ?s, i.e. translation to ANSI failed. */
5043 case ERROR_INVALID_NAME:
5044 errno = ENOENT;
5045 break;
5046 case ERROR_TOO_MANY_OPEN_FILES:
5047 errno = ENFILE;
5048 break;
5049 case ERROR_ACCESS_DENIED:
5050 case ERROR_SHARING_VIOLATION:
5051 errno = EACCES;
5052 break;
5053 default:
5054 errno = EINVAL;
5055 break;
5056 }
16bb7578
GV
5057 return -1;
5058 }
5059 return 0;
5060}
5061
7c80d5ec 5062\f
6dad7178
EZ
5063/* Symlink-related functions. */
5064#ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
5065#define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1
5066#endif
5067
0f7bb05d 5068int
6dad7178 5069symlink (char const *filename, char const *linkname)
0f7bb05d 5070{
eeccd06f 5071 char linkfn[MAX_UTF8_PATH], *tgtfn;
6dad7178
EZ
5072 DWORD flags = 0;
5073 int dir_access, filename_ends_in_slash;
5074
5075 /* Diagnostics follows Posix as much as possible. */
5076 if (filename == NULL || linkname == NULL)
5077 {
5078 errno = EFAULT;
5079 return -1;
5080 }
5081 if (!*filename)
5082 {
5083 errno = ENOENT;
5084 return -1;
5085 }
eeccd06f 5086 if (strlen (filename) > MAX_UTF8_PATH || strlen (linkname) > MAX_UTF8_PATH)
6dad7178
EZ
5087 {
5088 errno = ENAMETOOLONG;
5089 return -1;
5090 }
5091
5092 strcpy (linkfn, map_w32_filename (linkname, NULL));
5093 if ((volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) == 0)
5094 {
5095 errno = EPERM;
5096 return -1;
5097 }
5098
5099 /* Note: since empty FILENAME was already rejected, we can safely
5100 refer to FILENAME[1]. */
5101 if (!(IS_DIRECTORY_SEP (filename[0]) || IS_DEVICE_SEP (filename[1])))
5102 {
5103 /* Non-absolute FILENAME is understood as being relative to
5104 LINKNAME's directory. We need to prepend that directory to
14f20728 5105 FILENAME to get correct results from faccessat below, since
6dad7178
EZ
5106 otherwise it will interpret FILENAME relative to the
5107 directory where the Emacs process runs. Note that
5108 make-symbolic-link always makes sure LINKNAME is a fully
5109 expanded file name. */
eeccd06f 5110 char tem[MAX_UTF8_PATH];
6dad7178
EZ
5111 char *p = linkfn + strlen (linkfn);
5112
eeccd06f
EZ
5113 while (p > linkfn && !IS_ANY_SEP (p[-1]))
5114 p--;
6dad7178
EZ
5115 if (p > linkfn)
5116 strncpy (tem, linkfn, p - linkfn);
5117 tem[p - linkfn] = '\0';
5118 strcat (tem, filename);
14f20728 5119 dir_access = faccessat (AT_FDCWD, tem, D_OK, AT_EACCESS);
6dad7178
EZ
5120 }
5121 else
14f20728 5122 dir_access = faccessat (AT_FDCWD, filename, D_OK, AT_EACCESS);
6dad7178
EZ
5123
5124 /* Since Windows distinguishes between symlinks to directories and
8b2e00a3 5125 to files, we provide a kludgy feature: if FILENAME doesn't
6dad7178
EZ
5126 exist, but ends in a slash, we create a symlink to directory. If
5127 FILENAME exists and is a directory, we always create a symlink to
5128 directory. */
eeccd06f 5129 filename_ends_in_slash = IS_DIRECTORY_SEP (filename[strlen (filename) - 1]);
6dad7178
EZ
5130 if (dir_access == 0 || filename_ends_in_slash)
5131 flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
5132
5133 tgtfn = (char *)map_w32_filename (filename, NULL);
5134 if (filename_ends_in_slash)
5135 tgtfn[strlen (tgtfn) - 1] = '\0';
5136
5137 errno = 0;
5138 if (!create_symbolic_link (linkfn, tgtfn, flags))
5139 {
5140 /* ENOSYS is set by create_symbolic_link, when it detects that
5141 the OS doesn't support the CreateSymbolicLink API. */
5142 if (errno != ENOSYS)
5143 {
5144 DWORD w32err = GetLastError ();
5145
5146 switch (w32err)
5147 {
5148 /* ERROR_SUCCESS is sometimes returned when LINKFN and
5149 TGTFN point to the same file name, go figure. */
5150 case ERROR_SUCCESS:
5151 case ERROR_FILE_EXISTS:
5152 errno = EEXIST;
5153 break;
5154 case ERROR_ACCESS_DENIED:
5155 errno = EACCES;
5156 break;
5157 case ERROR_FILE_NOT_FOUND:
5158 case ERROR_PATH_NOT_FOUND:
5159 case ERROR_BAD_NETPATH:
5160 case ERROR_INVALID_REPARSE_DATA:
5161 errno = ENOENT;
5162 break;
5163 case ERROR_DIRECTORY:
5164 errno = EISDIR;
5165 break;
5166 case ERROR_PRIVILEGE_NOT_HELD:
5167 case ERROR_NOT_ALL_ASSIGNED:
5168 errno = EPERM;
5169 break;
5170 case ERROR_DISK_FULL:
5171 errno = ENOSPC;
5172 break;
5173 default:
5174 errno = EINVAL;
5175 break;
5176 }
5177 }
5178 return -1;
5179 }
5180 return 0;
0f7bb05d
EZ
5181}
5182
6dad7178
EZ
5183/* A quick inexpensive test of whether FILENAME identifies a file that
5184 is a symlink. Returns non-zero if it is, zero otherwise. FILENAME
5185 must already be in the normalized form returned by
5186 map_w32_filename.
5187
5188 Note: for repeated operations on many files, it is best to test
5189 whether the underlying volume actually supports symlinks, by
5190 testing the FILE_SUPPORTS_REPARSE_POINTS bit in volume's flags, and
5191 avoid the call to this function if it doesn't. That's because the
8b2e00a3 5192 call to GetFileAttributes takes a non-negligible time, especially
6dad7178
EZ
5193 on non-local or removable filesystems. See stat_worker for an
5194 example of how to do that. */
5195static int
5196is_symlink (const char *filename)
5197{
5198 DWORD attrs;
eeccd06f
EZ
5199 wchar_t filename_w[MAX_PATH];
5200 char filename_a[MAX_PATH];
5201 WIN32_FIND_DATAW wfdw;
5202 WIN32_FIND_DATAA wfda;
6dad7178 5203 HANDLE fh;
eeccd06f 5204 int attrs_mean_symlink;
6dad7178 5205
eeccd06f
EZ
5206 if (w32_unicode_filenames)
5207 {
5208 filename_to_utf16 (filename, filename_w);
5209 attrs = GetFileAttributesW (filename_w);
5210 }
5211 else
5212 {
5213 filename_to_ansi (filename, filename_a);
5214 attrs = GetFileAttributesA (filename_a);
5215 }
6dad7178
EZ
5216 if (attrs == -1)
5217 {
5218 DWORD w32err = GetLastError ();
5219
5220 switch (w32err)
5221 {
5222 case ERROR_BAD_NETPATH: /* network share, can't be a symlink */
5223 break;
5224 case ERROR_ACCESS_DENIED:
5225 errno = EACCES;
5226 break;
5227 case ERROR_FILE_NOT_FOUND:
5228 case ERROR_PATH_NOT_FOUND:
5229 default:
5230 errno = ENOENT;
5231 break;
5232 }
5233 return 0;
5234 }
5235 if ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
5236 return 0;
5237 logon_network_drive (filename);
eeccd06f
EZ
5238 if (w32_unicode_filenames)
5239 {
5240 fh = FindFirstFileW (filename_w, &wfdw);
5241 attrs_mean_symlink =
5242 (wfdw.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0
5243 && (wfdw.dwReserved0 & IO_REPARSE_TAG_SYMLINK) == IO_REPARSE_TAG_SYMLINK;
5244 }
5245 else if (_mbspbrk (filename_a, "?"))
5246 {
5247 /* filename_to_ansi failed to convert the file name. */
5248 errno = ENOENT;
5249 return 0;
5250 }
5251 else
5252 {
5253 fh = FindFirstFileA (filename_a, &wfda);
5254 attrs_mean_symlink =
5255 (wfda.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0
5256 && (wfda.dwReserved0 & IO_REPARSE_TAG_SYMLINK) == IO_REPARSE_TAG_SYMLINK;
5257 }
6dad7178
EZ
5258 if (fh == INVALID_HANDLE_VALUE)
5259 return 0;
5260 FindClose (fh);
eeccd06f 5261 return attrs_mean_symlink;
6dad7178
EZ
5262}
5263
5264/* If NAME identifies a symbolic link, copy into BUF the file name of
5265 the symlink's target. Copy at most BUF_SIZE bytes, and do NOT
5266 null-terminate the target name, even if it fits. Return the number
5267 of bytes copied, or -1 if NAME is not a symlink or any error was
5268 encountered while resolving it. The file name copied into BUF is
5269 encoded in the current ANSI codepage. */
0f7bb05d 5270ssize_t
6dad7178 5271readlink (const char *name, char *buf, size_t buf_size)
0f7bb05d 5272{
6dad7178
EZ
5273 const char *path;
5274 TOKEN_PRIVILEGES privs;
5275 int restore_privs = 0;
5276 HANDLE sh;
5277 ssize_t retval;
eeccd06f 5278 char resolved[MAX_UTF8_PATH];
6dad7178
EZ
5279
5280 if (name == NULL)
5281 {
5282 errno = EFAULT;
5283 return -1;
5284 }
5285 if (!*name)
5286 {
5287 errno = ENOENT;
5288 return -1;
5289 }
5290
5291 path = map_w32_filename (name, NULL);
5292
eeccd06f 5293 if (strlen (path) > MAX_UTF8_PATH)
6dad7178
EZ
5294 {
5295 errno = ENAMETOOLONG;
5296 return -1;
5297 }
5298
5299 errno = 0;
5300 if (is_windows_9x () == TRUE
5301 || (volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) == 0
5302 || !is_symlink (path))
5303 {
5304 if (!errno)
5305 errno = EINVAL; /* not a symlink */
5306 return -1;
5307 }
5308
5309 /* Done with simple tests, now we're in for some _real_ work. */
5310 if (enable_privilege (SE_BACKUP_NAME, TRUE, &privs))
5311 restore_privs = 1;
5312 /* Implementation note: From here and onward, don't return early,
5313 since that will fail to restore the original set of privileges of
5314 the calling thread. */
5315
5316 retval = -1; /* not too optimistic, are we? */
5317
5318 /* Note: In the next call to CreateFile, we use zero as the 2nd
5319 argument because, when the symlink is a hidden/system file,
5320 e.g. 'C:\Users\All Users', GENERIC_READ fails with
5321 ERROR_ACCESS_DENIED. Zero seems to work just fine, both for file
5322 and directory symlinks. */
eeccd06f
EZ
5323 if (w32_unicode_filenames)
5324 {
5325 wchar_t path_w[MAX_PATH];
5326
5327 filename_to_utf16 (path, path_w);
5328 sh = CreateFileW (path_w, 0, 0, NULL, OPEN_EXISTING,
5329 FILE_FLAG_OPEN_REPARSE_POINT
5330 | FILE_FLAG_BACKUP_SEMANTICS,
5331 NULL);
5332 }
5333 else
5334 {
5335 char path_a[MAX_PATH];
5336
5337 filename_to_ansi (path, path_a);
5338 sh = CreateFileA (path_a, 0, 0, NULL, OPEN_EXISTING,
5339 FILE_FLAG_OPEN_REPARSE_POINT
5340 | FILE_FLAG_BACKUP_SEMANTICS,
5341 NULL);
5342 }
6dad7178
EZ
5343 if (sh != INVALID_HANDLE_VALUE)
5344 {
5345 BYTE reparse_buf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
5346 REPARSE_DATA_BUFFER *reparse_data = (REPARSE_DATA_BUFFER *)&reparse_buf[0];
5347 DWORD retbytes;
5348
5349 if (!DeviceIoControl (sh, FSCTL_GET_REPARSE_POINT, NULL, 0,
5350 reparse_buf, MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
5351 &retbytes, NULL))
5352 errno = EIO;
5353 else if (reparse_data->ReparseTag != IO_REPARSE_TAG_SYMLINK)
5354 errno = EINVAL;
5355 else
5356 {
474d441e 5357 /* Copy the link target name, in wide characters, from
6dad7178
EZ
5358 reparse_data, then convert it to multibyte encoding in
5359 the current locale's codepage. */
5360 WCHAR *lwname;
eeccd06f 5361 size_t lname_size;
6dad7178
EZ
5362 USHORT lwname_len =
5363 reparse_data->SymbolicLinkReparseBuffer.PrintNameLength;
5364 WCHAR *lwname_src =
5365 reparse_data->SymbolicLinkReparseBuffer.PathBuffer
5366 + reparse_data->SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(WCHAR);
eeccd06f 5367 size_t size_to_copy = buf_size;
6dad7178
EZ
5368
5369 /* According to MSDN, PrintNameLength does not include the
5370 terminating null character. */
5371 lwname = alloca ((lwname_len + 1) * sizeof(WCHAR));
5372 memcpy (lwname, lwname_src, lwname_len);
5373 lwname[lwname_len/sizeof(WCHAR)] = 0; /* null-terminate */
eeccd06f
EZ
5374 filename_from_utf16 (lwname, resolved);
5375 dostounix_filename (resolved);
5376 lname_size = strlen (resolved) + 1;
5377 if (lname_size <= buf_size)
5378 size_to_copy = lname_size;
5379 strncpy (buf, resolved, size_to_copy);
5380 /* Success! */
5381 retval = size_to_copy;
6dad7178
EZ
5382 }
5383 CloseHandle (sh);
5384 }
5385 else
5386 {
5387 /* CreateFile failed. */
5388 DWORD w32err2 = GetLastError ();
5389
5390 switch (w32err2)
5391 {
5392 case ERROR_FILE_NOT_FOUND:
5393 case ERROR_PATH_NOT_FOUND:
5394 errno = ENOENT;
5395 break;
5396 case ERROR_ACCESS_DENIED:
5397 case ERROR_TOO_MANY_OPEN_FILES:
5398 errno = EACCES;
5399 break;
5400 default:
5401 errno = EPERM;
5402 break;
5403 }
5404 }
5405 if (restore_privs)
5406 {
5407 restore_privilege (&privs);
5408 revert_to_self ();
5409 }
5410
5411 return retval;
0f7bb05d
EZ
5412}
5413
8654f9d7
PE
5414ssize_t
5415readlinkat (int fd, char const *name, char *buffer,
5416 size_t buffer_size)
5417{
5418 /* Rely on a hack: an open directory is modeled as file descriptor 0,
5419 as in fstatat. FIXME: Add proper support for readlinkat. */
eeccd06f 5420 char fullname[MAX_UTF8_PATH];
8654f9d7
PE
5421
5422 if (fd != AT_FDCWD)
5423 {
5424 if (_snprintf (fullname, sizeof fullname, "%s/%s", dir_pathname, name)
5425 < 0)
5426 {
5427 errno = ENAMETOOLONG;
5428 return -1;
5429 }
5430 name = fullname;
5431 }
5432
5433 return readlink (name, buffer, buffer_size);
5434}
5435
6dad7178
EZ
5436/* If FILE is a symlink, return its target (stored in a static
5437 buffer); otherwise return FILE.
5438
5439 This function repeatedly resolves symlinks in the last component of
5440 a chain of symlink file names, as in foo -> bar -> baz -> ...,
5441 until it arrives at a file whose last component is not a symlink,
5442 or some error occurs. It returns the target of the last
5443 successfully resolved symlink in the chain. If it succeeds to
5444 resolve even a single symlink, the value returned is an absolute
5445 file name with backslashes (result of GetFullPathName). By
5446 contrast, if the original FILE is returned, it is unaltered.
5447
5448 Note: This function can set errno even if it succeeds.
5449
5450 Implementation note: we only resolve the last portion ("basename")
5451 of the argument FILE and of each following file in the chain,
5452 disregarding any possible symlinks in its leading directories.
5453 This is because Windows system calls and library functions
5454 transparently resolve symlinks in leading directories and return
5455 correct information, as long as the basename is not a symlink. */
5456static char *
5457chase_symlinks (const char *file)
5458{
eeccd06f
EZ
5459 static char target[MAX_UTF8_PATH];
5460 char link[MAX_UTF8_PATH];
5461 wchar_t target_w[MAX_PATH], link_w[MAX_PATH];
5462 char target_a[MAX_PATH], link_a[MAX_PATH];
6dad7178
EZ
5463 ssize_t res, link_len;
5464 int loop_count = 0;
5465
5466 if (is_windows_9x () == TRUE || !is_symlink (file))
5467 return (char *)file;
5468
eeccd06f
EZ
5469 if (w32_unicode_filenames)
5470 {
5471 wchar_t file_w[MAX_PATH];
5472
5473 filename_to_utf16 (file, file_w);
5474 if (GetFullPathNameW (file_w, MAX_PATH, link_w, NULL) == 0)
5475 return (char *)file;
5476 filename_from_utf16 (link_w, link);
5477 }
5478 else
5479 {
5480 char file_a[MAX_PATH];
5481
5482 filename_to_ansi (file, file_a);
5483 if (GetFullPathNameA (file_a, MAX_PATH, link_a, NULL) == 0)
5484 return (char *)file;
5485 filename_from_ansi (link_a, link);
5486 }
5487 link_len = strlen (link);
6dad7178
EZ
5488
5489 target[0] = '\0';
5490 do {
5491
5492 /* Remove trailing slashes, as we want to resolve the last
5493 non-trivial part of the link name. */
eeccd06f
EZ
5494 while (link_len > 3 && IS_DIRECTORY_SEP (link[link_len-1]))
5495 link[link_len--] = '\0';
6dad7178 5496
eeccd06f 5497 res = readlink (link, target, MAX_UTF8_PATH);
6dad7178
EZ
5498 if (res > 0)
5499 {
5500 target[res] = '\0';
5501 if (!(IS_DEVICE_SEP (target[1])
25a20a3a 5502 || (IS_DIRECTORY_SEP (target[0]) && IS_DIRECTORY_SEP (target[1]))))
6dad7178
EZ
5503 {
5504 /* Target is relative. Append it to the directory part of
5505 the symlink, then copy the result back to target. */
5506 char *p = link + link_len;
5507
eeccd06f
EZ
5508 while (p > link && !IS_ANY_SEP (p[-1]))
5509 p--;
6dad7178
EZ
5510 strcpy (p, target);
5511 strcpy (target, link);
5512 }
5513 /* Resolve any "." and ".." to get a fully-qualified file name
5514 in link[] again. */
eeccd06f
EZ
5515 if (w32_unicode_filenames)
5516 {
5517 filename_to_utf16 (target, target_w);
5518 link_len = GetFullPathNameW (target_w, MAX_PATH, link_w, NULL);
5519 if (link_len > 0)
5520 filename_from_utf16 (link_w, link);
5521 }
5522 else
5523 {
5524 filename_to_ansi (target, target_a);
5525 link_len = GetFullPathNameA (target_a, MAX_PATH, link_a, NULL);
5526 if (link_len > 0)
5527 filename_from_ansi (link_a, link);
5528 }
5529 link_len = strlen (link);
6dad7178
EZ
5530 }
5531 } while (res > 0 && link_len > 0 && ++loop_count <= 100);
5532
5533 if (loop_count > 100)
5534 errno = ELOOP;
5535
5536 if (target[0] == '\0') /* not a single call to readlink succeeded */
5537 return (char *)file;
5538 return target;
5539}
5540
66447e07
EZ
5541\f
5542/* Posix ACL emulation. */
5543
5544int
5545acl_valid (acl_t acl)
5546{
5547 return is_valid_security_descriptor ((PSECURITY_DESCRIPTOR)acl) ? 0 : -1;
5548}
5549
5550char *
5551acl_to_text (acl_t acl, ssize_t *size)
5552{
5553 LPTSTR str_acl;
5554 SECURITY_INFORMATION flags =
5555 OWNER_SECURITY_INFORMATION |
5556 GROUP_SECURITY_INFORMATION |
5557 DACL_SECURITY_INFORMATION;
5558 char *retval = NULL;
b764d018 5559 ULONG local_size;
66447e07
EZ
5560 int e = errno;
5561
5562 errno = 0;
5563
5564 if (convert_sd_to_sddl ((PSECURITY_DESCRIPTOR)acl, SDDL_REVISION_1, flags, &str_acl, &local_size))
5565 {
5566 errno = e;
5567 /* We don't want to mix heaps, so we duplicate the string in our
5568 heap and free the one allocated by the API. */
5569 retval = xstrdup (str_acl);
5570 if (size)
5571 *size = local_size;
5572 LocalFree (str_acl);
5573 }
5574 else if (errno != ENOTSUP)
5575 errno = EINVAL;
5576
5577 return retval;
5578}
5579
5580acl_t
5581acl_from_text (const char *acl_str)
5582{
5583 PSECURITY_DESCRIPTOR psd, retval = NULL;
5584 ULONG sd_size;
5585 int e = errno;
5586
5587 errno = 0;
5588
5589 if (convert_sddl_to_sd (acl_str, SDDL_REVISION_1, &psd, &sd_size))
5590 {
5591 errno = e;
5592 retval = xmalloc (sd_size);
5593 memcpy (retval, psd, sd_size);
5594 LocalFree (psd);
5595 }
5596 else if (errno != ENOTSUP)
5597 errno = EINVAL;
5598
5599 return retval;
5600}
5601
5602int
5603acl_free (void *ptr)
5604{
5605 xfree (ptr);
5606 return 0;
5607}
5608
5609acl_t
5610acl_get_file (const char *fname, acl_type_t type)
5611{
5612 PSECURITY_DESCRIPTOR psd = NULL;
474d441e 5613 const char *filename;
66447e07
EZ
5614
5615 if (type == ACL_TYPE_ACCESS)
5616 {
5617 DWORD sd_len, err;
5618 SECURITY_INFORMATION si =
5619 OWNER_SECURITY_INFORMATION |
5620 GROUP_SECURITY_INFORMATION |
5621 DACL_SECURITY_INFORMATION ;
5622 int e = errno;
5623
474d441e
EZ
5624 filename = map_w32_filename (fname, NULL);
5625 if ((volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) != 0)
5626 fname = chase_symlinks (filename);
5627 else
5628 fname = filename;
5629
66447e07
EZ
5630 errno = 0;
5631 if (!get_file_security (fname, si, psd, 0, &sd_len)
5632 && errno != ENOTSUP)
5633 {
5634 err = GetLastError ();
5635 if (err == ERROR_INSUFFICIENT_BUFFER)
5636 {
5637 psd = xmalloc (sd_len);
5638 if (!get_file_security (fname, si, psd, sd_len, &sd_len))
5639 {
5640 xfree (psd);
5641 errno = EIO;
5642 psd = NULL;
5643 }
5644 }
5645 else if (err == ERROR_FILE_NOT_FOUND
5646 || err == ERROR_PATH_NOT_FOUND)
5647 errno = ENOENT;
5648 else
5649 errno = EIO;
5650 }
5651 else if (!errno)
5652 errno = e;
5653 }
5654 else if (type != ACL_TYPE_DEFAULT)
5655 errno = EINVAL;
5656
5657 return psd;
5658}
5659
5660int
5661acl_set_file (const char *fname, acl_type_t type, acl_t acl)
5662{
5663 TOKEN_PRIVILEGES old1, old2;
5664 DWORD err;
66447e07
EZ
5665 int st = 0, retval = -1;
5666 SECURITY_INFORMATION flags = 0;
5667 PSID psid;
5668 PACL pacl;
5669 BOOL dflt;
5670 BOOL dacl_present;
5671 int e;
474d441e 5672 const char *filename;
66447e07
EZ
5673
5674 if (acl_valid (acl) != 0
5675 || (type != ACL_TYPE_DEFAULT && type != ACL_TYPE_ACCESS))
5676 {
5677 errno = EINVAL;
5678 return -1;
5679 }
5680
5681 if (type == ACL_TYPE_DEFAULT)
5682 {
5683 errno = ENOSYS;
5684 return -1;
5685 }
5686
474d441e
EZ
5687 filename = map_w32_filename (fname, NULL);
5688 if ((volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) != 0)
5689 fname = chase_symlinks (filename);
5690 else
5691 fname = filename;
5692
66447e07
EZ
5693 if (get_security_descriptor_owner ((PSECURITY_DESCRIPTOR)acl, &psid, &dflt)
5694 && psid)
5695 flags |= OWNER_SECURITY_INFORMATION;
5696 if (get_security_descriptor_group ((PSECURITY_DESCRIPTOR)acl, &psid, &dflt)
5697 && psid)
5698 flags |= GROUP_SECURITY_INFORMATION;
5699 if (get_security_descriptor_dacl ((PSECURITY_DESCRIPTOR)acl, &dacl_present,
5700 &pacl, &dflt)
5701 && dacl_present)
5702 flags |= DACL_SECURITY_INFORMATION;
5703 if (!flags)
5704 return 0;
5705
5706 /* According to KB-245153, setting the owner will succeed if either:
5707 (1) the caller is the user who will be the new owner, and has the
5708 SE_TAKE_OWNERSHIP privilege, or
5709 (2) the caller has the SE_RESTORE privilege, in which case she can
5710 set any valid user or group as the owner
5711
5712 We request below both SE_TAKE_OWNERSHIP and SE_RESTORE
5713 privileges, and disregard any failures in obtaining them. If
5714 these privileges cannot be obtained, and do not already exist in
5715 the calling thread's security token, this function could fail
5716 with EPERM. */
5717 if (enable_privilege (SE_TAKE_OWNERSHIP_NAME, TRUE, &old1))
5718 st++;
5719 if (enable_privilege (SE_RESTORE_NAME, TRUE, &old2))
5720 st++;
5721
5722 e = errno;
5723 errno = 0;
84e5ed96 5724 if (!set_file_security ((char *)fname, flags, (PSECURITY_DESCRIPTOR)acl))
474d441e 5725 {
84e5ed96 5726 err = GetLastError ();
40ff07a5 5727
84e5ed96
EZ
5728 if (errno == ENOTSUP)
5729 ;
5730 else if (err == ERROR_INVALID_OWNER
5731 || err == ERROR_NOT_ALL_ASSIGNED
5732 || err == ERROR_ACCESS_DENIED)
40ff07a5 5733 {
84e5ed96
EZ
5734 /* Maybe the requested ACL and the one the file already has
5735 are identical, in which case we can silently ignore the
5736 failure. (And no, Windows doesn't.) */
5737 acl_t current_acl = acl_get_file (fname, ACL_TYPE_ACCESS);
40ff07a5 5738
84e5ed96
EZ
5739 errno = EPERM;
5740 if (current_acl)
40ff07a5 5741 {
84e5ed96
EZ
5742 char *acl_from = acl_to_text (current_acl, NULL);
5743 char *acl_to = acl_to_text (acl, NULL);
5744
5745 if (acl_from && acl_to && xstrcasecmp (acl_from, acl_to) == 0)
5746 {
5747 retval = 0;
5748 errno = e;
5749 }
5750 if (acl_from)
5751 acl_free (acl_from);
5752 if (acl_to)
5753 acl_free (acl_to);
5754 acl_free (current_acl);
40ff07a5 5755 }
40ff07a5 5756 }
84e5ed96
EZ
5757 else if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND)
5758 errno = ENOENT;
5759 else
5760 errno = EACCES;
5761 }
5762 else
5763 {
5764 retval = 0;
5765 errno = e;
5766 }
5767
5768 if (st)
5769 {
5770 if (st >= 2)
5771 restore_privilege (&old2);
5772 restore_privilege (&old1);
5773 revert_to_self ();
40ff07a5 5774 }
66447e07
EZ
5775
5776 return retval;
5777}
5778
5779\f
6dad7178
EZ
5780/* MS-Windows version of careadlinkat (cf. ../lib/careadlinkat.c). We
5781 have a fixed max size for file names, so we don't need the kind of
5782 alloc/malloc/realloc dance the gnulib version does. We also don't
5783 support FD-relative symlinks. */
973f782d
EZ
5784char *
5785careadlinkat (int fd, char const *filename,
5786 char *buffer, size_t buffer_size,
5787 struct allocator const *alloc,
5788 ssize_t (*preadlinkat) (int, char const *, char *, size_t))
5789{
eeccd06f 5790 char linkname[MAX_UTF8_PATH];
6dad7178
EZ
5791 ssize_t link_size;
5792
6dad7178
EZ
5793 link_size = preadlinkat (fd, filename, linkname, sizeof(linkname));
5794
5795 if (link_size > 0)
5796 {
5797 char *retval = buffer;
5798
5799 linkname[link_size++] = '\0';
5800 if (link_size > buffer_size)
5801 retval = (char *)(alloc ? alloc->allocate : xmalloc) (link_size);
5802 if (retval)
5803 memcpy (retval, linkname, link_size);
5804
5805 return retval;
5806 }
973f782d
EZ
5807 return NULL;
5808}
5809
0f7bb05d 5810\f
7c80d5ec
EZ
5811/* Support for browsing other processes and their attributes. See
5812 process.c for the Lisp bindings. */
5813
5814/* Helper wrapper functions. */
5815
bedf4aab
JB
5816static HANDLE WINAPI
5817create_toolhelp32_snapshot (DWORD Flags, DWORD Ignored)
7c80d5ec
EZ
5818{
5819 static CreateToolhelp32Snapshot_Proc s_pfn_Create_Toolhelp32_Snapshot = NULL;
5820
5821 if (g_b_init_create_toolhelp32_snapshot == 0)
5822 {
5823 g_b_init_create_toolhelp32_snapshot = 1;
5824 s_pfn_Create_Toolhelp32_Snapshot = (CreateToolhelp32Snapshot_Proc)
5825 GetProcAddress (GetModuleHandle ("kernel32.dll"),
5826 "CreateToolhelp32Snapshot");
5827 }
5828 if (s_pfn_Create_Toolhelp32_Snapshot == NULL)
5829 {
5830 return INVALID_HANDLE_VALUE;
5831 }
5832 return (s_pfn_Create_Toolhelp32_Snapshot (Flags, Ignored));
5833}
5834
bedf4aab
JB
5835static BOOL WINAPI
5836process32_first (HANDLE hSnapshot, LPPROCESSENTRY32 lppe)
7c80d5ec
EZ
5837{
5838 static Process32First_Proc s_pfn_Process32_First = NULL;
5839
5840 if (g_b_init_process32_first == 0)
5841 {
5842 g_b_init_process32_first = 1;
5843 s_pfn_Process32_First = (Process32First_Proc)
5844 GetProcAddress (GetModuleHandle ("kernel32.dll"),
5845 "Process32First");
5846 }
5847 if (s_pfn_Process32_First == NULL)
5848 {
5849 return FALSE;
5850 }
5851 return (s_pfn_Process32_First (hSnapshot, lppe));
5852}
5853
bedf4aab
JB
5854static BOOL WINAPI
5855process32_next (HANDLE hSnapshot, LPPROCESSENTRY32 lppe)
7c80d5ec
EZ
5856{
5857 static Process32Next_Proc s_pfn_Process32_Next = NULL;
5858
5859 if (g_b_init_process32_next == 0)
5860 {
5861 g_b_init_process32_next = 1;
5862 s_pfn_Process32_Next = (Process32Next_Proc)
5863 GetProcAddress (GetModuleHandle ("kernel32.dll"),
5864 "Process32Next");
5865 }
5866 if (s_pfn_Process32_Next == NULL)
5867 {
5868 return FALSE;
5869 }
5870 return (s_pfn_Process32_Next (hSnapshot, lppe));
5871}
5872
bedf4aab
JB
5873static BOOL WINAPI
5874open_thread_token (HANDLE ThreadHandle,
5875 DWORD DesiredAccess,
5876 BOOL OpenAsSelf,
5877 PHANDLE TokenHandle)
7c80d5ec
EZ
5878{
5879 static OpenThreadToken_Proc s_pfn_Open_Thread_Token = NULL;
5880 HMODULE hm_advapi32 = NULL;
5881 if (is_windows_9x () == TRUE)
5882 {
5883 SetLastError (ERROR_NOT_SUPPORTED);
5884 return FALSE;
5885 }
5886 if (g_b_init_open_thread_token == 0)
5887 {
5888 g_b_init_open_thread_token = 1;
5889 hm_advapi32 = LoadLibrary ("Advapi32.dll");
5890 s_pfn_Open_Thread_Token =
5891 (OpenThreadToken_Proc) GetProcAddress (hm_advapi32, "OpenThreadToken");
5892 }
5893 if (s_pfn_Open_Thread_Token == NULL)
5894 {
5895 SetLastError (ERROR_NOT_SUPPORTED);
5896 return FALSE;
5897 }
5898 return (
5899 s_pfn_Open_Thread_Token (
5900 ThreadHandle,
5901 DesiredAccess,
5902 OpenAsSelf,
5903 TokenHandle)
5904 );
5905}
5906
bedf4aab
JB
5907static BOOL WINAPI
5908impersonate_self (SECURITY_IMPERSONATION_LEVEL ImpersonationLevel)
7c80d5ec
EZ
5909{
5910 static ImpersonateSelf_Proc s_pfn_Impersonate_Self = NULL;
5911 HMODULE hm_advapi32 = NULL;
5912 if (is_windows_9x () == TRUE)
5913 {
5914 return FALSE;
5915 }
5916 if (g_b_init_impersonate_self == 0)
5917 {
5918 g_b_init_impersonate_self = 1;
5919 hm_advapi32 = LoadLibrary ("Advapi32.dll");
5920 s_pfn_Impersonate_Self =
5921 (ImpersonateSelf_Proc) GetProcAddress (hm_advapi32, "ImpersonateSelf");
5922 }
5923 if (s_pfn_Impersonate_Self == NULL)
5924 {
5925 return FALSE;
5926 }
5927 return s_pfn_Impersonate_Self (ImpersonationLevel);
5928}
5929
bedf4aab
JB
5930static BOOL WINAPI
5931revert_to_self (void)
7c80d5ec
EZ
5932{
5933 static RevertToSelf_Proc s_pfn_Revert_To_Self = NULL;
5934 HMODULE hm_advapi32 = NULL;
5935 if (is_windows_9x () == TRUE)
5936 {
5937 return FALSE;
5938 }
5939 if (g_b_init_revert_to_self == 0)
5940 {
5941 g_b_init_revert_to_self = 1;
5942 hm_advapi32 = LoadLibrary ("Advapi32.dll");
5943 s_pfn_Revert_To_Self =
5944 (RevertToSelf_Proc) GetProcAddress (hm_advapi32, "RevertToSelf");
5945 }
5946 if (s_pfn_Revert_To_Self == NULL)
5947 {
5948 return FALSE;
5949 }
5950 return s_pfn_Revert_To_Self ();
5951}
5952
bedf4aab
JB
5953static BOOL WINAPI
5954get_process_memory_info (HANDLE h_proc,
5955 PPROCESS_MEMORY_COUNTERS mem_counters,
5956 DWORD bufsize)
7c80d5ec
EZ
5957{
5958 static GetProcessMemoryInfo_Proc s_pfn_Get_Process_Memory_Info = NULL;
5959 HMODULE hm_psapi = NULL;
5960 if (is_windows_9x () == TRUE)
5961 {
5962 return FALSE;
5963 }
5964 if (g_b_init_get_process_memory_info == 0)
5965 {
5966 g_b_init_get_process_memory_info = 1;
5967 hm_psapi = LoadLibrary ("Psapi.dll");
5968 if (hm_psapi)
5969 s_pfn_Get_Process_Memory_Info = (GetProcessMemoryInfo_Proc)
5970 GetProcAddress (hm_psapi, "GetProcessMemoryInfo");
5971 }
5972 if (s_pfn_Get_Process_Memory_Info == NULL)
5973 {
5974 return FALSE;
5975 }
5976 return s_pfn_Get_Process_Memory_Info (h_proc, mem_counters, bufsize);
5977}
5978
bedf4aab
JB
5979static BOOL WINAPI
5980get_process_working_set_size (HANDLE h_proc,
cb576b5c
FP
5981 PSIZE_T minrss,
5982 PSIZE_T maxrss)
7c80d5ec
EZ
5983{
5984 static GetProcessWorkingSetSize_Proc
5985 s_pfn_Get_Process_Working_Set_Size = NULL;
5986
5987 if (is_windows_9x () == TRUE)
5988 {
5989 return FALSE;
5990 }
5991 if (g_b_init_get_process_working_set_size == 0)
5992 {
5993 g_b_init_get_process_working_set_size = 1;
5994 s_pfn_Get_Process_Working_Set_Size = (GetProcessWorkingSetSize_Proc)
5995 GetProcAddress (GetModuleHandle ("kernel32.dll"),
5996 "GetProcessWorkingSetSize");
5997 }
5998 if (s_pfn_Get_Process_Working_Set_Size == NULL)
5999 {
6000 return FALSE;
6001 }
6002 return s_pfn_Get_Process_Working_Set_Size (h_proc, minrss, maxrss);
6003}
6004
bedf4aab
JB
6005static BOOL WINAPI
6006global_memory_status (MEMORYSTATUS *buf)
7c80d5ec
EZ
6007{
6008 static GlobalMemoryStatus_Proc s_pfn_Global_Memory_Status = NULL;
6009
6010 if (is_windows_9x () == TRUE)
6011 {
6012 return FALSE;
6013 }
6014 if (g_b_init_global_memory_status == 0)
6015 {
6016 g_b_init_global_memory_status = 1;
6017 s_pfn_Global_Memory_Status = (GlobalMemoryStatus_Proc)
6018 GetProcAddress (GetModuleHandle ("kernel32.dll"),
6019 "GlobalMemoryStatus");
6020 }
6021 if (s_pfn_Global_Memory_Status == NULL)
6022 {
6023 return FALSE;
6024 }
6025 return s_pfn_Global_Memory_Status (buf);
6026}
6027
bedf4aab
JB
6028static BOOL WINAPI
6029global_memory_status_ex (MEMORY_STATUS_EX *buf)
7c80d5ec
EZ
6030{
6031 static GlobalMemoryStatusEx_Proc s_pfn_Global_Memory_Status_Ex = NULL;
6032
6033 if (is_windows_9x () == TRUE)
6034 {
6035 return FALSE;
6036 }
6037 if (g_b_init_global_memory_status_ex == 0)
6038 {
6039 g_b_init_global_memory_status_ex = 1;
6040 s_pfn_Global_Memory_Status_Ex = (GlobalMemoryStatusEx_Proc)
6041 GetProcAddress (GetModuleHandle ("kernel32.dll"),
6042 "GlobalMemoryStatusEx");
6043 }
6044 if (s_pfn_Global_Memory_Status_Ex == NULL)
6045 {
6046 return FALSE;
6047 }
6048 return s_pfn_Global_Memory_Status_Ex (buf);
6049}
6050
6051Lisp_Object
b56ceb92 6052list_system_processes (void)
7c80d5ec
EZ
6053{
6054 struct gcpro gcpro1;
6055 Lisp_Object proclist = Qnil;
6056 HANDLE h_snapshot;
6057
6058 h_snapshot = create_toolhelp32_snapshot (TH32CS_SNAPPROCESS, 0);
6059
6060 if (h_snapshot != INVALID_HANDLE_VALUE)
6061 {
6062 PROCESSENTRY32 proc_entry;
6063 DWORD proc_id;
6064 BOOL res;
6065
6066 GCPRO1 (proclist);
6067
6068 proc_entry.dwSize = sizeof (PROCESSENTRY32);
6069 for (res = process32_first (h_snapshot, &proc_entry); res;
6070 res = process32_next (h_snapshot, &proc_entry))
6071 {
6072 proc_id = proc_entry.th32ProcessID;
6073 proclist = Fcons (make_fixnum_or_float (proc_id), proclist);
6074 }
6075
6076 CloseHandle (h_snapshot);
6077 UNGCPRO;
6078 proclist = Fnreverse (proclist);
6079 }
6080
6081 return proclist;
6082}
6083
6084static int
6085enable_privilege (LPCTSTR priv_name, BOOL enable_p, TOKEN_PRIVILEGES *old_priv)
6086{
6087 TOKEN_PRIVILEGES priv;
6088 DWORD priv_size = sizeof (priv);
6089 DWORD opriv_size = sizeof (*old_priv);
6090 HANDLE h_token = NULL;
6091 HANDLE h_thread = GetCurrentThread ();
6092 int ret_val = 0;
6093 BOOL res;
6094
6095 res = open_thread_token (h_thread,
6096 TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
6097 FALSE, &h_token);
6098 if (!res && GetLastError () == ERROR_NO_TOKEN)
6099 {
6100 if (impersonate_self (SecurityImpersonation))
6101 res = open_thread_token (h_thread,
6102 TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
6103 FALSE, &h_token);
6104 }
6105 if (res)
6106 {
6107 priv.PrivilegeCount = 1;
6108 priv.Privileges[0].Attributes = enable_p ? SE_PRIVILEGE_ENABLED : 0;
6109 LookupPrivilegeValue (NULL, priv_name, &priv.Privileges[0].Luid);
6110 if (AdjustTokenPrivileges (h_token, FALSE, &priv, priv_size,
6111 old_priv, &opriv_size)
6112 && GetLastError () != ERROR_NOT_ALL_ASSIGNED)
6113 ret_val = 1;
6114 }
6115 if (h_token)
6116 CloseHandle (h_token);
6117
6118 return ret_val;
6119}
6120
6121static int
6122restore_privilege (TOKEN_PRIVILEGES *priv)
6123{
6124 DWORD priv_size = sizeof (*priv);
6125 HANDLE h_token = NULL;
6126 int ret_val = 0;
6127
6128 if (open_thread_token (GetCurrentThread (),
6129 TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
6130 FALSE, &h_token))
6131 {
6132 if (AdjustTokenPrivileges (h_token, FALSE, priv, priv_size, NULL, NULL)
6133 && GetLastError () != ERROR_NOT_ALL_ASSIGNED)
6134 ret_val = 1;
6135 }
6136 if (h_token)
6137 CloseHandle (h_token);
6138
6139 return ret_val;
6140}
6141
ca300656 6142static Lisp_Object
d35af63c 6143ltime (ULONGLONG time_100ns)
7c80d5ec 6144{
d35af63c
PE
6145 ULONGLONG time_sec = time_100ns / 10000000;
6146 int subsec = time_100ns % 10000000;
3de717bd
DA
6147 return list4i (time_sec >> 16, time_sec & 0xffff,
6148 subsec / 10, subsec % 10 * 100000);
7c80d5ec
EZ
6149}
6150
d35af63c 6151#define U64_TO_LISP_TIME(time) ltime (time)
5da9424d 6152
7c80d5ec 6153static int
b56ceb92
JB
6154process_times (HANDLE h_proc, Lisp_Object *ctime, Lisp_Object *etime,
6155 Lisp_Object *stime, Lisp_Object *utime, Lisp_Object *ttime,
6156 double *pcpu)
7c80d5ec
EZ
6157{
6158 FILETIME ft_creation, ft_exit, ft_kernel, ft_user, ft_current;
5da9424d 6159 ULONGLONG tem1, tem2, tem3, tem;
7c80d5ec
EZ
6160
6161 if (!h_proc
6162 || !get_process_times_fn
ed3751c8
JB
6163 || !(*get_process_times_fn) (h_proc, &ft_creation, &ft_exit,
6164 &ft_kernel, &ft_user))
7c80d5ec
EZ
6165 return 0;
6166
6167 GetSystemTimeAsFileTime (&ft_current);
6168
5da9424d 6169 FILETIME_TO_U64 (tem1, ft_kernel);
5da9424d
JB
6170 *stime = U64_TO_LISP_TIME (tem1);
6171
6172 FILETIME_TO_U64 (tem2, ft_user);
5da9424d
JB
6173 *utime = U64_TO_LISP_TIME (tem2);
6174
6175 tem3 = tem1 + tem2;
6176 *ttime = U64_TO_LISP_TIME (tem3);
6177
6178 FILETIME_TO_U64 (tem, ft_creation);
3af03101
EZ
6179 /* Process no 4 (System) returns zero creation time. */
6180 if (tem)
d35af63c 6181 tem -= utc_base;
5da9424d
JB
6182 *ctime = U64_TO_LISP_TIME (tem);
6183
3af03101 6184 if (tem)
5da9424d
JB
6185 {
6186 FILETIME_TO_U64 (tem3, ft_current);
d35af63c 6187 tem = (tem3 - utc_base) - tem;
5da9424d
JB
6188 }
6189 *etime = U64_TO_LISP_TIME (tem);
7c80d5ec 6190
3af03101
EZ
6191 if (tem)
6192 {
6193 *pcpu = 100.0 * (tem1 + tem2) / tem;
6194 if (*pcpu > 100)
6195 *pcpu = 100.0;
6196 }
6197 else
6198 *pcpu = 0;
6199
6200 return 1;
7c80d5ec
EZ
6201}
6202
6203Lisp_Object
b56ceb92 6204system_process_attributes (Lisp_Object pid)
7c80d5ec
EZ
6205{
6206 struct gcpro gcpro1, gcpro2, gcpro3;
6207 Lisp_Object attrs = Qnil;
6208 Lisp_Object cmd_str, decoded_cmd, tem;
6209 HANDLE h_snapshot, h_proc;
6210 DWORD proc_id;
754a2d13 6211 int found_proc = 0;
7c80d5ec 6212 char uname[UNLEN+1], gname[GNLEN+1], domain[1025];
32cef06e 6213 DWORD ulength = sizeof (uname), dlength = sizeof (domain), needed;
7c80d5ec
EZ
6214 DWORD glength = sizeof (gname);
6215 HANDLE token = NULL;
6216 SID_NAME_USE user_type;
32cef06e
EZ
6217 unsigned char *buf = NULL;
6218 DWORD blen = 0;
7c80d5ec
EZ
6219 TOKEN_USER user_token;
6220 TOKEN_PRIMARY_GROUP group_token;
22749e9a
EZ
6221 unsigned euid;
6222 unsigned egid;
7c80d5ec
EZ
6223 PROCESS_MEMORY_COUNTERS mem;
6224 PROCESS_MEMORY_COUNTERS_EX mem_ex;
cb576b5c 6225 SIZE_T minrss, maxrss;
7c80d5ec 6226 MEMORYSTATUS memst;
b8526f6e 6227 MEMORY_STATUS_EX memstex;
7c80d5ec 6228 double totphys = 0.0;
031da700 6229 Lisp_Object ctime, stime, utime, etime, ttime;
7c80d5ec 6230 double pcpu;
32cef06e 6231 BOOL result = FALSE;
7c80d5ec
EZ
6232
6233 CHECK_NUMBER_OR_FLOAT (pid);
6234 proc_id = FLOATP (pid) ? XFLOAT_DATA (pid) : XINT (pid);
6235
6236 h_snapshot = create_toolhelp32_snapshot (TH32CS_SNAPPROCESS, 0);
6237
6238 GCPRO3 (attrs, decoded_cmd, tem);
6239
6240 if (h_snapshot != INVALID_HANDLE_VALUE)
6241 {
6242 PROCESSENTRY32 pe;
6243 BOOL res;
6244
6245 pe.dwSize = sizeof (PROCESSENTRY32);
6246 for (res = process32_first (h_snapshot, &pe); res;
6247 res = process32_next (h_snapshot, &pe))
6248 {
6249 if (proc_id == pe.th32ProcessID)
6250 {
6251 if (proc_id == 0)
6252 decoded_cmd = build_string ("Idle");
6253 else
6254 {
6255 /* Decode the command name from locale-specific
6256 encoding. */
309f24d1
DA
6257 cmd_str = build_unibyte_string (pe.szExeFile);
6258
7c80d5ec
EZ
6259 decoded_cmd =
6260 code_convert_string_norecord (cmd_str,
6261 Vlocale_coding_system, 0);
6262 }
6263 attrs = Fcons (Fcons (Qcomm, decoded_cmd), attrs);
6264 attrs = Fcons (Fcons (Qppid,
6265 make_fixnum_or_float (pe.th32ParentProcessID)),
6266 attrs);
6267 attrs = Fcons (Fcons (Qpri, make_number (pe.pcPriClassBase)),
6268 attrs);
6269 attrs = Fcons (Fcons (Qthcount,
6270 make_fixnum_or_float (pe.cntThreads)),
6271 attrs);
754a2d13 6272 found_proc = 1;
7c80d5ec
EZ
6273 break;
6274 }
6275 }
6276
6277 CloseHandle (h_snapshot);
6278 }
6279
754a2d13
EZ
6280 if (!found_proc)
6281 {
6282 UNGCPRO;
6283 return Qnil;
6284 }
6285
7c80d5ec
EZ
6286 h_proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
6287 FALSE, proc_id);
6288 /* If we were denied a handle to the process, try again after
6289 enabling the SeDebugPrivilege in our process. */
6290 if (!h_proc)
6291 {
6292 TOKEN_PRIVILEGES priv_current;
6293
6294 if (enable_privilege (SE_DEBUG_NAME, TRUE, &priv_current))
6295 {
6296 h_proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
6297 FALSE, proc_id);
6298 restore_privilege (&priv_current);
6299 revert_to_self ();
6300 }
6301 }
32cef06e 6302 if (h_proc)
7c80d5ec 6303 {
32cef06e
EZ
6304 result = open_process_token (h_proc, TOKEN_QUERY, &token);
6305 if (result)
f8b35b24 6306 {
32cef06e
EZ
6307 result = get_token_information (token, TokenUser, NULL, 0, &blen);
6308 if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER)
6309 {
6310 buf = xmalloc (blen);
6311 result = get_token_information (token, TokenUser,
6312 (LPVOID)buf, blen, &needed);
6313 if (result)
6314 {
6315 memcpy (&user_token, buf, sizeof (user_token));
6316 if (!w32_cached_id (user_token.User.Sid, &euid, uname))
6317 {
6318 euid = get_rid (user_token.User.Sid);
6319 result = lookup_account_sid (NULL, user_token.User.Sid,
6320 uname, &ulength,
6321 domain, &dlength,
6322 &user_type);
6323 if (result)
6324 w32_add_to_cache (user_token.User.Sid, euid, uname);
6325 else
6326 {
6327 strcpy (uname, "unknown");
6328 result = TRUE;
6329 }
6330 }
6331 ulength = strlen (uname);
6332 }
6333 }
7c80d5ec 6334 }
32cef06e 6335 if (result)
7c80d5ec 6336 {
32cef06e
EZ
6337 /* Determine a reasonable euid and gid values. */
6338 if (xstrcasecmp ("administrator", uname) == 0)
7c80d5ec 6339 {
32cef06e
EZ
6340 euid = 500; /* well-known Administrator uid */
6341 egid = 513; /* well-known None gid */
6342 }
6343 else
6344 {
6345 /* Get group id and name. */
6346 result = get_token_information (token, TokenPrimaryGroup,
6347 (LPVOID)buf, blen, &needed);
6348 if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER)
f8b35b24 6349 {
32cef06e
EZ
6350 buf = xrealloc (buf, blen = needed);
6351 result = get_token_information (token, TokenPrimaryGroup,
6352 (LPVOID)buf, blen, &needed);
6353 }
6354 if (result)
6355 {
6356 memcpy (&group_token, buf, sizeof (group_token));
6357 if (!w32_cached_id (group_token.PrimaryGroup, &egid, gname))
6358 {
6359 egid = get_rid (group_token.PrimaryGroup);
6360 dlength = sizeof (domain);
6361 result =
6362 lookup_account_sid (NULL, group_token.PrimaryGroup,
6363 gname, &glength, NULL, &dlength,
6364 &user_type);
6365 if (result)
6366 w32_add_to_cache (group_token.PrimaryGroup,
6367 egid, gname);
6368 else
6369 {
6370 strcpy (gname, "None");
6371 result = TRUE;
6372 }
6373 }
6374 glength = strlen (gname);
f8b35b24 6375 }
7c80d5ec 6376 }
7c80d5ec 6377 }
5f445726 6378 xfree (buf);
7c80d5ec 6379 }
32cef06e 6380 if (!result)
7c80d5ec 6381 {
32cef06e
EZ
6382 if (!is_windows_9x ())
6383 {
6384 /* We couldn't open the process token, presumably because of
6385 insufficient access rights. Assume this process is run
6386 by the system. */
6387 strcpy (uname, "SYSTEM");
6388 strcpy (gname, "None");
6389 euid = 18; /* SYSTEM */
6390 egid = 513; /* None */
6391 glength = strlen (gname);
6392 ulength = strlen (uname);
6393 }
6394 /* If we are running under Windows 9X, where security calls are
6395 not supported, we assume all processes are run by the current
6396 user. */
6397 else if (GetUserName (uname, &ulength))
6398 {
6399 if (xstrcasecmp ("administrator", uname) == 0)
6400 euid = 0;
6401 else
6402 euid = 123;
6403 egid = euid;
6404 strcpy (gname, "None");
6405 glength = strlen (gname);
6406 ulength = strlen (uname);
6407 }
7c80d5ec 6408 else
32cef06e
EZ
6409 {
6410 euid = 123;
6411 egid = 123;
6412 strcpy (uname, "administrator");
6413 ulength = strlen (uname);
6414 strcpy (gname, "None");
6415 glength = strlen (gname);
6416 }
6417 if (token)
6418 CloseHandle (token);
7c80d5ec 6419 }
7c80d5ec
EZ
6420
6421 attrs = Fcons (Fcons (Qeuid, make_fixnum_or_float (euid)), attrs);
6422 tem = make_unibyte_string (uname, ulength);
6423 attrs = Fcons (Fcons (Quser,
6424 code_convert_string_norecord (tem, Vlocale_coding_system, 0)),
6425 attrs);
6426 attrs = Fcons (Fcons (Qegid, make_fixnum_or_float (egid)), attrs);
6427 tem = make_unibyte_string (gname, glength);
6428 attrs = Fcons (Fcons (Qgroup,
6429 code_convert_string_norecord (tem, Vlocale_coding_system, 0)),
6430 attrs);
6431
6432 if (global_memory_status_ex (&memstex))
235661f6 6433#if __GNUC__ || (defined (_MSC_VER) && _MSC_VER >= 1300)
7c80d5ec 6434 totphys = memstex.ullTotalPhys / 1024.0;
235661f6
EZ
6435#else
6436 /* Visual Studio 6 cannot convert an unsigned __int64 type to
6437 double, so we need to do this for it... */
6438 {
6439 DWORD tot_hi = memstex.ullTotalPhys >> 32;
6440 DWORD tot_md = (memstex.ullTotalPhys & 0x00000000ffffffff) >> 10;
6441 DWORD tot_lo = memstex.ullTotalPhys % 1024;
6442
6443 totphys = tot_hi * 4194304.0 + tot_md + tot_lo / 1024.0;
6444 }
6445#endif /* __GNUC__ || _MSC_VER >= 1300 */
7c80d5ec
EZ
6446 else if (global_memory_status (&memst))
6447 totphys = memst.dwTotalPhys / 1024.0;
6448
6449 if (h_proc
6450 && get_process_memory_info (h_proc, (PROCESS_MEMORY_COUNTERS *)&mem_ex,
6451 sizeof (mem_ex)))
6452 {
cb576b5c 6453 SIZE_T rss = mem_ex.WorkingSetSize / 1024;
7c80d5ec
EZ
6454
6455 attrs = Fcons (Fcons (Qmajflt,
6456 make_fixnum_or_float (mem_ex.PageFaultCount)),
6457 attrs);
6458 attrs = Fcons (Fcons (Qvsize,
6459 make_fixnum_or_float (mem_ex.PrivateUsage / 1024)),
6460 attrs);
6461 attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (rss)), attrs);
6462 if (totphys)
6463 attrs = Fcons (Fcons (Qpmem, make_float (100. * rss / totphys)), attrs);
6464 }
6465 else if (h_proc
6466 && get_process_memory_info (h_proc, &mem, sizeof (mem)))
6467 {
cb576b5c 6468 SIZE_T rss = mem_ex.WorkingSetSize / 1024;
7c80d5ec
EZ
6469
6470 attrs = Fcons (Fcons (Qmajflt,
6471 make_fixnum_or_float (mem.PageFaultCount)),
6472 attrs);
6473 attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (rss)), attrs);
6474 if (totphys)
6475 attrs = Fcons (Fcons (Qpmem, make_float (100. * rss / totphys)), attrs);
6476 }
6477 else if (h_proc
6478 && get_process_working_set_size (h_proc, &minrss, &maxrss))
6479 {
6480 DWORD rss = maxrss / 1024;
6481
6482 attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (maxrss / 1024)), attrs);
6483 if (totphys)
6484 attrs = Fcons (Fcons (Qpmem, make_float (100. * rss / totphys)), attrs);
6485 }
6486
031da700 6487 if (process_times (h_proc, &ctime, &etime, &stime, &utime, &ttime, &pcpu))
7c80d5ec
EZ
6488 {
6489 attrs = Fcons (Fcons (Qutime, utime), attrs);
6490 attrs = Fcons (Fcons (Qstime, stime), attrs);
031da700 6491 attrs = Fcons (Fcons (Qtime, ttime), attrs);
7c80d5ec
EZ
6492 attrs = Fcons (Fcons (Qstart, ctime), attrs);
6493 attrs = Fcons (Fcons (Qetime, etime), attrs);
6494 attrs = Fcons (Fcons (Qpcpu, make_float (pcpu)), attrs);
6495 }
6496
6497 /* FIXME: Retrieve command line by walking the PEB of the process. */
6498
6499 if (h_proc)
6500 CloseHandle (h_proc);
6501 UNGCPRO;
6502 return attrs;
6503}
6504
6505\f
480b0c5b
GV
6506/* Wrappers for winsock functions to map between our file descriptors
6507 and winsock's handles; also set h_errno for convenience.
6508
6509 To allow Emacs to run on systems which don't have winsock support
6510 installed, we dynamically link to winsock on startup if present, and
6511 otherwise provide the minimum necessary functionality
6512 (eg. gethostname). */
6513
6514/* function pointers for relevant socket functions */
6515int (PASCAL *pfn_WSAStartup) (WORD wVersionRequired, LPWSADATA lpWSAData);
6516void (PASCAL *pfn_WSASetLastError) (int iError);
6517int (PASCAL *pfn_WSAGetLastError) (void);
26fb7bc4 6518int (PASCAL *pfn_WSAEventSelect) (SOCKET s, HANDLE hEventObject, long lNetworkEvents);
64570b36
KS
6519HANDLE (PASCAL *pfn_WSACreateEvent) (void);
6520int (PASCAL *pfn_WSACloseEvent) (HANDLE hEvent);
480b0c5b
GV
6521int (PASCAL *pfn_socket) (int af, int type, int protocol);
6522int (PASCAL *pfn_bind) (SOCKET s, const struct sockaddr *addr, int namelen);
6523int (PASCAL *pfn_connect) (SOCKET s, const struct sockaddr *addr, int namelen);
6524int (PASCAL *pfn_ioctlsocket) (SOCKET s, long cmd, u_long *argp);
6525int (PASCAL *pfn_recv) (SOCKET s, char * buf, int len, int flags);
6526int (PASCAL *pfn_send) (SOCKET s, const char * buf, int len, int flags);
6527int (PASCAL *pfn_closesocket) (SOCKET s);
6528int (PASCAL *pfn_shutdown) (SOCKET s, int how);
6529int (PASCAL *pfn_WSACleanup) (void);
6530
6531u_short (PASCAL *pfn_htons) (u_short hostshort);
6532u_short (PASCAL *pfn_ntohs) (u_short netshort);
6533unsigned long (PASCAL *pfn_inet_addr) (const char * cp);
6534int (PASCAL *pfn_gethostname) (char * name, int namelen);
6535struct hostent * (PASCAL *pfn_gethostbyname) (const char * name);
6536struct servent * (PASCAL *pfn_getservbyname) (const char * name, const char * proto);
ecd270eb 6537int (PASCAL *pfn_getpeername) (SOCKET s, struct sockaddr *addr, int * namelen);
962955c5
JR
6538int (PASCAL *pfn_setsockopt) (SOCKET s, int level, int optname,
6539 const char * optval, int optlen);
6540int (PASCAL *pfn_listen) (SOCKET s, int backlog);
6541int (PASCAL *pfn_getsockname) (SOCKET s, struct sockaddr * name,
6542 int * namelen);
6543SOCKET (PASCAL *pfn_accept) (SOCKET s, struct sockaddr * addr, int * addrlen);
6544int (PASCAL *pfn_recvfrom) (SOCKET s, char * buf, int len, int flags,
6545 struct sockaddr * from, int * fromlen);
6546int (PASCAL *pfn_sendto) (SOCKET s, const char * buf, int len, int flags,
6547 const struct sockaddr * to, int tolen);
6548
f1614061
RS
6549/* SetHandleInformation is only needed to make sockets non-inheritable. */
6550BOOL (WINAPI *pfn_SetHandleInformation) (HANDLE object, DWORD mask, DWORD flags);
6551#ifndef HANDLE_FLAG_INHERIT
6552#define HANDLE_FLAG_INHERIT 1
6553#endif
480b0c5b 6554
f249a012
RS
6555HANDLE winsock_lib;
6556static int winsock_inuse;
480b0c5b 6557
f249a012 6558BOOL
480b0c5b
GV
6559term_winsock (void)
6560{
f249a012 6561 if (winsock_lib != NULL && winsock_inuse == 0)
480b0c5b 6562 {
3f940c5a 6563 release_listen_threads ();
f249a012
RS
6564 /* Not sure what would cause WSAENETDOWN, or even if it can happen
6565 after WSAStartup returns successfully, but it seems reasonable
6566 to allow unloading winsock anyway in that case. */
6567 if (pfn_WSACleanup () == 0 ||
6568 pfn_WSAGetLastError () == WSAENETDOWN)
6569 {
6570 if (FreeLibrary (winsock_lib))
6571 winsock_lib = NULL;
6572 return TRUE;
6573 }
480b0c5b 6574 }
f249a012 6575 return FALSE;
480b0c5b
GV
6576}
6577
f249a012
RS
6578BOOL
6579init_winsock (int load_now)
480b0c5b
GV
6580{
6581 WSADATA winsockData;
6582
f249a012
RS
6583 if (winsock_lib != NULL)
6584 return TRUE;
f1614061 6585
f1614061
RS
6586 pfn_SetHandleInformation
6587 = (void *) GetProcAddress (GetModuleHandle ("kernel32.dll"),
6588 "SetHandleInformation");
6589
64570b36 6590 winsock_lib = LoadLibrary ("Ws2_32.dll");
480b0c5b
GV
6591
6592 if (winsock_lib != NULL)
6593 {
6594 /* dynamically link to socket functions */
6595
6596#define LOAD_PROC(fn) \
6597 if ((pfn_##fn = (void *) GetProcAddress (winsock_lib, #fn)) == NULL) \
6598 goto fail;
6599
ed3751c8
JB
6600 LOAD_PROC (WSAStartup);
6601 LOAD_PROC (WSASetLastError);
6602 LOAD_PROC (WSAGetLastError);
6603 LOAD_PROC (WSAEventSelect);
6604 LOAD_PROC (WSACreateEvent);
6605 LOAD_PROC (WSACloseEvent);
6606 LOAD_PROC (socket);
6607 LOAD_PROC (bind);
6608 LOAD_PROC (connect);
6609 LOAD_PROC (ioctlsocket);
6610 LOAD_PROC (recv);
6611 LOAD_PROC (send);
6612 LOAD_PROC (closesocket);
6613 LOAD_PROC (shutdown);
6614 LOAD_PROC (htons);
6615 LOAD_PROC (ntohs);
6616 LOAD_PROC (inet_addr);
6617 LOAD_PROC (gethostname);
6618 LOAD_PROC (gethostbyname);
6619 LOAD_PROC (getservbyname);
6620 LOAD_PROC (getpeername);
6621 LOAD_PROC (WSACleanup);
6622 LOAD_PROC (setsockopt);
6623 LOAD_PROC (listen);
6624 LOAD_PROC (getsockname);
6625 LOAD_PROC (accept);
6626 LOAD_PROC (recvfrom);
6627 LOAD_PROC (sendto);
f249a012
RS
6628#undef LOAD_PROC
6629
480b0c5b
GV
6630 /* specify version 1.1 of winsock */
6631 if (pfn_WSAStartup (0x101, &winsockData) == 0)
6632 {
f249a012
RS
6633 if (winsockData.wVersion != 0x101)
6634 goto fail;
6635
6636 if (!load_now)
6637 {
6638 /* Report that winsock exists and is usable, but leave
6639 socket functions disabled. I am assuming that calling
6640 WSAStartup does not require any network interaction,
6641 and in particular does not cause or require a dial-up
6642 connection to be established. */
6643
6644 pfn_WSACleanup ();
6645 FreeLibrary (winsock_lib);
6646 winsock_lib = NULL;
6647 }
6648 winsock_inuse = 0;
6649 return TRUE;
480b0c5b
GV
6650 }
6651
6652 fail:
6653 FreeLibrary (winsock_lib);
f249a012 6654 winsock_lib = NULL;
480b0c5b 6655 }
f249a012
RS
6656
6657 return FALSE;
480b0c5b
GV
6658}
6659
6660
6661int h_errno = 0;
6662
f277993b
EZ
6663/* Function to map winsock error codes to errno codes for those errno
6664 code defined in errno.h (errno values not defined by errno.h are
6665 already in nt/inc/sys/socket.h). */
9bfb11f9 6666static void
b56ceb92 6667set_errno (void)
480b0c5b 6668{
f277993b
EZ
6669 int wsa_err;
6670
6671 h_errno = 0;
f249a012 6672 if (winsock_lib == NULL)
f277993b 6673 wsa_err = EINVAL;
480b0c5b 6674 else
f277993b 6675 wsa_err = pfn_WSAGetLastError ();
480b0c5b 6676
f277993b 6677 switch (wsa_err)
480b0c5b 6678 {
f277993b
EZ
6679 case WSAEACCES: errno = EACCES; break;
6680 case WSAEBADF: errno = EBADF; break;
6681 case WSAEFAULT: errno = EFAULT; break;
6682 case WSAEINTR: errno = EINTR; break;
6683 case WSAEINVAL: errno = EINVAL; break;
6684 case WSAEMFILE: errno = EMFILE; break;
6685 case WSAENAMETOOLONG: errno = ENAMETOOLONG; break;
6686 case WSAENOTEMPTY: errno = ENOTEMPTY; break;
6687 default: errno = wsa_err; break;
480b0c5b 6688 }
480b0c5b
GV
6689}
6690
9bfb11f9 6691static void
b56ceb92 6692check_errno (void)
480b0c5b 6693{
f277993b
EZ
6694 h_errno = 0;
6695 if (winsock_lib != NULL)
480b0c5b
GV
6696 pfn_WSASetLastError (0);
6697}
6698
d8fcc1b9
AI
6699/* Extend strerror to handle the winsock-specific error codes. */
6700struct {
6701 int errnum;
6702 char * msg;
6703} _wsa_errlist[] = {
1db5b1ad
JB
6704 {WSAEINTR , "Interrupted function call"},
6705 {WSAEBADF , "Bad file descriptor"},
6706 {WSAEACCES , "Permission denied"},
6707 {WSAEFAULT , "Bad address"},
6708 {WSAEINVAL , "Invalid argument"},
6709 {WSAEMFILE , "Too many open files"},
6710
6711 {WSAEWOULDBLOCK , "Resource temporarily unavailable"},
6712 {WSAEINPROGRESS , "Operation now in progress"},
6713 {WSAEALREADY , "Operation already in progress"},
6714 {WSAENOTSOCK , "Socket operation on non-socket"},
6715 {WSAEDESTADDRREQ , "Destination address required"},
6716 {WSAEMSGSIZE , "Message too long"},
6717 {WSAEPROTOTYPE , "Protocol wrong type for socket"},
6718 {WSAENOPROTOOPT , "Bad protocol option"},
6719 {WSAEPROTONOSUPPORT , "Protocol not supported"},
6720 {WSAESOCKTNOSUPPORT , "Socket type not supported"},
6721 {WSAEOPNOTSUPP , "Operation not supported"},
6722 {WSAEPFNOSUPPORT , "Protocol family not supported"},
6723 {WSAEAFNOSUPPORT , "Address family not supported by protocol family"},
6724 {WSAEADDRINUSE , "Address already in use"},
6725 {WSAEADDRNOTAVAIL , "Cannot assign requested address"},
6726 {WSAENETDOWN , "Network is down"},
6727 {WSAENETUNREACH , "Network is unreachable"},
6728 {WSAENETRESET , "Network dropped connection on reset"},
6729 {WSAECONNABORTED , "Software caused connection abort"},
6730 {WSAECONNRESET , "Connection reset by peer"},
6731 {WSAENOBUFS , "No buffer space available"},
6732 {WSAEISCONN , "Socket is already connected"},
6733 {WSAENOTCONN , "Socket is not connected"},
6734 {WSAESHUTDOWN , "Cannot send after socket shutdown"},
6735 {WSAETOOMANYREFS , "Too many references"}, /* not sure */
6736 {WSAETIMEDOUT , "Connection timed out"},
6737 {WSAECONNREFUSED , "Connection refused"},
6738 {WSAELOOP , "Network loop"}, /* not sure */
6739 {WSAENAMETOOLONG , "Name is too long"},
6740 {WSAEHOSTDOWN , "Host is down"},
6741 {WSAEHOSTUNREACH , "No route to host"},
6742 {WSAENOTEMPTY , "Buffer not empty"}, /* not sure */
6743 {WSAEPROCLIM , "Too many processes"},
6744 {WSAEUSERS , "Too many users"}, /* not sure */
6745 {WSAEDQUOT , "Double quote in host name"}, /* really not sure */
6746 {WSAESTALE , "Data is stale"}, /* not sure */
6747 {WSAEREMOTE , "Remote error"}, /* not sure */
6748
6749 {WSASYSNOTREADY , "Network subsystem is unavailable"},
6750 {WSAVERNOTSUPPORTED , "WINSOCK.DLL version out of range"},
6751 {WSANOTINITIALISED , "Winsock not initialized successfully"},
6752 {WSAEDISCON , "Graceful shutdown in progress"},
d8fcc1b9 6753#ifdef WSAENOMORE
1db5b1ad
JB
6754 {WSAENOMORE , "No more operations allowed"}, /* not sure */
6755 {WSAECANCELLED , "Operation cancelled"}, /* not sure */
6756 {WSAEINVALIDPROCTABLE , "Invalid procedure table from service provider"},
6757 {WSAEINVALIDPROVIDER , "Invalid service provider version number"},
6758 {WSAEPROVIDERFAILEDINIT , "Unable to initialize a service provider"},
6759 {WSASYSCALLFAILURE , "System call failure"},
6760 {WSASERVICE_NOT_FOUND , "Service not found"}, /* not sure */
6761 {WSATYPE_NOT_FOUND , "Class type not found"},
6762 {WSA_E_NO_MORE , "No more resources available"}, /* really not sure */
6763 {WSA_E_CANCELLED , "Operation already cancelled"}, /* really not sure */
6764 {WSAEREFUSED , "Operation refused"}, /* not sure */
d8fcc1b9 6765#endif
177c0ea7 6766
1db5b1ad
JB
6767 {WSAHOST_NOT_FOUND , "Host not found"},
6768 {WSATRY_AGAIN , "Authoritative host not found during name lookup"},
6769 {WSANO_RECOVERY , "Non-recoverable error during name lookup"},
6770 {WSANO_DATA , "Valid name, no data record of requested type"},
d8fcc1b9 6771
1db5b1ad 6772 {-1, NULL}
d8fcc1b9
AI
6773};
6774
6775char *
ed3751c8 6776sys_strerror (int error_no)
d8fcc1b9
AI
6777{
6778 int i;
6779 static char unknown_msg[40];
6780
a302c7ae
AI
6781 if (error_no >= 0 && error_no < sys_nerr)
6782 return sys_errlist[error_no];
d8fcc1b9
AI
6783
6784 for (i = 0; _wsa_errlist[i].errnum >= 0; i++)
6785 if (_wsa_errlist[i].errnum == error_no)
6786 return _wsa_errlist[i].msg;
6787
ed3751c8 6788 sprintf (unknown_msg, "Unidentified error: %d", error_no);
d8fcc1b9
AI
6789 return unknown_msg;
6790}
6791
480b0c5b
GV
6792/* [andrewi 3-May-96] I've had conflicting results using both methods,
6793 but I believe the method of keeping the socket handle separate (and
6794 insuring it is not inheritable) is the correct one. */
6795
480b0c5b 6796#define SOCK_HANDLE(fd) ((SOCKET) fd_info[fd].hnd)
480b0c5b 6797
bedf4aab 6798static int socket_to_fd (SOCKET s);
962955c5 6799
480b0c5b 6800int
ed3751c8 6801sys_socket (int af, int type, int protocol)
480b0c5b 6802{
962955c5 6803 SOCKET s;
480b0c5b 6804
f249a012 6805 if (winsock_lib == NULL)
480b0c5b 6806 {
f277993b 6807 errno = ENETDOWN;
480b0c5b
GV
6808 return INVALID_SOCKET;
6809 }
6810
6811 check_errno ();
6812
6813 /* call the real socket function */
962955c5 6814 s = pfn_socket (af, type, protocol);
177c0ea7 6815
480b0c5b 6816 if (s != INVALID_SOCKET)
f277993b 6817 return socket_to_fd (s);
480b0c5b 6818
962955c5
JR
6819 set_errno ();
6820 return -1;
6821}
6822
6823/* Convert a SOCKET to a file descriptor. */
bedf4aab 6824static int
962955c5
JR
6825socket_to_fd (SOCKET s)
6826{
6827 int fd;
6828 child_process * cp;
6829
6830 /* Although under NT 3.5 _open_osfhandle will accept a socket
6831 handle, if opened with SO_OPENTYPE == SO_SYNCHRONOUS_NONALERT,
6832 that does not work under NT 3.1. However, we can get the same
6833 effect by using a backdoor function to replace an existing
6834 descriptor handle with the one we want. */
6835
6836 /* allocate a file descriptor (with appropriate flags) */
6837 fd = _open ("NUL:", _O_RDWR);
6838 if (fd >= 0)
6839 {
962955c5
JR
6840 /* Make a non-inheritable copy of the socket handle. Note
6841 that it is possible that sockets aren't actually kernel
6842 handles, which appears to be the case on Windows 9x when
6843 the MS Proxy winsock client is installed. */
6844 {
6845 /* Apparently there is a bug in NT 3.51 with some service
6846 packs, which prevents using DuplicateHandle to make a
6847 socket handle non-inheritable (causes WSACleanup to
6848 hang). The work-around is to use SetHandleInformation
6849 instead if it is available and implemented. */
6850 if (pfn_SetHandleInformation)
480b0c5b 6851 {
962955c5
JR
6852 pfn_SetHandleInformation ((HANDLE) s, HANDLE_FLAG_INHERIT, 0);
6853 }
6854 else
6855 {
6856 HANDLE parent = GetCurrentProcess ();
6857 HANDLE new_s = INVALID_HANDLE_VALUE;
6858
6859 if (DuplicateHandle (parent,
6860 (HANDLE) s,
6861 parent,
6862 &new_s,
6863 0,
6864 FALSE,
6865 DUPLICATE_SAME_ACCESS))
f1614061 6866 {
962955c5
JR
6867 /* It is possible that DuplicateHandle succeeds even
6868 though the socket wasn't really a kernel handle,
6869 because a real handle has the same value. So
6870 test whether the new handle really is a socket. */
6871 long nonblocking = 0;
6872 if (pfn_ioctlsocket ((SOCKET) new_s, FIONBIO, &nonblocking) == 0)
ca149beb 6873 {
962955c5
JR
6874 pfn_closesocket (s);
6875 s = (SOCKET) new_s;
6876 }
6877 else
6878 {
6879 CloseHandle (new_s);
6880 }
177c0ea7 6881 }
480b0c5b 6882 }
962955c5 6883 }
bcf7fe2a 6884 eassert (fd < MAXDESC);
962955c5 6885 fd_info[fd].hnd = (HANDLE) s;
480b0c5b 6886
962955c5
JR
6887 /* set our own internal flags */
6888 fd_info[fd].flags = FILE_SOCKET | FILE_BINARY | FILE_READ | FILE_WRITE;
480b0c5b 6889
962955c5
JR
6890 cp = new_child ();
6891 if (cp)
6892 {
6893 cp->fd = fd;
6894 cp->status = STATUS_READ_ACKNOWLEDGED;
480b0c5b 6895
962955c5
JR
6896 /* attach child_process to fd_info */
6897 if (fd_info[ fd ].cp != NULL)
6898 {
6899 DebPrint (("sys_socket: fd_info[%d] apparently in use!\n", fd));
1088b922 6900 emacs_abort ();
480b0c5b
GV
6901 }
6902
962955c5
JR
6903 fd_info[ fd ].cp = cp;
6904
6905 /* success! */
6906 winsock_inuse++; /* count open sockets */
6907 return fd;
480b0c5b 6908 }
480b0c5b 6909
962955c5
JR
6910 /* clean up */
6911 _close (fd);
6912 }
f277993b 6913 else
962955c5 6914 pfn_closesocket (s);
f277993b 6915 errno = EMFILE;
480b0c5b
GV
6916 return -1;
6917}
6918
480b0c5b
GV
6919int
6920sys_bind (int s, const struct sockaddr * addr, int namelen)
6921{
f249a012 6922 if (winsock_lib == NULL)
480b0c5b 6923 {
f277993b 6924 errno = ENOTSOCK;
480b0c5b
GV
6925 return SOCKET_ERROR;
6926 }
6927
6928 check_errno ();
6929 if (fd_info[s].flags & FILE_SOCKET)
6930 {
6931 int rc = pfn_bind (SOCK_HANDLE (s), addr, namelen);
6932 if (rc == SOCKET_ERROR)
6933 set_errno ();
6934 return rc;
6935 }
f277993b 6936 errno = ENOTSOCK;
480b0c5b
GV
6937 return SOCKET_ERROR;
6938}
6939
480b0c5b
GV
6940int
6941sys_connect (int s, const struct sockaddr * name, int namelen)
6942{
f249a012 6943 if (winsock_lib == NULL)
480b0c5b 6944 {
f277993b 6945 errno = ENOTSOCK;
480b0c5b
GV
6946 return SOCKET_ERROR;
6947 }
6948
6949 check_errno ();
6950 if (fd_info[s].flags & FILE_SOCKET)
6951 {
6952 int rc = pfn_connect (SOCK_HANDLE (s), name, namelen);
6953 if (rc == SOCKET_ERROR)
6954 set_errno ();
6955 return rc;
6956 }
f277993b 6957 errno = ENOTSOCK;
480b0c5b
GV
6958 return SOCKET_ERROR;
6959}
6960
6961u_short
6962sys_htons (u_short hostshort)
6963{
f249a012 6964 return (winsock_lib != NULL) ?
480b0c5b
GV
6965 pfn_htons (hostshort) : hostshort;
6966}
6967
6968u_short
6969sys_ntohs (u_short netshort)
6970{
f249a012 6971 return (winsock_lib != NULL) ?
480b0c5b
GV
6972 pfn_ntohs (netshort) : netshort;
6973}
6974
6975unsigned long
6976sys_inet_addr (const char * cp)
6977{
f249a012 6978 return (winsock_lib != NULL) ?
480b0c5b
GV
6979 pfn_inet_addr (cp) : INADDR_NONE;
6980}
6981
6982int
6983sys_gethostname (char * name, int namelen)
6984{
f249a012 6985 if (winsock_lib != NULL)
f277993b
EZ
6986 {
6987 int retval;
6988
6989 check_errno ();
6990 retval = pfn_gethostname (name, namelen);
6991 if (retval == SOCKET_ERROR)
6992 set_errno ();
6993 return retval;
6994 }
480b0c5b
GV
6995
6996 if (namelen > MAX_COMPUTERNAME_LENGTH)
a302c7ae 6997 return !GetComputerName (name, (DWORD *)&namelen);
480b0c5b 6998
f277993b 6999 errno = EFAULT;
480b0c5b
GV
7000 return SOCKET_ERROR;
7001}
7002
7003struct hostent *
ed3751c8 7004sys_gethostbyname (const char * name)
480b0c5b
GV
7005{
7006 struct hostent * host;
f277993b 7007 int h_err = h_errno;
480b0c5b 7008
f249a012 7009 if (winsock_lib == NULL)
480b0c5b 7010 {
f277993b
EZ
7011 h_errno = NO_RECOVERY;
7012 errno = ENETDOWN;
480b0c5b
GV
7013 return NULL;
7014 }
7015
7016 check_errno ();
7017 host = pfn_gethostbyname (name);
7018 if (!host)
f277993b
EZ
7019 {
7020 set_errno ();
7021 h_errno = errno;
7022 }
7023 else
7024 h_errno = h_err;
480b0c5b
GV
7025 return host;
7026}
7027
7028struct servent *
ed3751c8 7029sys_getservbyname (const char * name, const char * proto)
480b0c5b
GV
7030{
7031 struct servent * serv;
7032
f249a012 7033 if (winsock_lib == NULL)
480b0c5b 7034 {
f277993b 7035 errno = ENETDOWN;
480b0c5b
GV
7036 return NULL;
7037 }
7038
7039 check_errno ();
7040 serv = pfn_getservbyname (name, proto);
7041 if (!serv)
7042 set_errno ();
7043 return serv;
7044}
7045
ecd270eb
JR
7046int
7047sys_getpeername (int s, struct sockaddr *addr, int * namelen)
7048{
7049 if (winsock_lib == NULL)
7050 {
f277993b 7051 errno = ENETDOWN;
ecd270eb
JR
7052 return SOCKET_ERROR;
7053 }
7054
7055 check_errno ();
7056 if (fd_info[s].flags & FILE_SOCKET)
7057 {
7058 int rc = pfn_getpeername (SOCK_HANDLE (s), addr, namelen);
7059 if (rc == SOCKET_ERROR)
7060 set_errno ();
7061 return rc;
7062 }
f277993b 7063 errno = ENOTSOCK;
ecd270eb
JR
7064 return SOCKET_ERROR;
7065}
7066
380961a6
GV
7067int
7068sys_shutdown (int s, int how)
7069{
380961a6
GV
7070 if (winsock_lib == NULL)
7071 {
f277993b 7072 errno = ENETDOWN;
380961a6
GV
7073 return SOCKET_ERROR;
7074 }
7075
7076 check_errno ();
7077 if (fd_info[s].flags & FILE_SOCKET)
7078 {
7079 int rc = pfn_shutdown (SOCK_HANDLE (s), how);
962955c5
JR
7080 if (rc == SOCKET_ERROR)
7081 set_errno ();
7082 return rc;
7083 }
f277993b 7084 errno = ENOTSOCK;
962955c5
JR
7085 return SOCKET_ERROR;
7086}
7087
7088int
a5a389bb 7089sys_setsockopt (int s, int level, int optname, const void * optval, int optlen)
962955c5
JR
7090{
7091 if (winsock_lib == NULL)
7092 {
f277993b 7093 errno = ENETDOWN;
962955c5
JR
7094 return SOCKET_ERROR;
7095 }
7096
7097 check_errno ();
7098 if (fd_info[s].flags & FILE_SOCKET)
7099 {
7100 int rc = pfn_setsockopt (SOCK_HANDLE (s), level, optname,
a5a389bb 7101 (const char *)optval, optlen);
962955c5
JR
7102 if (rc == SOCKET_ERROR)
7103 set_errno ();
7104 return rc;
7105 }
f277993b 7106 errno = ENOTSOCK;
177c0ea7 7107 return SOCKET_ERROR;
962955c5
JR
7108}
7109
7110int
7111sys_listen (int s, int backlog)
7112{
7113 if (winsock_lib == NULL)
7114 {
f277993b 7115 errno = ENETDOWN;
962955c5
JR
7116 return SOCKET_ERROR;
7117 }
7118
7119 check_errno ();
7120 if (fd_info[s].flags & FILE_SOCKET)
7121 {
7122 int rc = pfn_listen (SOCK_HANDLE (s), backlog);
7123 if (rc == SOCKET_ERROR)
7124 set_errno ();
26fb7bc4 7125 else
64570b36 7126 fd_info[s].flags |= FILE_LISTEN;
962955c5
JR
7127 return rc;
7128 }
f277993b 7129 errno = ENOTSOCK;
177c0ea7 7130 return SOCKET_ERROR;
962955c5
JR
7131}
7132
7133int
7134sys_getsockname (int s, struct sockaddr * name, int * namelen)
7135{
7136 if (winsock_lib == NULL)
7137 {
f277993b 7138 errno = ENETDOWN;
962955c5
JR
7139 return SOCKET_ERROR;
7140 }
7141
7142 check_errno ();
7143 if (fd_info[s].flags & FILE_SOCKET)
7144 {
7145 int rc = pfn_getsockname (SOCK_HANDLE (s), name, namelen);
7146 if (rc == SOCKET_ERROR)
7147 set_errno ();
7148 return rc;
7149 }
f277993b 7150 errno = ENOTSOCK;
177c0ea7 7151 return SOCKET_ERROR;
962955c5
JR
7152}
7153
7154int
7155sys_accept (int s, struct sockaddr * addr, int * addrlen)
7156{
7157 if (winsock_lib == NULL)
7158 {
f277993b 7159 errno = ENETDOWN;
962955c5
JR
7160 return -1;
7161 }
7162
7163 check_errno ();
26fb7bc4 7164 if (fd_info[s].flags & FILE_LISTEN)
962955c5 7165 {
a0ad1860 7166 SOCKET t = pfn_accept (SOCK_HANDLE (s), addr, addrlen);
64570b36
KS
7167 int fd = -1;
7168 if (t == INVALID_SOCKET)
7169 set_errno ();
7170 else
f277993b 7171 fd = socket_to_fd (t);
962955c5 7172
bcf7fe2a
EZ
7173 if (fd >= 0)
7174 {
7175 fd_info[s].cp->status = STATUS_READ_ACKNOWLEDGED;
7176 ResetEvent (fd_info[s].cp->char_avail);
7177 }
64570b36 7178 return fd;
962955c5 7179 }
f277993b 7180 errno = ENOTSOCK;
962955c5
JR
7181 return -1;
7182}
7183
7184int
7185sys_recvfrom (int s, char * buf, int len, int flags,
b56ceb92 7186 struct sockaddr * from, int * fromlen)
962955c5
JR
7187{
7188 if (winsock_lib == NULL)
7189 {
f277993b 7190 errno = ENETDOWN;
962955c5
JR
7191 return SOCKET_ERROR;
7192 }
7193
7194 check_errno ();
7195 if (fd_info[s].flags & FILE_SOCKET)
7196 {
7197 int rc = pfn_recvfrom (SOCK_HANDLE (s), buf, len, flags, from, fromlen);
7198 if (rc == SOCKET_ERROR)
7199 set_errno ();
7200 return rc;
7201 }
f277993b 7202 errno = ENOTSOCK;
962955c5
JR
7203 return SOCKET_ERROR;
7204}
7205
7206int
7207sys_sendto (int s, const char * buf, int len, int flags,
7208 const struct sockaddr * to, int tolen)
7209{
7210 if (winsock_lib == NULL)
7211 {
f277993b 7212 errno = ENETDOWN;
962955c5
JR
7213 return SOCKET_ERROR;
7214 }
7215
7216 check_errno ();
7217 if (fd_info[s].flags & FILE_SOCKET)
7218 {
7219 int rc = pfn_sendto (SOCK_HANDLE (s), buf, len, flags, to, tolen);
380961a6
GV
7220 if (rc == SOCKET_ERROR)
7221 set_errno ();
7222 return rc;
7223 }
f277993b 7224 errno = ENOTSOCK;
380961a6
GV
7225 return SOCKET_ERROR;
7226}
7227
ecd270eb 7228/* Windows does not have an fcntl function. Provide an implementation
067428c1 7229 good enough for Emacs. */
ecd270eb
JR
7230int
7231fcntl (int s, int cmd, int options)
7232{
067428c1
PE
7233 /* In the w32 Emacs port, fcntl (fd, F_DUPFD_CLOEXEC, fd1) is always
7234 invoked in a context where fd1 is closed and all descriptors less
7235 than fd1 are open, so sys_dup is an adequate implementation. */
7236 if (cmd == F_DUPFD_CLOEXEC)
7237 return sys_dup (s);
7238
ecd270eb
JR
7239 if (winsock_lib == NULL)
7240 {
f277993b 7241 errno = ENETDOWN;
ecd270eb
JR
7242 return -1;
7243 }
7244
7245 check_errno ();
7246 if (fd_info[s].flags & FILE_SOCKET)
7247 {
49cdacda 7248 if (cmd == F_SETFL && options == O_NONBLOCK)
ecd270eb
JR
7249 {
7250 unsigned long nblock = 1;
7251 int rc = pfn_ioctlsocket (SOCK_HANDLE (s), FIONBIO, &nblock);
7252 if (rc == SOCKET_ERROR)
9d4f32e8 7253 set_errno ();
ecd270eb
JR
7254 /* Keep track of the fact that we set this to non-blocking. */
7255 fd_info[s].flags |= FILE_NDELAY;
7256 return rc;
7257 }
7258 else
7259 {
f277993b 7260 errno = EINVAL;
ecd270eb
JR
7261 return SOCKET_ERROR;
7262 }
7263 }
f277993b 7264 errno = ENOTSOCK;
ecd270eb
JR
7265 return SOCKET_ERROR;
7266}
7267
480b0c5b
GV
7268
7269/* Shadow main io functions: we need to handle pipes and sockets more
7270 intelligently, and implement non-blocking mode as well. */
7271
7272int
7273sys_close (int fd)
7274{
7275 int rc;
7276
7559f399 7277 if (fd < 0)
480b0c5b
GV
7278 {
7279 errno = EBADF;
7280 return -1;
7281 }
7282
7559f399 7283 if (fd < MAXDESC && fd_info[fd].cp)
480b0c5b
GV
7284 {
7285 child_process * cp = fd_info[fd].cp;
7286
7287 fd_info[fd].cp = NULL;
7288
7289 if (CHILD_ACTIVE (cp))
7290 {
7291 /* if last descriptor to active child_process then cleanup */
7292 int i;
7293 for (i = 0; i < MAXDESC; i++)
7294 {
7295 if (i == fd)
7296 continue;
7297 if (fd_info[i].cp == cp)
7298 break;
7299 }
7300 if (i == MAXDESC)
7301 {
480b0c5b
GV
7302 if (fd_info[fd].flags & FILE_SOCKET)
7303 {
1088b922 7304 if (winsock_lib == NULL) emacs_abort ();
480b0c5b
GV
7305
7306 pfn_shutdown (SOCK_HANDLE (fd), 2);
7307 rc = pfn_closesocket (SOCK_HANDLE (fd));
7d701334 7308
f249a012 7309 winsock_inuse--; /* count open sockets */
480b0c5b 7310 }
299614f3
EZ
7311 /* If the process handle is NULL, it's either a socket
7312 or serial connection, or a subprocess that was
7313 already reaped by reap_subprocess, but whose
7314 resources were not yet freed, because its output was
7315 not fully read yet by the time it was reaped. (This
7316 usually happens with async subprocesses whose output
7317 is being read by Emacs.) Otherwise, this process was
7318 not reaped yet, so we set its FD to a negative value
7319 to make sure sys_select will eventually get to
7320 calling the SIGCHLD handler for it, which will then
7321 invoke waitpid and reap_subprocess. */
7322 if (cp->procinfo.hProcess == NULL)
7323 delete_child (cp);
7324 else
7325 cp->fd = -1;
480b0c5b
GV
7326 }
7327 }
7328 }
7329
224f4ec1
EZ
7330 if (fd >= 0 && fd < MAXDESC)
7331 fd_info[fd].flags = 0;
7332
480b0c5b 7333 /* Note that sockets do not need special treatment here (at least on
e9e23e23 7334 NT and Windows 95 using the standard tcp/ip stacks) - it appears that
480b0c5b
GV
7335 closesocket is equivalent to CloseHandle, which is to be expected
7336 because socket handles are fully fledged kernel handles. */
7337 rc = _close (fd);
7338
480b0c5b
GV
7339 return rc;
7340}
7341
7342int
7343sys_dup (int fd)
7344{
7345 int new_fd;
7346
7347 new_fd = _dup (fd);
7559f399 7348 if (new_fd >= 0 && new_fd < MAXDESC)
480b0c5b
GV
7349 {
7350 /* duplicate our internal info as well */
7351 fd_info[new_fd] = fd_info[fd];
7352 }
7353 return new_fd;
7354}
7355
480b0c5b
GV
7356int
7357sys_dup2 (int src, int dst)
7358{
7359 int rc;
7360
7361 if (dst < 0 || dst >= MAXDESC)
7362 {
7363 errno = EBADF;
7364 return -1;
7365 }
7366
7367 /* make sure we close the destination first if it's a pipe or socket */
7368 if (src != dst && fd_info[dst].flags != 0)
7369 sys_close (dst);
177c0ea7 7370
480b0c5b
GV
7371 rc = _dup2 (src, dst);
7372 if (rc == 0)
7373 {
7374 /* duplicate our internal info as well */
7375 fd_info[dst] = fd_info[src];
7376 }
7377 return rc;
7378}
7379
480b0c5b 7380int
067428c1 7381pipe2 (int * phandles, int pipe2_flags)
480b0c5b
GV
7382{
7383 int rc;
7384 unsigned flags;
480b0c5b 7385
067428c1
PE
7386 eassert (pipe2_flags == O_CLOEXEC);
7387
76b3903d
GV
7388 /* make pipe handles non-inheritable; when we spawn a child, we
7389 replace the relevant handle with an inheritable one. Also put
7390 pipes into binary mode; we will do text mode translation ourselves
7391 if required. */
7392 rc = _pipe (phandles, 0, _O_NOINHERIT | _O_BINARY);
480b0c5b
GV
7393
7394 if (rc == 0)
7395 {
cb72110d
JR
7396 /* Protect against overflow, since Windows can open more handles than
7397 our fd_info array has room for. */
7398 if (phandles[0] >= MAXDESC || phandles[1] >= MAXDESC)
7399 {
7400 _close (phandles[0]);
7401 _close (phandles[1]);
6e432f0c 7402 errno = EMFILE;
cb72110d
JR
7403 rc = -1;
7404 }
7405 else
7406 {
7407 flags = FILE_PIPE | FILE_READ | FILE_BINARY;
7408 fd_info[phandles[0]].flags = flags;
480b0c5b 7409
cb72110d
JR
7410 flags = FILE_PIPE | FILE_WRITE | FILE_BINARY;
7411 fd_info[phandles[1]].flags = flags;
7412 }
480b0c5b
GV
7413 }
7414
7415 return rc;
7416}
7417
7418/* Function to do blocking read of one byte, needed to implement
d3d14b40
EZ
7419 select. It is only allowed on communication ports, sockets, or
7420 pipes. */
480b0c5b
GV
7421int
7422_sys_read_ahead (int fd)
7423{
7424 child_process * cp;
7425 int rc;
7426
7427 if (fd < 0 || fd >= MAXDESC)
7428 return STATUS_READ_ERROR;
7429
7430 cp = fd_info[fd].cp;
7431
7432 if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY)
7433 return STATUS_READ_ERROR;
7434
d888760c 7435 if ((fd_info[fd].flags & (FILE_PIPE | FILE_SERIAL | FILE_SOCKET)) == 0
480b0c5b
GV
7436 || (fd_info[fd].flags & FILE_READ) == 0)
7437 {
d888760c 7438 DebPrint (("_sys_read_ahead: internal error: fd %d is not a pipe, serial port, or socket!\n", fd));
1088b922 7439 emacs_abort ();
480b0c5b 7440 }
177c0ea7 7441
480b0c5b 7442 cp->status = STATUS_READ_IN_PROGRESS;
177c0ea7 7443
480b0c5b 7444 if (fd_info[fd].flags & FILE_PIPE)
f7554349 7445 {
f7554349
KH
7446 rc = _read (fd, &cp->chr, sizeof (char));
7447
7448 /* Give subprocess time to buffer some more output for us before
e9e23e23 7449 reporting that input is available; we need this because Windows 95
f7554349
KH
7450 connects DOS programs to pipes by making the pipe appear to be
7451 the normal console stdout - as a result most DOS programs will
d888760c 7452 write to stdout without buffering, ie. one character at a
fbd6baed 7453 time. Even some W32 programs do this - "dir" in a command
f7554349
KH
7454 shell on NT is very slow if we don't do this. */
7455 if (rc > 0)
7456 {
78806724 7457 int wait = w32_pipe_read_delay;
f7554349
KH
7458
7459 if (wait > 0)
7460 Sleep (wait);
7461 else if (wait < 0)
7462 while (++wait <= 0)
7463 /* Yield remainder of our time slice, effectively giving a
7464 temporary priority boost to the child process. */
7465 Sleep (0);
7466 }
7467 }
d888760c
GM
7468 else if (fd_info[fd].flags & FILE_SERIAL)
7469 {
7470 HANDLE hnd = fd_info[fd].hnd;
7471 OVERLAPPED *ovl = &fd_info[fd].cp->ovl_read;
7472 COMMTIMEOUTS ct;
7473
7474 /* Configure timeouts for blocking read. */
7475 if (!GetCommTimeouts (hnd, &ct))
6e432f0c
EZ
7476 {
7477 cp->status = STATUS_READ_ERROR;
7478 return STATUS_READ_ERROR;
7479 }
d888760c
GM
7480 ct.ReadIntervalTimeout = 0;
7481 ct.ReadTotalTimeoutMultiplier = 0;
7482 ct.ReadTotalTimeoutConstant = 0;
7483 if (!SetCommTimeouts (hnd, &ct))
6e432f0c
EZ
7484 {
7485 cp->status = STATUS_READ_ERROR;
7486 return STATUS_READ_ERROR;
7487 }
d888760c
GM
7488
7489 if (!ReadFile (hnd, &cp->chr, sizeof (char), (DWORD*) &rc, ovl))
7490 {
7491 if (GetLastError () != ERROR_IO_PENDING)
6e432f0c
EZ
7492 {
7493 cp->status = STATUS_READ_ERROR;
7494 return STATUS_READ_ERROR;
7495 }
d888760c 7496 if (!GetOverlappedResult (hnd, ovl, (DWORD*) &rc, TRUE))
6e432f0c
EZ
7497 {
7498 cp->status = STATUS_READ_ERROR;
7499 return STATUS_READ_ERROR;
7500 }
d888760c
GM
7501 }
7502 }
480b0c5b 7503 else if (fd_info[fd].flags & FILE_SOCKET)
ecd270eb
JR
7504 {
7505 unsigned long nblock = 0;
7506 /* We always want this to block, so temporarily disable NDELAY. */
7507 if (fd_info[fd].flags & FILE_NDELAY)
7508 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
7509
7510 rc = pfn_recv (SOCK_HANDLE (fd), &cp->chr, sizeof (char), 0);
7511
7512 if (fd_info[fd].flags & FILE_NDELAY)
7513 {
7514 nblock = 1;
7515 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
7516 }
7517 }
177c0ea7 7518
480b0c5b
GV
7519 if (rc == sizeof (char))
7520 cp->status = STATUS_READ_SUCCEEDED;
7521 else
7522 cp->status = STATUS_READ_FAILED;
7523
7524 return cp->status;
7525}
7526
9bfb11f9
KS
7527int
7528_sys_wait_accept (int fd)
64570b36
KS
7529{
7530 HANDLE hEv;
7531 child_process * cp;
7532 int rc;
7533
7534 if (fd < 0 || fd >= MAXDESC)
7535 return STATUS_READ_ERROR;
7536
7537 cp = fd_info[fd].cp;
7538
7539 if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY)
7540 return STATUS_READ_ERROR;
7541
7542 cp->status = STATUS_READ_FAILED;
7543
7544 hEv = pfn_WSACreateEvent ();
7545 rc = pfn_WSAEventSelect (SOCK_HANDLE (fd), hEv, FD_ACCEPT);
7546 if (rc != SOCKET_ERROR)
7547 {
3f940c5a
EZ
7548 do {
7549 rc = WaitForSingleObject (hEv, 500);
7550 Sleep (5);
7551 } while (rc == WAIT_TIMEOUT
7552 && cp->status != STATUS_READ_ERROR
7553 && cp->char_avail);
64570b36 7554 pfn_WSAEventSelect (SOCK_HANDLE (fd), NULL, 0);
64570b36
KS
7555 if (rc == WAIT_OBJECT_0)
7556 cp->status = STATUS_READ_SUCCEEDED;
7557 }
7046f191 7558 pfn_WSACloseEvent (hEv);
64570b36
KS
7559
7560 return cp->status;
7561}
7562
480b0c5b
GV
7563int
7564sys_read (int fd, char * buffer, unsigned int count)
7565{
7566 int nchars;
480b0c5b
GV
7567 int to_read;
7568 DWORD waiting;
76b3903d 7569 char * orig_buffer = buffer;
480b0c5b 7570
7559f399 7571 if (fd < 0)
480b0c5b
GV
7572 {
7573 errno = EBADF;
7574 return -1;
7575 }
7576
d888760c 7577 if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET | FILE_SERIAL))
480b0c5b
GV
7578 {
7579 child_process *cp = fd_info[fd].cp;
7580
7581 if ((fd_info[fd].flags & FILE_READ) == 0)
7582 {
7583 errno = EBADF;
7584 return -1;
7585 }
7586
76b3903d
GV
7587 nchars = 0;
7588
7589 /* re-read CR carried over from last read */
7590 if (fd_info[fd].flags & FILE_LAST_CR)
7591 {
1088b922 7592 if (fd_info[fd].flags & FILE_BINARY) emacs_abort ();
76b3903d
GV
7593 *buffer++ = 0x0d;
7594 count--;
7595 nchars++;
f52eb3ef 7596 fd_info[fd].flags &= ~FILE_LAST_CR;
76b3903d
GV
7597 }
7598
480b0c5b
GV
7599 /* presence of a child_process structure means we are operating in
7600 non-blocking mode - otherwise we just call _read directly.
7601 Note that the child_process structure might be missing because
7602 reap_subprocess has been called; in this case the pipe is
7603 already broken, so calling _read on it is okay. */
7604 if (cp)
7605 {
7606 int current_status = cp->status;
7607
7608 switch (current_status)
7609 {
7610 case STATUS_READ_FAILED:
7611 case STATUS_READ_ERROR:
f52eb3ef
GV
7612 /* report normal EOF if nothing in buffer */
7613 if (nchars <= 0)
7614 fd_info[fd].flags |= FILE_AT_EOF;
7615 return nchars;
480b0c5b
GV
7616
7617 case STATUS_READ_READY:
7618 case STATUS_READ_IN_PROGRESS:
7619 DebPrint (("sys_read called when read is in progress\n"));
7620 errno = EWOULDBLOCK;
7621 return -1;
7622
7623 case STATUS_READ_SUCCEEDED:
7624 /* consume read-ahead char */
7625 *buffer++ = cp->chr;
7626 count--;
76b3903d 7627 nchars++;
480b0c5b
GV
7628 cp->status = STATUS_READ_ACKNOWLEDGED;
7629 ResetEvent (cp->char_avail);
7630
7631 case STATUS_READ_ACKNOWLEDGED:
7632 break;
7633
7634 default:
7635 DebPrint (("sys_read: bad status %d\n", current_status));
7636 errno = EBADF;
7637 return -1;
7638 }
7639
7640 if (fd_info[fd].flags & FILE_PIPE)
7641 {
7642 PeekNamedPipe ((HANDLE) _get_osfhandle (fd), NULL, 0, NULL, &waiting, NULL);
7643 to_read = min (waiting, (DWORD) count);
f52eb3ef
GV
7644
7645 if (to_read > 0)
7646 nchars += _read (fd, buffer, to_read);
480b0c5b 7647 }
d888760c
GM
7648 else if (fd_info[fd].flags & FILE_SERIAL)
7649 {
7650 HANDLE hnd = fd_info[fd].hnd;
7651 OVERLAPPED *ovl = &fd_info[fd].cp->ovl_read;
d888760c
GM
7652 int rc = 0;
7653 COMMTIMEOUTS ct;
7654
7655 if (count > 0)
7656 {
7657 /* Configure timeouts for non-blocking read. */
7658 if (!GetCommTimeouts (hnd, &ct))
7659 {
7660 errno = EIO;
7661 return -1;
7662 }
7663 ct.ReadIntervalTimeout = MAXDWORD;
7664 ct.ReadTotalTimeoutMultiplier = 0;
7665 ct.ReadTotalTimeoutConstant = 0;
7666 if (!SetCommTimeouts (hnd, &ct))
7667 {
7668 errno = EIO;
7669 return -1;
7670 }
7671
7672 if (!ResetEvent (ovl->hEvent))
7673 {
7674 errno = EIO;
7675 return -1;
7676 }
7677 if (!ReadFile (hnd, buffer, count, (DWORD*) &rc, ovl))
7678 {
7679 if (GetLastError () != ERROR_IO_PENDING)
7680 {
7681 errno = EIO;
7682 return -1;
7683 }
7684 if (!GetOverlappedResult (hnd, ovl, (DWORD*) &rc, TRUE))
7685 {
7686 errno = EIO;
7687 return -1;
7688 }
7689 }
7690 nchars += rc;
7691 }
7692 }
480b0c5b
GV
7693 else /* FILE_SOCKET */
7694 {
1088b922 7695 if (winsock_lib == NULL) emacs_abort ();
480b0c5b
GV
7696
7697 /* do the equivalent of a non-blocking read */
7698 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONREAD, &waiting);
76b3903d 7699 if (waiting == 0 && nchars == 0)
480b0c5b 7700 {
f277993b 7701 errno = EWOULDBLOCK;
480b0c5b
GV
7702 return -1;
7703 }
7704
480b0c5b
GV
7705 if (waiting)
7706 {
7707 /* always use binary mode for sockets */
76b3903d
GV
7708 int res = pfn_recv (SOCK_HANDLE (fd), buffer, count, 0);
7709 if (res == SOCKET_ERROR)
480b0c5b 7710 {
ed3751c8
JB
7711 DebPrint (("sys_read.recv failed with error %d on socket %ld\n",
7712 pfn_WSAGetLastError (), SOCK_HANDLE (fd)));
76b3903d
GV
7713 set_errno ();
7714 return -1;
480b0c5b 7715 }
76b3903d 7716 nchars += res;
480b0c5b
GV
7717 }
7718 }
480b0c5b
GV
7719 }
7720 else
f52eb3ef
GV
7721 {
7722 int nread = _read (fd, buffer, count);
7723 if (nread >= 0)
7724 nchars += nread;
7725 else if (nchars == 0)
7726 nchars = nread;
7727 }
76b3903d 7728
f52eb3ef
GV
7729 if (nchars <= 0)
7730 fd_info[fd].flags |= FILE_AT_EOF;
76b3903d 7731 /* Perform text mode translation if required. */
f52eb3ef 7732 else if ((fd_info[fd].flags & FILE_BINARY) == 0)
76b3903d
GV
7733 {
7734 nchars = crlf_to_lf (nchars, orig_buffer);
7735 /* If buffer contains only CR, return that. To be absolutely
7736 sure we should attempt to read the next char, but in
7737 practice a CR to be followed by LF would not appear by
7738 itself in the buffer. */
7739 if (nchars > 1 && orig_buffer[nchars - 1] == 0x0d)
7740 {
7741 fd_info[fd].flags |= FILE_LAST_CR;
7742 nchars--;
7743 }
76b3903d 7744 }
480b0c5b
GV
7745 }
7746 else
7747 nchars = _read (fd, buffer, count);
7748
76b3903d 7749 return nchars;
480b0c5b
GV
7750}
7751
d888760c
GM
7752/* From w32xfns.c */
7753extern HANDLE interrupt_handle;
7754
480b0c5b
GV
7755/* For now, don't bother with a non-blocking mode */
7756int
7757sys_write (int fd, const void * buffer, unsigned int count)
7758{
7759 int nchars;
7760
7559f399 7761 if (fd < 0)
480b0c5b
GV
7762 {
7763 errno = EBADF;
7764 return -1;
7765 }
7766
d888760c 7767 if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET | FILE_SERIAL))
76b3903d
GV
7768 {
7769 if ((fd_info[fd].flags & FILE_WRITE) == 0)
7770 {
7771 errno = EBADF;
7772 return -1;
7773 }
7774
7775 /* Perform text mode translation if required. */
7776 if ((fd_info[fd].flags & FILE_BINARY) == 0)
7777 {
7778 char * tmpbuf = alloca (count * 2);
7779 unsigned char * src = (void *)buffer;
7780 unsigned char * dst = tmpbuf;
7781 int nbytes = count;
7782
7783 while (1)
7784 {
7785 unsigned char *next;
7786 /* copy next line or remaining bytes */
7787 next = _memccpy (dst, src, '\n', nbytes);
7788 if (next)
7789 {
7790 /* copied one line ending with '\n' */
7791 int copied = next - dst;
7792 nbytes -= copied;
7793 src += copied;
7794 /* insert '\r' before '\n' */
7795 next[-1] = '\r';
7796 next[0] = '\n';
7797 dst = next + 1;
7798 count++;
177c0ea7 7799 }
76b3903d
GV
7800 else
7801 /* copied remaining partial line -> now finished */
7802 break;
7803 }
7804 buffer = tmpbuf;
7805 }
7806 }
7807
d888760c
GM
7808 if (fd < MAXDESC && fd_info[fd].flags & FILE_SERIAL)
7809 {
7810 HANDLE hnd = (HANDLE) _get_osfhandle (fd);
7811 OVERLAPPED *ovl = &fd_info[fd].cp->ovl_write;
7812 HANDLE wait_hnd[2] = { interrupt_handle, ovl->hEvent };
7813 DWORD active = 0;
7814
7815 if (!WriteFile (hnd, buffer, count, (DWORD*) &nchars, ovl))
7816 {
7817 if (GetLastError () != ERROR_IO_PENDING)
7818 {
7819 errno = EIO;
7820 return -1;
7821 }
7822 if (detect_input_pending ())
7823 active = MsgWaitForMultipleObjects (2, wait_hnd, FALSE, INFINITE,
7824 QS_ALLINPUT);
7825 else
7826 active = WaitForMultipleObjects (2, wait_hnd, FALSE, INFINITE);
7827 if (active == WAIT_OBJECT_0)
7828 { /* User pressed C-g, cancel write, then leave. Don't bother
7829 cleaning up as we may only get stuck in buggy drivers. */
7830 PurgeComm (hnd, PURGE_TXABORT | PURGE_TXCLEAR);
7831 CancelIo (hnd);
7832 errno = EIO;
7833 return -1;
7834 }
7835 if (active == WAIT_OBJECT_0 + 1
7836 && !GetOverlappedResult (hnd, ovl, (DWORD*) &nchars, TRUE))
7837 {
7838 errno = EIO;
7839 return -1;
7840 }
7841 }
7842 }
7d701334 7843 else if (fd < MAXDESC && fd_info[fd].flags & FILE_SOCKET)
480b0c5b 7844 {
30a32e0e 7845 unsigned long nblock = 0;
1088b922 7846 if (winsock_lib == NULL) emacs_abort ();
30a32e0e
JR
7847
7848 /* TODO: implement select() properly so non-blocking I/O works. */
7849 /* For now, make sure the write blocks. */
7850 if (fd_info[fd].flags & FILE_NDELAY)
7851 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
7852
480b0c5b 7853 nchars = pfn_send (SOCK_HANDLE (fd), buffer, count, 0);
30a32e0e
JR
7854
7855 /* Set the socket back to non-blocking if it was before,
7856 for other operations that support it. */
7857 if (fd_info[fd].flags & FILE_NDELAY)
7858 {
7859 nblock = 1;
7860 pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
7861 }
7862
480b0c5b
GV
7863 if (nchars == SOCKET_ERROR)
7864 {
ed3751c8
JB
7865 DebPrint (("sys_write.send failed with error %d on socket %ld\n",
7866 pfn_WSAGetLastError (), SOCK_HANDLE (fd)));
480b0c5b
GV
7867 set_errno ();
7868 }
7869 }
7870 else
6e83d800
EZ
7871 {
7872 /* Some networked filesystems don't like too large writes, so
7873 break them into smaller chunks. See the Comments section of
7874 the MSDN documentation of WriteFile for details behind the
7875 choice of the value of CHUNK below. See also the thread
7876 http://thread.gmane.org/gmane.comp.version-control.git/145294
7877 in the git mailing list. */
7878 const unsigned char *p = buffer;
7879 const unsigned chunk = 30 * 1024 * 1024;
7880
7881 nchars = 0;
7882 while (count > 0)
7883 {
7884 unsigned this_chunk = count < chunk ? count : chunk;
7885 int n = _write (fd, p, this_chunk);
7886
7887 nchars += n;
7888 if (n < 0)
7889 {
7890 nchars = n;
7891 break;
7892 }
7893 else if (n < this_chunk)
7894 break;
7895 count -= n;
7896 p += n;
7897 }
7898 }
480b0c5b
GV
7899
7900 return nchars;
7901}
7902
3dffe395
EZ
7903\f
7904/* Emulation of SIOCGIFCONF and getifaddrs, see process.c. */
7905
7906extern Lisp_Object conv_sockaddr_to_lisp (struct sockaddr *, int);
7907
7908/* Return information about network interface IFNAME, or about all
7909 interfaces (if IFNAME is nil). */
7910static Lisp_Object
7911network_interface_get_info (Lisp_Object ifname)
7912{
7913 ULONG ainfo_len = sizeof (IP_ADAPTER_INFO);
7914 IP_ADAPTER_INFO *adapter, *ainfo = xmalloc (ainfo_len);
7915 DWORD retval = get_adapters_info (ainfo, &ainfo_len);
7916 Lisp_Object res = Qnil;
7917
7918 if (retval == ERROR_BUFFER_OVERFLOW)
7919 {
7920 ainfo = xrealloc (ainfo, ainfo_len);
7921 retval = get_adapters_info (ainfo, &ainfo_len);
7922 }
7923
7924 if (retval == ERROR_SUCCESS)
7925 {
7926 int eth_count = 0, tr_count = 0, fddi_count = 0, ppp_count = 0;
7927 int sl_count = 0, wlan_count = 0, lo_count = 0, ifx_count = 0;
7928 int if_num;
7929 struct sockaddr_in sa;
7930
7931 /* For the below, we need some winsock functions, so make sure
7932 the winsock DLL is loaded. If we cannot successfully load
7933 it, they will have no use of the information we provide,
7934 anyway, so punt. */
7935 if (!winsock_lib && !init_winsock (1))
7936 goto done;
7937
7938 for (adapter = ainfo; adapter; adapter = adapter->Next)
7939 {
7940 char namebuf[MAX_ADAPTER_NAME_LENGTH + 4];
7941 u_long ip_addr;
7942 /* Present Unix-compatible interface names, instead of the
7943 Windows names, which are really GUIDs not readable by
7944 humans. */
7945 static const char *ifmt[] = {
7946 "eth%d", "tr%d", "fddi%d", "ppp%d", "sl%d", "wlan%d",
7947 "lo", "ifx%d"
7948 };
7949 enum {
7950 NONE = -1,
7951 ETHERNET = 0,
7952 TOKENRING = 1,
7953 FDDI = 2,
7954 PPP = 3,
7955 SLIP = 4,
7956 WLAN = 5,
7957 LOOPBACK = 6,
7958 OTHER_IF = 7
7959 } ifmt_idx;
7960
7961 switch (adapter->Type)
7962 {
7963 case MIB_IF_TYPE_ETHERNET:
7964 /* Windows before Vista reports wireless adapters as
7965 Ethernet. Work around by looking at the Description
7966 string. */
7967 if (strstr (adapter->Description, "Wireless "))
7968 {
7969 ifmt_idx = WLAN;
7970 if_num = wlan_count++;
7971 }
7972 else
7973 {
7974 ifmt_idx = ETHERNET;
7975 if_num = eth_count++;
7976 }
7977 break;
7978 case MIB_IF_TYPE_TOKENRING:
7979 ifmt_idx = TOKENRING;
7980 if_num = tr_count++;
7981 break;
7982 case MIB_IF_TYPE_FDDI:
7983 ifmt_idx = FDDI;
7984 if_num = fddi_count++;
7985 break;
7986 case MIB_IF_TYPE_PPP:
7987 ifmt_idx = PPP;
7988 if_num = ppp_count++;
7989 break;
7990 case MIB_IF_TYPE_SLIP:
7991 ifmt_idx = SLIP;
7992 if_num = sl_count++;
7993 break;
7994 case IF_TYPE_IEEE80211:
7995 ifmt_idx = WLAN;
7996 if_num = wlan_count++;
7997 break;
7998 case MIB_IF_TYPE_LOOPBACK:
7999 if (lo_count < 0)
8000 {
8001 ifmt_idx = LOOPBACK;
8002 if_num = lo_count++;
8003 }
8004 else
8005 ifmt_idx = NONE;
8006 break;
8007 default:
8008 ifmt_idx = OTHER_IF;
8009 if_num = ifx_count++;
8010 break;
8011 }
8012 if (ifmt_idx == NONE)
8013 continue;
8014 sprintf (namebuf, ifmt[ifmt_idx], if_num);
8015
8016 sa.sin_family = AF_INET;
8017 ip_addr = sys_inet_addr (adapter->IpAddressList.IpAddress.String);
8018 if (ip_addr == INADDR_NONE)
8019 {
8020 /* Bogus address, skip this interface. */
8021 continue;
8022 }
8023 sa.sin_addr.s_addr = ip_addr;
8024 sa.sin_port = 0;
8025 if (NILP (ifname))
8026 res = Fcons (Fcons (build_string (namebuf),
8027 conv_sockaddr_to_lisp ((struct sockaddr*) &sa,
8028 sizeof (struct sockaddr))),
8029 res);
8030 else if (strcmp (namebuf, SSDATA (ifname)) == 0)
8031 {
8032 Lisp_Object hwaddr = Fmake_vector (make_number (6), Qnil);
8033 register struct Lisp_Vector *p = XVECTOR (hwaddr);
8034 Lisp_Object flags = Qnil;
8035 int n;
8036 u_long net_mask;
8037
8038 /* Flags. We guess most of them by type, since the
8039 Windows flags are different and hard to get by. */
8040 flags = Fcons (intern ("up"), flags);
8041 if (ifmt_idx == ETHERNET || ifmt_idx == WLAN)
8042 {
8043 flags = Fcons (intern ("broadcast"), flags);
8044 flags = Fcons (intern ("multicast"), flags);
8045 }
8046 flags = Fcons (intern ("running"), flags);
8047 if (ifmt_idx == PPP)
8048 {
8049 flags = Fcons (intern ("pointopoint"), flags);
8050 flags = Fcons (intern ("noarp"), flags);
8051 }
8052 if (adapter->HaveWins)
8053 flags = Fcons (intern ("WINS"), flags);
8054 if (adapter->DhcpEnabled)
8055 flags = Fcons (intern ("dynamic"), flags);
8056
8057 res = Fcons (flags, res);
8058
8059 /* Hardware address and its family. */
8060 for (n = 0; n < adapter->AddressLength; n++)
91f2d272 8061 p->contents[n] = make_number ((int) adapter->Address[n]);
3dffe395
EZ
8062 /* Windows does not support AF_LINK or AF_PACKET family
8063 of addresses. Use an arbitrary family number that is
8064 identical to what GNU/Linux returns. */
8065 res = Fcons (Fcons (make_number (1), hwaddr), res);
8066
8067 /* Network mask. */
8068 sa.sin_family = AF_INET;
8069 net_mask = sys_inet_addr (adapter->IpAddressList.IpMask.String);
8070 if (net_mask != INADDR_NONE)
8071 {
8072 sa.sin_addr.s_addr = net_mask;
8073 sa.sin_port = 0;
8074 res = Fcons (conv_sockaddr_to_lisp ((struct sockaddr *) &sa,
8075 sizeof (struct sockaddr)),
8076 res);
8077 }
8078 else
8079 res = Fcons (Qnil, res);
8080
8081 sa.sin_family = AF_INET;
8082 if (ip_addr != INADDR_NONE)
8083 {
8084 /* Broadcast address is only reported by
8085 GetAdaptersAddresses, which is of limited
8086 availability. Generate it on our own. */
8087 u_long bcast_addr = (ip_addr & net_mask) | ~net_mask;
8088
8089 sa.sin_addr.s_addr = bcast_addr;
8090 sa.sin_port = 0;
8091 res = Fcons (conv_sockaddr_to_lisp ((struct sockaddr *) &sa,
8092 sizeof (struct sockaddr)),
8093 res);
8094
8095 /* IP address. */
8096 sa.sin_addr.s_addr = ip_addr;
8097 sa.sin_port = 0;
8098 res = Fcons (conv_sockaddr_to_lisp ((struct sockaddr *) &sa,
8099 sizeof (struct sockaddr)),
8100 res);
8101 }
8102 else
8103 res = Fcons (Qnil, Fcons (Qnil, res));
8104 }
8105 }
8106 /* GetAdaptersInfo is documented to not report loopback
8107 interfaces, so we generate one out of thin air. */
8108 if (!lo_count)
8109 {
8110 sa.sin_family = AF_INET;
8111 sa.sin_port = 0;
8112 if (NILP (ifname))
8113 {
8114 sa.sin_addr.s_addr = sys_inet_addr ("127.0.0.1");
8115 res = Fcons (Fcons (build_string ("lo"),
8116 conv_sockaddr_to_lisp ((struct sockaddr*) &sa,
8117 sizeof (struct sockaddr))),
8118 res);
8119 }
8120 else if (strcmp (SSDATA (ifname), "lo") == 0)
8121 {
8122 res = Fcons (Fcons (intern ("running"),
8123 Fcons (intern ("loopback"),
8124 Fcons (intern ("up"), Qnil))), Qnil);
8125 /* 772 is what 3 different GNU/Linux systems report for
8126 the loopback interface. */
8127 res = Fcons (Fcons (make_number (772),
8128 Fmake_vector (make_number (6),
8129 make_number (0))),
8130 res);
8131 sa.sin_addr.s_addr = sys_inet_addr ("255.0.0.0");
8132 res = Fcons (conv_sockaddr_to_lisp ((struct sockaddr *) &sa,
8133 sizeof (struct sockaddr)),
8134 res);
8135 sa.sin_addr.s_addr = sys_inet_addr ("0.0.0.0");
8136 res = Fcons (conv_sockaddr_to_lisp ((struct sockaddr *) &sa,
8137 sizeof (struct sockaddr)),
8138 res);
8139 sa.sin_addr.s_addr = sys_inet_addr ("127.0.0.1");
8140 res = Fcons (conv_sockaddr_to_lisp ((struct sockaddr *) &sa,
8141 sizeof (struct sockaddr)),
8142 res);
8143 }
8144
8145 }
8146 }
8147
8148 done:
8149 xfree (ainfo);
8150 return res;
8151}
8152
8153Lisp_Object
8154network_interface_list (void)
8155{
8156 return network_interface_get_info (Qnil);
8157}
8158
8159Lisp_Object
8160network_interface_info (Lisp_Object ifname)
8161{
8162 return network_interface_get_info (ifname);
8163}
8164
8165\f
97a93095
EZ
8166/* The Windows CRT functions are "optimized for speed", so they don't
8167 check for timezone and DST changes if they were last called less
8168 than 1 minute ago (see http://support.microsoft.com/kb/821231). So
8169 all Emacs features that repeatedly call time functions (e.g.,
8170 display-time) are in real danger of missing timezone and DST
8171 changes. Calling tzset before each localtime call fixes that. */
8172struct tm *
8173sys_localtime (const time_t *t)
8174{
8175 tzset ();
8176 return localtime (t);
8177}
8178
0898ca10
JB
8179
8180\f
d07ff9db
CY
8181/* Try loading LIBRARY_ID from the file(s) specified in
8182 Vdynamic_library_alist. If the library is loaded successfully,
8183 return the handle of the DLL, and record the filename in the
8184 property :loaded-from of LIBRARY_ID. If the library could not be
8185 found, or when it was already loaded (because the handle is not
8186 recorded anywhere, and so is lost after use), return NULL.
8187
8188 We could also save the handle in :loaded-from, but currently
8189 there's no use case for it. */
0898ca10 8190HMODULE
d07ff9db 8191w32_delayed_load (Lisp_Object library_id)
0898ca10 8192{
a868f601 8193 HMODULE dll_handle = NULL;
0898ca10
JB
8194
8195 CHECK_SYMBOL (library_id);
8196
d07ff9db
CY
8197 if (CONSP (Vdynamic_library_alist)
8198 && NILP (Fassq (library_id, Vlibrary_cache)))
0898ca10
JB
8199 {
8200 Lisp_Object found = Qnil;
d07ff9db 8201 Lisp_Object dlls = Fassq (library_id, Vdynamic_library_alist);
0898ca10
JB
8202
8203 if (CONSP (dlls))
8204 for (dlls = XCDR (dlls); CONSP (dlls); dlls = XCDR (dlls))
8205 {
a868f601
EZ
8206 Lisp_Object dll = XCAR (dlls);
8207 char name[MAX_UTF8_PATH];
8208 DWORD res = -1;
8209
8210 CHECK_STRING (dll);
8211 dll = ENCODE_FILE (dll);
8212 if (w32_unicode_filenames)
8213 {
8214 wchar_t name_w[MAX_PATH];
8215
8216 filename_to_utf16 (SSDATA (dll), name_w);
8217 dll_handle = LoadLibraryW (name_w);
8218 if (dll_handle)
8219 {
8220 res = GetModuleFileNameW (dll_handle, name_w,
8221 sizeof (name_w));
8222 if (res > 0)
8223 filename_from_utf16 (name_w, name);
8224 }
8225 }
8226 else
8227 {
8228 char name_a[MAX_PATH];
8229
8230 filename_to_ansi (SSDATA (dll), name_a);
8231 dll_handle = LoadLibraryA (name_a);
8232 if (dll_handle)
8233 {
8234 res = GetModuleFileNameA (dll_handle, name_a,
8235 sizeof (name_a));
8236 if (res > 0)
8237 filename_from_ansi (name_a, name);
8238 }
8239 }
8240 if (dll_handle)
8241 {
8242 ptrdiff_t len = strlen (name);
8243 found = Fcons (dll,
8244 (res > 0)
8245 /* Possibly truncated */
8246 ? make_specified_string (name, -1, len, 1)
8247 : Qnil);
8248 break;
8249 }
8250 }
0898ca10
JB
8251
8252 Fput (library_id, QCloaded_from, found);
8253 }
8254
a868f601 8255 return dll_handle;
0898ca10
JB
8256}
8257
8258\f
76151e2c 8259void
b56ceb92 8260check_windows_init_file (void)
f52eb3ef 8261{
f52eb3ef
GV
8262 /* A common indication that Emacs is not installed properly is when
8263 it cannot find the Windows installation file. If this file does
8264 not exist in the expected place, tell the user. */
8265
c7aa8333
EZ
8266 if (!noninteractive && !inhibit_window_system
8267 /* Vload_path is not yet initialized when we are loading
8268 loadup.el. */
8269 && NILP (Vpurify_flag))
d54abccd 8270 {
d54abccd
GV
8271 Lisp_Object init_file;
8272 int fd;
f52eb3ef 8273
a868f601
EZ
8274 /* Implementation note: this function runs early during Emacs
8275 startup, before startup.el is run. So Vload_path is still in
8276 its initial unibyte form, holding ANSI-encoded file names.
8277 That is why we never bother to ENCODE_FILE here, nor use wide
8278 APIs for file names: we will never get UTF-8 encoded file
8279 names here. */
d54abccd 8280 init_file = build_string ("term/w32-win");
76151e2c 8281 fd = openp (Vload_path, init_file, Fget_load_suffixes (), NULL, Qnil);
177c0ea7 8282 if (fd < 0)
d54abccd 8283 {
76151e2c 8284 Lisp_Object load_path_print = Fprin1_to_string (Vload_path, Qnil);
d5db4077
KR
8285 char *init_file_name = SDATA (init_file);
8286 char *load_path = SDATA (load_path_print);
acc23b87
KS
8287 char *buffer = alloca (1024
8288 + strlen (init_file_name)
8289 + strlen (load_path));
d54abccd 8290
177c0ea7 8291 sprintf (buffer,
d54abccd
GV
8292 "The Emacs Windows initialization file \"%s.el\" "
8293 "could not be found in your Emacs installation. "
8294 "Emacs checked the following directories for this file:\n"
8295 "\n%s\n\n"
8296 "When Emacs cannot find this file, it usually means that it "
8297 "was not installed properly, or its distribution file was "
8298 "not unpacked properly.\nSee the README.W32 file in the "
8299 "top-level Emacs directory for more information.",
8300 init_file_name, load_path);
8301 MessageBox (NULL,
8302 buffer,
8303 "Emacs Abort Dialog",
8304 MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
1088b922 8305 /* Use the low-level system abort. */
d54abccd
GV
8306 abort ();
8307 }
8308 else
8309 {
a302c7ae 8310 _close (fd);
d54abccd 8311 }
f52eb3ef 8312 }
f52eb3ef 8313}
480b0c5b
GV
8314
8315void
16b22fef 8316term_ntproc (int ignored)
480b0c5b 8317{
16b22fef 8318 (void)ignored;
c06c382a
EZ
8319
8320 term_timers ();
8321
480b0c5b
GV
8322 /* shutdown the socket interface if necessary */
8323 term_winsock ();
52c7f9ee
JR
8324
8325 term_w32select ();
480b0c5b
GV
8326}
8327
8328void
16b22fef 8329init_ntproc (int dumping)
480b0c5b 8330{
c06c382a
EZ
8331 sigset_t initial_mask = 0;
8332
e1dbe924 8333 /* Initialize the socket interface now if available and requested by
f249a012 8334 the user by defining PRELOAD_WINSOCK; otherwise loading will be
fbd6baed 8335 delayed until open-network-stream is called (w32-has-winsock can
f249a012
RS
8336 also be used to dynamically load or reload winsock).
8337
8338 Conveniently, init_environment is called before us, so
8339 PRELOAD_WINSOCK can be set in the registry. */
8340
8341 /* Always initialize this correctly. */
8342 winsock_lib = NULL;
8343
8344 if (getenv ("PRELOAD_WINSOCK") != NULL)
8345 init_winsock (TRUE);
480b0c5b
GV
8346
8347 /* Initial preparation for subprocess support: replace our standard
8348 handles with non-inheritable versions. */
8349 {
8350 HANDLE parent;
8351 HANDLE stdin_save = INVALID_HANDLE_VALUE;
8352 HANDLE stdout_save = INVALID_HANDLE_VALUE;
8353 HANDLE stderr_save = INVALID_HANDLE_VALUE;
8354
8355 parent = GetCurrentProcess ();
8356
8357 /* ignore errors when duplicating and closing; typically the
8358 handles will be invalid when running as a gui program. */
177c0ea7
JB
8359 DuplicateHandle (parent,
8360 GetStdHandle (STD_INPUT_HANDLE),
480b0c5b 8361 parent,
177c0ea7
JB
8362 &stdin_save,
8363 0,
8364 FALSE,
480b0c5b 8365 DUPLICATE_SAME_ACCESS);
177c0ea7 8366
480b0c5b
GV
8367 DuplicateHandle (parent,
8368 GetStdHandle (STD_OUTPUT_HANDLE),
8369 parent,
8370 &stdout_save,
8371 0,
8372 FALSE,
8373 DUPLICATE_SAME_ACCESS);
177c0ea7 8374
480b0c5b
GV
8375 DuplicateHandle (parent,
8376 GetStdHandle (STD_ERROR_HANDLE),
8377 parent,
8378 &stderr_save,
8379 0,
8380 FALSE,
8381 DUPLICATE_SAME_ACCESS);
177c0ea7 8382
480b0c5b
GV
8383 fclose (stdin);
8384 fclose (stdout);
8385 fclose (stderr);
8386
8387 if (stdin_save != INVALID_HANDLE_VALUE)
62aba0d4 8388 _open_osfhandle ((intptr_t) stdin_save, O_TEXT);
480b0c5b 8389 else
76b3903d
GV
8390 _open ("nul", O_TEXT | O_NOINHERIT | O_RDONLY);
8391 _fdopen (0, "r");
480b0c5b
GV
8392
8393 if (stdout_save != INVALID_HANDLE_VALUE)
62aba0d4 8394 _open_osfhandle ((intptr_t) stdout_save, O_TEXT);
480b0c5b 8395 else
76b3903d
GV
8396 _open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY);
8397 _fdopen (1, "w");
480b0c5b
GV
8398
8399 if (stderr_save != INVALID_HANDLE_VALUE)
62aba0d4 8400 _open_osfhandle ((intptr_t) stderr_save, O_TEXT);
480b0c5b 8401 else
76b3903d
GV
8402 _open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY);
8403 _fdopen (2, "w");
480b0c5b
GV
8404 }
8405
8406 /* unfortunately, atexit depends on implementation of malloc */
8407 /* atexit (term_ntproc); */
16b22fef 8408 if (!dumping)
c06c382a
EZ
8409 {
8410 /* Make sure we start with all signals unblocked. */
8411 sigprocmask (SIG_SETMASK, &initial_mask, NULL);
8412 signal (SIGABRT, term_ntproc);
8413 }
8414 init_timers ();
76b3903d
GV
8415
8416 /* determine which drives are fixed, for GetCachedVolumeInformation */
8417 {
8418 /* GetDriveType must have trailing backslash. */
8419 char drive[] = "A:\\";
8420
8421 /* Loop over all possible drive letters */
8422 while (*drive <= 'Z')
8423 {
8424 /* Record if this drive letter refers to a fixed drive. */
177c0ea7 8425 fixed_drives[DRIVE_INDEX (*drive)] =
76b3903d
GV
8426 (GetDriveType (drive) == DRIVE_FIXED);
8427
8428 (*drive)++;
8429 }
a302c7ae
AI
8430
8431 /* Reset the volume info cache. */
8432 volume_cache = NULL;
76b3903d 8433 }
480b0c5b
GV
8434}
8435
a8c3a596
JR
8436/*
8437 shutdown_handler ensures that buffers' autosave files are
8438 up to date when the user logs off, or the system shuts down.
8439*/
bedf4aab 8440static BOOL WINAPI
ed3751c8 8441shutdown_handler (DWORD type)
a8c3a596
JR
8442{
8443 /* Ctrl-C and Ctrl-Break are already suppressed, so don't handle them. */
8444 if (type == CTRL_CLOSE_EVENT /* User closes console window. */
8445 || type == CTRL_LOGOFF_EVENT /* User logs off. */
8446 || type == CTRL_SHUTDOWN_EVENT) /* User shutsdown. */
8447 {
8448 /* Shut down cleanly, making sure autosave files are up to date. */
1882aa38 8449 shut_down_emacs (0, Qnil);
a8c3a596
JR
8450 }
8451
7046f191 8452 /* Allow other handlers to handle this signal. */
a8c3a596
JR
8453 return FALSE;
8454}
8455
9785d95b
BK
8456/*
8457 globals_of_w32 is used to initialize those global variables that
8458 must always be initialized on startup even when the global variable
8459 initialized is non zero (see the function main in emacs.c).
8460*/
9bfb11f9 8461void
b56ceb92 8462globals_of_w32 (void)
9785d95b 8463{
74258518
JR
8464 HMODULE kernel32 = GetModuleHandle ("kernel32.dll");
8465
8466 get_process_times_fn = (GetProcessTimes_Proc)
8467 GetProcAddress (kernel32, "GetProcessTimes");
8468
cd3520a4 8469 DEFSYM (QCloaded_from, ":loaded-from");
0898ca10 8470
9785d95b
BK
8471 g_b_init_is_windows_9x = 0;
8472 g_b_init_open_process_token = 0;
8473 g_b_init_get_token_information = 0;
8474 g_b_init_lookup_account_sid = 0;
9d95a291
EZ
8475 g_b_init_get_sid_sub_authority = 0;
8476 g_b_init_get_sid_sub_authority_count = 0;
6dad7178 8477 g_b_init_get_security_info = 0;
df87c56c
EZ
8478 g_b_init_get_file_security_w = 0;
8479 g_b_init_get_file_security_a = 0;
8aaaec6b
EZ
8480 g_b_init_get_security_descriptor_owner = 0;
8481 g_b_init_get_security_descriptor_group = 0;
8482 g_b_init_is_valid_sid = 0;
7c80d5ec
EZ
8483 g_b_init_create_toolhelp32_snapshot = 0;
8484 g_b_init_process32_first = 0;
8485 g_b_init_process32_next = 0;
8486 g_b_init_open_thread_token = 0;
8487 g_b_init_impersonate_self = 0;
8488 g_b_init_revert_to_self = 0;
8489 g_b_init_get_process_memory_info = 0;
8490 g_b_init_get_process_working_set_size = 0;
8491 g_b_init_global_memory_status = 0;
8492 g_b_init_global_memory_status_ex = 0;
f8b35b24
EZ
8493 g_b_init_equal_sid = 0;
8494 g_b_init_copy_sid = 0;
8495 g_b_init_get_length_sid = 0;
ad9e2d54
EZ
8496 g_b_init_get_native_system_info = 0;
8497 g_b_init_get_system_times = 0;
eeccd06f
EZ
8498 g_b_init_create_symbolic_link_w = 0;
8499 g_b_init_create_symbolic_link_a = 0;
66447e07
EZ
8500 g_b_init_get_security_descriptor_dacl = 0;
8501 g_b_init_convert_sd_to_sddl = 0;
8502 g_b_init_convert_sddl_to_sd = 0;
8503 g_b_init_is_valid_security_descriptor = 0;
8504 g_b_init_set_file_security = 0;
3dffe395 8505 g_b_init_get_adapters_info = 0;
ad9e2d54 8506 num_of_processors = 0;
a8c3a596
JR
8507 /* The following sets a handler for shutdown notifications for
8508 console apps. This actually applies to Emacs in both console and
8509 GUI modes, since we had to fool windows into thinking emacs is a
8510 console application to get console mode to work. */
ed3751c8 8511 SetConsoleCtrlHandler (shutdown_handler, TRUE);
8aaaec6b
EZ
8512
8513 /* "None" is the default group name on standalone workstations. */
8514 strcpy (dflt_group_name, "None");
5c207910
EZ
8515
8516 /* Reset, in case it has some value inherited from dump time. */
8517 w32_stat_get_owner_group = 0;
03d58cca
EZ
8518
8519 /* If w32_unicode_filenames is non-zero, we will be using Unicode
8520 (a.k.a. "wide") APIs to invoke functions that accept file
8521 names. */
8522 if (is_windows_9x ())
8523 w32_unicode_filenames = 0;
8524 else
8525 w32_unicode_filenames = 1;
9785d95b
BK
8526}
8527
d888760c 8528/* For make-serial-process */
b56ceb92 8529int
4e604a5d 8530serial_open (Lisp_Object port_obj)
d888760c 8531{
4e604a5d 8532 char *port = SSDATA (port_obj);
d888760c
GM
8533 HANDLE hnd;
8534 child_process *cp;
8535 int fd = -1;
8536
8537 hnd = CreateFile (port, GENERIC_READ | GENERIC_WRITE, 0, 0,
8538 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
8539 if (hnd == INVALID_HANDLE_VALUE)
8540 error ("Could not open %s", port);
62aba0d4 8541 fd = (int) _open_osfhandle ((intptr_t) hnd, 0);
d888760c
GM
8542 if (fd == -1)
8543 error ("Could not open %s", port);
8544
8545 cp = new_child ();
8546 if (!cp)
8547 error ("Could not create child process");
8548 cp->fd = fd;
8549 cp->status = STATUS_READ_ACKNOWLEDGED;
8550 fd_info[ fd ].hnd = hnd;
8551 fd_info[ fd ].flags |=
8552 FILE_READ | FILE_WRITE | FILE_BINARY | FILE_SERIAL;
8553 if (fd_info[ fd ].cp != NULL)
8554 {
8555 error ("fd_info[fd = %d] is already in use", fd);
8556 }
8557 fd_info[ fd ].cp = cp;
8558 cp->ovl_read.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
8559 if (cp->ovl_read.hEvent == NULL)
8560 error ("Could not create read event");
8561 cp->ovl_write.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
8562 if (cp->ovl_write.hEvent == NULL)
8563 error ("Could not create write event");
8564
8565 return fd;
8566}
8567
8568/* For serial-process-configure */
8569void
9d4f32e8 8570serial_configure (struct Lisp_Process *p, Lisp_Object contact)
d888760c
GM
8571{
8572 Lisp_Object childp2 = Qnil;
8573 Lisp_Object tem = Qnil;
8574 HANDLE hnd;
8575 DCB dcb;
8576 COMMTIMEOUTS ct;
8577 char summary[4] = "???"; /* This usually becomes "8N1". */
8578
8579 if ((fd_info[ p->outfd ].flags & FILE_SERIAL) == 0)
8580 error ("Not a serial process");
8581 hnd = fd_info[ p->outfd ].hnd;
8582
4d2b044c 8583 childp2 = Fcopy_sequence (p->childp);
d888760c
GM
8584
8585 /* Initialize timeouts for blocking read and blocking write. */
8586 if (!GetCommTimeouts (hnd, &ct))
8587 error ("GetCommTimeouts() failed");
8588 ct.ReadIntervalTimeout = 0;
8589 ct.ReadTotalTimeoutMultiplier = 0;
8590 ct.ReadTotalTimeoutConstant = 0;
8591 ct.WriteTotalTimeoutMultiplier = 0;
8592 ct.WriteTotalTimeoutConstant = 0;
8593 if (!SetCommTimeouts (hnd, &ct))
8594 error ("SetCommTimeouts() failed");
8595 /* Read port attributes and prepare default configuration. */
8596 memset (&dcb, 0, sizeof (dcb));
8597 dcb.DCBlength = sizeof (DCB);
8598 if (!GetCommState (hnd, &dcb))
8599 error ("GetCommState() failed");
8600 dcb.fBinary = TRUE;
8601 dcb.fNull = FALSE;
8602 dcb.fAbortOnError = FALSE;
8603 /* dcb.XonLim and dcb.XoffLim are set by GetCommState() */
8604 dcb.ErrorChar = 0;
8605 dcb.EofChar = 0;
8606 dcb.EvtChar = 0;
8607
8608 /* Configure speed. */
8609 if (!NILP (Fplist_member (contact, QCspeed)))
8610 tem = Fplist_get (contact, QCspeed);
8611 else
4d2b044c 8612 tem = Fplist_get (p->childp, QCspeed);
d888760c
GM
8613 CHECK_NUMBER (tem);
8614 dcb.BaudRate = XINT (tem);
8615 childp2 = Fplist_put (childp2, QCspeed, tem);
8616
8617 /* Configure bytesize. */
8618 if (!NILP (Fplist_member (contact, QCbytesize)))
8619 tem = Fplist_get (contact, QCbytesize);
8620 else
4d2b044c 8621 tem = Fplist_get (p->childp, QCbytesize);
d888760c
GM
8622 if (NILP (tem))
8623 tem = make_number (8);
8624 CHECK_NUMBER (tem);
8625 if (XINT (tem) != 7 && XINT (tem) != 8)
8626 error (":bytesize must be nil (8), 7, or 8");
8627 dcb.ByteSize = XINT (tem);
8628 summary[0] = XINT (tem) + '0';
8629 childp2 = Fplist_put (childp2, QCbytesize, tem);
8630
8631 /* Configure parity. */
8632 if (!NILP (Fplist_member (contact, QCparity)))
8633 tem = Fplist_get (contact, QCparity);
8634 else
4d2b044c 8635 tem = Fplist_get (p->childp, QCparity);
d888760c
GM
8636 if (!NILP (tem) && !EQ (tem, Qeven) && !EQ (tem, Qodd))
8637 error (":parity must be nil (no parity), `even', or `odd'");
8638 dcb.fParity = FALSE;
8639 dcb.Parity = NOPARITY;
8640 dcb.fErrorChar = FALSE;
8641 if (NILP (tem))
8642 {
8643 summary[1] = 'N';
8644 }
8645 else if (EQ (tem, Qeven))
8646 {
8647 summary[1] = 'E';
8648 dcb.fParity = TRUE;
8649 dcb.Parity = EVENPARITY;
8650 dcb.fErrorChar = TRUE;
8651 }
8652 else if (EQ (tem, Qodd))
8653 {
8654 summary[1] = 'O';
8655 dcb.fParity = TRUE;
8656 dcb.Parity = ODDPARITY;
8657 dcb.fErrorChar = TRUE;
8658 }
8659 childp2 = Fplist_put (childp2, QCparity, tem);
8660
8661 /* Configure stopbits. */
8662 if (!NILP (Fplist_member (contact, QCstopbits)))
8663 tem = Fplist_get (contact, QCstopbits);
8664 else
4d2b044c 8665 tem = Fplist_get (p->childp, QCstopbits);
d888760c
GM
8666 if (NILP (tem))
8667 tem = make_number (1);
8668 CHECK_NUMBER (tem);
8669 if (XINT (tem) != 1 && XINT (tem) != 2)
8670 error (":stopbits must be nil (1 stopbit), 1, or 2");
8671 summary[2] = XINT (tem) + '0';
8672 if (XINT (tem) == 1)
8673 dcb.StopBits = ONESTOPBIT;
8674 else if (XINT (tem) == 2)
8675 dcb.StopBits = TWOSTOPBITS;
8676 childp2 = Fplist_put (childp2, QCstopbits, tem);
8677
8678 /* Configure flowcontrol. */
8679 if (!NILP (Fplist_member (contact, QCflowcontrol)))
8680 tem = Fplist_get (contact, QCflowcontrol);
8681 else
4d2b044c 8682 tem = Fplist_get (p->childp, QCflowcontrol);
d888760c
GM
8683 if (!NILP (tem) && !EQ (tem, Qhw) && !EQ (tem, Qsw))
8684 error (":flowcontrol must be nil (no flowcontrol), `hw', or `sw'");
8685 dcb.fOutxCtsFlow = FALSE;
8686 dcb.fOutxDsrFlow = FALSE;
8687 dcb.fDtrControl = DTR_CONTROL_DISABLE;
8688 dcb.fDsrSensitivity = FALSE;
8689 dcb.fTXContinueOnXoff = FALSE;
8690 dcb.fOutX = FALSE;
8691 dcb.fInX = FALSE;
8692 dcb.fRtsControl = RTS_CONTROL_DISABLE;
8693 dcb.XonChar = 17; /* Control-Q */
8694 dcb.XoffChar = 19; /* Control-S */
8695 if (NILP (tem))
8696 {
8697 /* Already configured. */
8698 }
8699 else if (EQ (tem, Qhw))
8700 {
8701 dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
8702 dcb.fOutxCtsFlow = TRUE;
8703 }
8704 else if (EQ (tem, Qsw))
8705 {
8706 dcb.fOutX = TRUE;
8707 dcb.fInX = TRUE;
8708 }
8709 childp2 = Fplist_put (childp2, QCflowcontrol, tem);
8710
8711 /* Activate configuration. */
8712 if (!SetCommState (hnd, &dcb))
8713 error ("SetCommState() failed");
8714
8715 childp2 = Fplist_put (childp2, QCsummary, build_string (summary));
6a09a33b 8716 pset_childp (p, childp2);
d888760c
GM
8717}
8718
e061a11b
TZ
8719#ifdef HAVE_GNUTLS
8720
8721ssize_t
8722emacs_gnutls_pull (gnutls_transport_ptr_t p, void* buf, size_t sz)
8723{
d78cf5ed 8724 int n, err;
e061a11b 8725 SELECT_TYPE fdset;
43aac990 8726 struct timespec timeout;
e061a11b
TZ
8727 struct Lisp_Process *process = (struct Lisp_Process *)p;
8728 int fd = process->infd;
8729
d78cf5ed 8730 n = sys_read (fd, (char*)buf, sz);
e061a11b 8731
d78cf5ed
CB
8732 if (n >= 0)
8733 return n;
e061a11b 8734
d78cf5ed 8735 err = errno;
e061a11b 8736
d78cf5ed
CB
8737 /* Translate the WSAEWOULDBLOCK alias EWOULDBLOCK to EAGAIN. */
8738 if (err == EWOULDBLOCK)
8739 err = EAGAIN;
e061a11b 8740
d78cf5ed 8741 emacs_gnutls_transport_set_errno (process->gnutls_state, err);
e061a11b 8742
d78cf5ed 8743 return -1;
e061a11b 8744}
ab5796a9 8745
e061a11b
TZ
8746ssize_t
8747emacs_gnutls_push (gnutls_transport_ptr_t p, const void* buf, size_t sz)
8748{
8749 struct Lisp_Process *process = (struct Lisp_Process *)p;
42ce4c63 8750 int fd = process->outfd;
5e617bc2 8751 ssize_t n = sys_write (fd, buf, sz);
e061a11b
TZ
8752
8753 /* 0 or more bytes written means everything went fine. */
8754 if (n >= 0)
8755 return n;
8756
8757 /* Negative bytes written means we got an error in errno.
8758 Translate the WSAEWOULDBLOCK alias EWOULDBLOCK to EAGAIN. */
0898ca10
JB
8759 emacs_gnutls_transport_set_errno (process->gnutls_state,
8760 errno == EWOULDBLOCK ? EAGAIN : errno);
e061a11b
TZ
8761
8762 return -1;
8763}
8764#endif /* HAVE_GNUTLS */
8765
8766/* end of w32.c */