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