1 /* windows.c
2 * Copyright 1984-2017 Cisco Systems, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /* much of the following code courtesy of Bob Burger, burgerrg@sagian.com */
18
19 #include "system.h"
20 #include <objbase.h>
21 #include <io.h>
22 #include <sys/stat.h>
23
24 static ptr s_ErrorStringImp(DWORD dwMessageId, const char *lpcDefault);
25 static ptr s_ErrorString(DWORD dwMessageId);
26 static IUnknown *s_CreateInstance(CLSID *pCLSID, IID *iid);
27 static ptr s_GetRegistry(wchar_t *s);
28 static void s_PutRegistry(wchar_t *s, wchar_t *val);
29 static void s_RemoveRegistry(wchar_t *s);
30
S_machine_init()31 void S_machine_init() {
32 Sregister_symbol("(com)CreateInstance", (void *)s_CreateInstance);
33 Sregister_symbol("(windows)GetRegistry", (void *)s_GetRegistry);
34 Sregister_symbol("(windows)PutRegistry", (void *)s_PutRegistry);
35 Sregister_symbol("(windows)RemoveRegistry", (void *)s_RemoveRegistry);
36 Sregister_symbol("(windows)ErrorString", (void *)s_ErrorString);
37 }
38
S_getpagesize()39 INT S_getpagesize() {
40 SYSTEM_INFO si;
41 GetSystemInfo(&si);
42 return si.dwPageSize;
43 }
44
S_ntdlopen(const char * path)45 void *S_ntdlopen(const char *path) {
46 wchar_t *pathw = Sutf8_to_wide(path);
47 void *r = (void *)LoadLibraryW(pathw);
48 free(pathw);
49 return r;
50 }
51
S_ntdlsym(void * h,const char * s)52 void *S_ntdlsym(void *h, const char *s) {
53 return (void *)GetProcAddress(h, s);
54 }
55
56 /* Initial version of S_ntdlerror courtesy of Bob Burger
57 * Modifications by James-Adam Renquinha Henri, jarhmander@gmail.com */
S_ntdlerror(void)58 ptr S_ntdlerror(void) {
59 return s_ErrorStringImp(GetLastError(), "unable to load library");
60 }
61
62 #ifdef FLUSHCACHE
63 oops, no S_flushcache_max_gap or S_doflush
64 #endif /* FLUSHCACHE */
65
66 static void SplitRegistryKey(char *who, wchar_t *wholekey, HKEY *key, wchar_t **subkey, wchar_t **last) {
67 wchar_t c, *s;
68
69 /* Determine the base key */
70 if (_wcsnicmp(wholekey, L"HKEY_CLASSES_ROOT\\", 18) == 0) {
71 *key = HKEY_CLASSES_ROOT;
72 *subkey = wholekey+18;
73 } else if (_wcsnicmp(wholekey, L"HKEY_CURRENT_USER\\", 18) == 0) {
74 *key = HKEY_CURRENT_USER;
75 *subkey = wholekey+18;
76 } else if (_wcsnicmp(wholekey, L"HKEY_LOCAL_MACHINE\\", 19) == 0) {
77 *key = HKEY_LOCAL_MACHINE;
78 *subkey = wholekey+19;
79 } else if (_wcsnicmp(wholekey, L"HKEY_USERS\\", 11) == 0) {
80 *key = HKEY_USERS;
81 *subkey = wholekey+11;
82 } else if (_wcsnicmp(wholekey, L"HKEY_CURRENT_CONFIG\\", 20) == 0) {
83 *key = HKEY_CURRENT_CONFIG;
84 *subkey = wholekey+20;
85 } else if (_wcsnicmp(wholekey, L"HKEY_DYN_DATA\\", 14) == 0) {
86 *key = HKEY_DYN_DATA;
87 *subkey = wholekey+14;
88 } else {
89 char *wholekey_utf8 = Swide_to_utf8(wholekey);
90 ptr wholekey_scheme = Sstring_utf8(wholekey_utf8, -1);
91 free(wholekey_utf8);
92 S_error1(who, "invalid registry key ~s", wholekey_scheme);
93 }
94
95 for (*last = s = *subkey, c = *s; c != '\0'; c = *++s)
96 if (c == '\\') *last = s;
97 }
98
s_GetRegistry(wchar_t * s)99 static ptr s_GetRegistry(wchar_t *s) {
100 HKEY key, result;
101 wchar_t *subkey, *last;
102 DWORD rc, type, size;
103 ptr ans;
104
105 SplitRegistryKey("get-registry", s, &key, &subkey, &last);
106
107 /* open the key */
108 if (last == subkey) {
109 rc = RegOpenKeyExW(key, L"", 0, KEY_QUERY_VALUE, &result);
110 } else {
111 *last = '\0'; /* Truncate subkey at backslash */
112 rc = RegOpenKeyExW(key, subkey, 0, KEY_QUERY_VALUE, &result);
113 *last++ = '\\'; /* Restore backslash */
114 }
115 if (rc != ERROR_SUCCESS) return Sfalse;
116
117 /* Get the size of the value */
118 rc = RegQueryValueExW(result, last, NULL, &type, NULL, &size);
119 if (rc != ERROR_SUCCESS) {
120 RegCloseKey(result);
121 return Sfalse;
122 }
123
124 /* Allocate a Scheme bytevector of the proper size */
125 ans = S_bytevector(size);
126
127 /* Load up the bytevector */
128 rc = RegQueryValueExW(result, last, NULL, &type, &BVIT(ans,0), &size);
129 RegCloseKey(result);
130 if (rc != ERROR_SUCCESS) return Sfalse;
131
132 /* discard unwanted terminating null character, if present */
133 if (((type == REG_SZ) || (type == REG_EXPAND_SZ)) &&
134 (size >= 2) &&
135 (*(wchar_t*)(&BVIT(ans, size-2)) == 0))
136 BYTEVECTOR_TYPE(ans) = ((size-2) << bytevector_length_offset) | type_bytevector;
137
138 return ans;
139 }
140
s_PutRegistry(wchar_t * s,wchar_t * val)141 static void s_PutRegistry(wchar_t *s, wchar_t *val) {
142 HKEY key, result;
143 wchar_t *subkey, *last;
144 DWORD rc, type;
145 size_t n = (wcslen(val) + 1) * sizeof(wchar_t);
146 #if (size_t_bits > 32)
147 if ((DWORD)n != n) {
148 char *s_utf8 = Swide_to_utf8(s);
149 ptr s_scheme = Sstring_utf8(s_utf8, -1);
150 free(s_utf8);
151 S_error2("put-registry!", "cannot set ~a (~a)", s_scheme, Sstring("too long"));
152 }
153 #endif
154
155 SplitRegistryKey("put-registry!", s, &key, &subkey, &last);
156
157 /* create/open the key */
158 if (last == subkey) {
159 rc = RegCreateKeyExW(key, L"", 0, NULL, 0, KEY_SET_VALUE, NULL, &result, NULL);
160 } else {
161 *last = '\0'; /* Truncate subkey at backslash */
162 rc = RegCreateKeyExW(key, subkey, 0, NULL, 0, KEY_SET_VALUE, NULL, &result, NULL);
163 *last++ = '\\'; /* Restore backslash */
164 }
165
166 if (rc == ERROR_SUCCESS) {
167 /* lookup type for key (if it exists), if not assume REG_SZ */
168 if (ERROR_SUCCESS != RegQueryValueExW(result, last, NULL, &type, NULL, NULL))
169 type = REG_SZ;
170
171 /* set the value */
172 rc = RegSetValueExW(result, last, 0, type, (const BYTE*)val, (DWORD)n);
173 RegCloseKey(result);
174 }
175
176 if (rc != ERROR_SUCCESS) {
177 char *s_utf8 = Swide_to_utf8(s);
178 ptr s_scheme = Sstring_utf8(s_utf8, -1);
179 free(s_utf8);
180 S_error2("put-registry!", "cannot set ~a (~a)", s_scheme,
181 rc == ERROR_FILE_NOT_FOUND ? Sstring("not found") : s_ErrorString(rc));
182 }
183 }
184
185
s_RemoveRegistry(wchar_t * s)186 static void s_RemoveRegistry(wchar_t *s) {
187 HKEY key, result;
188 wchar_t *subkey, *last;
189 DWORD rc;
190
191 SplitRegistryKey("remove-registry!", s, &key, &subkey, &last);
192
193 /* open the key */
194 if (last == subkey) {
195 rc = RegOpenKeyExW(key, L"", 0, KEY_ALL_ACCESS, &result);
196 } else {
197 *last = '\0'; /* Truncate subkey at backslash */
198 rc = RegOpenKeyExW(key, subkey, 0, KEY_ALL_ACCESS, &result);
199 *last++ = '\\'; /* Restore backslash */
200 }
201 if (rc == ERROR_SUCCESS) {
202 /* delete the value */
203 rc = RegDeleteValueW(result, last);
204 if (rc == ERROR_FILE_NOT_FOUND)
205 /* value by given name not found; try deleting as key */
206 rc = RegDeleteKeyW(result, last);
207 RegCloseKey(result);
208 }
209
210 if (rc != ERROR_SUCCESS) {
211 char *s_utf8 = Swide_to_utf8(s);
212 ptr s_scheme = Sstring_utf8(s_utf8, -1);
213 free(s_utf8);
214 S_error2("remove-registry!", "cannot remove ~a (~a)", s_scheme,
215 rc == ERROR_FILE_NOT_FOUND ? Sstring("not found") :
216 rc == ERROR_ACCESS_DENIED ? Sstring("insufficient permission or subkeys exist") :
217 s_ErrorString(rc));
218 }
219 }
220
s_CreateInstance(CLSID * pCLSID,IID * iid)221 static IUnknown *s_CreateInstance(CLSID *pCLSID, IID *iid) {
222 IUnknown *pIface;
223 HRESULT hr;
224
225 hr = CoCreateInstance(pCLSID,
226 NULL,
227 CLSCTX_INPROC_SERVER,
228 iid,
229 (void **)&pIface);
230 if (SUCCEEDED(hr)) {
231 return (IUnknown *)pIface;
232 } else {
233 S_error1("", "unable to create instance: ~s", s_ErrorString(hr));
234 return (IUnknown *)0 /* not reached */;
235 }
236 }
237
s_ErrorString(DWORD dwMessageId)238 static ptr s_ErrorString(DWORD dwMessageId) {
239 return s_ErrorStringImp(dwMessageId, NULL);
240 }
241
s_ErrorStringImp(DWORD dwMessageId,const char * lpcDefault)242 static ptr s_ErrorStringImp(DWORD dwMessageId, const char *lpcDefault) {
243 wchar_t *lpMsgBuf;
244 DWORD len;
245 char *u8str;
246 ptr result;
247
248 len = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
249 NULL, dwMessageId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL);
250 /* If FormatMessage fails... */
251 if (len == 0) {
252 if (lpcDefault) {
253 /* ... use the default string if provided... */
254 return Sstring_utf8(lpcDefault, -1);
255 } else {
256 /* ...otherwise, use the error code in hexadecimal. */
257 char buf[(sizeof(dwMessageId) * 2) + 3];
258 int n = snprintf(buf, sizeof(buf), "0x%lx", dwMessageId);
259 if ((unsigned)n < sizeof(buf))
260 return Sstring_utf8(buf, n);
261 else
262 return Sstring("??");
263 }
264 }
265 /* Otherwise remove trailing newlines & returns and strip trailing period, if present. */
266 while (len > 0) {
267 wchar_t c = lpMsgBuf[len - 1];
268 if (c == L'\n' || c == '\r')
269 len--;
270 else if (c == L'.') {
271 len--;
272 break;
273 }
274 else break;
275 }
276 lpMsgBuf[len] = 0;
277 u8str = Swide_to_utf8(lpMsgBuf);
278 LocalFree(lpMsgBuf);
279 result = Sstring_utf8(u8str, -1);
280 free(u8str);
281 return result;
282 }
283
S_LastErrorString(void)284 ptr S_LastErrorString(void) {
285 return s_ErrorString(GetLastError());
286 }
287
288 #ifdef CHAFF
S_windows_open_exclusive(char * who,char * path,int flags)289 int S_windows_open_exclusive(char *who, char *path, int flags) {
290 HANDLE hfile;
291 int fd;
292 DWORD access = 0;
293 DWORD crdisp = 0;
294
295 /* could implement this later with more difficulty */
296 if ((flags & (O_TRUNC|O_CREAT)) == (O_TRUNC|O_CREAT))
297 S_error("open_exclusive", "O_TRUNC|O_CREAT not supported");
298
299 if (flags & O_RDWR) access |= GENERIC_READ|GENERIC_WRITE;
300 if (flags & O_RDONLY) access |= GENERIC_READ;
301 if (flags & O_WRONLY) access |= GENERIC_WRITE;
302
303 if (flags & O_CREAT) crdisp = OPEN_ALWAYS;
304 if (flags & O_TRUNC) crdisp = TRUNCATE_EXISTING;
305
306 hfile = CreateFile(path, access, 0, (SECURITY_ATTRIBUTES *)0,
307 crdisp, FILE_ATTRIBUTE_NORMAL, (HANDLE)0);
308 if (hfile == INVALID_HANDLE_VALUE)
309 S_error1(who, "~a", s_ErrorString(GetLastError()));
310
311 flags &= O_RDONLY|O_WRONLY|O_RDWR|O_APPEND;
312 fd = _open_osfhandle((long)hfile, flags);
313 if (fd == -1) S_error(who, "open_osfhandle failed");
314
315 return fd;
316 }
317 #endif
318
319 #include <winbase.h>
320
321 /* primitive version of flock compatible with Windows 95/98/ME. A better
322 version could be implemented for Windows NT/2000/XP using LockFileEx. */
S_windows_flock(int fd,int operation)323 int S_windows_flock(int fd, int operation) {
324 HANDLE hfile = (HANDLE)_get_osfhandle(fd);
325
326 switch (operation) {
327 case LOCK_EX|LOCK_NB:
328 if (LockFile(hfile, 0, 0, 0x0fffffff, 0)) return 0;
329 errno = EWOULDBLOCK;
330 return -1;
331 case LOCK_EX:
332 while (LockFile(hfile, 0, 0, 0x0fffffff, 0) == 0) Sleep(10);
333 return 0;
334 case LOCK_SH:
335 case LOCK_SH|LOCK_NB:
336 S_error("flock", "shared locks unsupported");
337 return -1;
338 case LOCK_UN:
339 case LOCK_UN|LOCK_NB:
340 UnlockFile(hfile, 0, 0, 0x0fffffff, 0);
341 return 0;
342 default:
343 errno = EINVAL;
344 return -1;
345 }
346 }
347
S_windows_chdir(const char * pathname)348 int S_windows_chdir(const char *pathname) {
349 wchar_t wpathname[PATH_MAX];
350 if (MultiByteToWideChar(CP_UTF8,0,pathname,-1,wpathname,PATH_MAX) == 0)
351 return _chdir(pathname);
352 else
353 return _wchdir(wpathname);
354 }
355
S_windows_chmod(const char * pathname,int mode)356 int S_windows_chmod(const char *pathname, int mode) {
357 wchar_t wpathname[PATH_MAX];
358 if (MultiByteToWideChar(CP_UTF8,0,pathname,-1,wpathname,PATH_MAX) == 0)
359 return _chmod(pathname, mode);
360 else
361 return _wchmod(wpathname, mode);
362 }
363
S_windows_mkdir(const char * pathname)364 int S_windows_mkdir(const char *pathname) {
365 wchar_t wpathname[PATH_MAX];
366 if (MultiByteToWideChar(CP_UTF8,0,pathname,-1,wpathname,PATH_MAX) == 0)
367 return _mkdir(pathname);
368 else
369 return _wmkdir(wpathname);
370 }
371
S_windows_open(const char * pathname,int flags,int mode)372 int S_windows_open(const char *pathname, int flags, int mode) {
373 wchar_t wpathname[PATH_MAX];
374 if (MultiByteToWideChar(CP_UTF8,0,pathname,-1,wpathname,PATH_MAX) == 0)
375 return _open(pathname,flags, mode);
376 else
377 return _wopen(wpathname,flags,mode);
378 }
379
S_windows_rename(const char * oldpathname,const char * newpathname)380 int S_windows_rename(const char *oldpathname, const char *newpathname) {
381 wchar_t woldpathname[PATH_MAX], wnewpathname[PATH_MAX];
382 if (MultiByteToWideChar(CP_UTF8,0,oldpathname,-1,woldpathname,PATH_MAX) == 0 ||
383 MultiByteToWideChar(CP_UTF8,0,newpathname,-1,wnewpathname,PATH_MAX) == 0)
384 return rename(oldpathname, newpathname);
385 else
386 return _wrename(woldpathname, wnewpathname);
387 }
388
S_windows_rmdir(const char * pathname)389 int S_windows_rmdir(const char *pathname) {
390 wchar_t wpathname[PATH_MAX];
391 if (MultiByteToWideChar(CP_UTF8,0,pathname,-1,wpathname,PATH_MAX) == 0)
392 return _rmdir(pathname);
393 else {
394 int rc;
395 if (!(rc = _wrmdir(wpathname))) {
396 // Spin loop until Windows deletes the directory.
397 int n;
398 for (n = 1000; n > 0; n--) {
399 if (_wrmdir(wpathname) && (errno == ENOENT)) break;
400 }
401 return 0;
402 }
403 return rc;
404 }
405 }
406
S_windows_stat64(const char * pathname,struct STATBUF * buffer)407 int S_windows_stat64(const char *pathname, struct STATBUF *buffer) {
408 wchar_t wpathname[PATH_MAX];
409 if (MultiByteToWideChar(CP_UTF8,0,pathname,-1,wpathname,PATH_MAX) == 0)
410 return _stat64(pathname, buffer);
411 else
412 return _wstat64(wpathname, buffer);
413 }
414
S_windows_system(const char * command)415 int S_windows_system(const char *command) {
416 wchar_t wcommand[PATH_MAX];
417 if (MultiByteToWideChar(CP_UTF8,0,command,-1,wcommand,PATH_MAX) == 0)
418 return system(command);
419 else
420 return _wsystem(wcommand);
421 }
422
S_windows_unlink(const char * pathname)423 int S_windows_unlink(const char *pathname) {
424 wchar_t wpathname[PATH_MAX];
425 if (MultiByteToWideChar(CP_UTF8,0,pathname,-1,wpathname,PATH_MAX) == 0)
426 return _unlink(pathname);
427 else {
428 int rc;
429 if (!(rc = _wunlink(wpathname))) {
430 // Spin loop until Windows deletes the file.
431 int n;
432 for (n = 1000; n > 0; n--) {
433 if (_wunlink(wpathname) && (errno == ENOENT)) break;
434 }
435 return 0;
436 }
437 return rc;
438 }
439 }
440
S_windows_getcwd(char * buffer,UNUSED int maxlen)441 char *S_windows_getcwd(char *buffer, UNUSED int maxlen) {
442 wchar_t wbuffer[PATH_MAX];
443 if (_wgetcwd(wbuffer, PATH_MAX) == NULL) return NULL;
444 if (WideCharToMultiByte(CP_UTF8,0,wbuffer,-1,buffer,PATH_MAX,NULL,NULL) == 0) {
445 switch (GetLastError()) {
446 case ERROR_INSUFFICIENT_BUFFER:
447 errno = ERANGE;
448 break;
449 default:
450 errno = EINVAL;
451 break;
452 }
453 return NULL;
454 } else
455 return buffer;
456 }
457
Swide_to_utf8(const wchar_t * arg)458 char *Swide_to_utf8(const wchar_t *arg) {
459 int len = WideCharToMultiByte(CP_UTF8, 0, arg, -1, NULL, 0, NULL, NULL);
460 if (0 == len) return NULL;
461 char* arg8 = (char*)malloc(len * sizeof(char));
462 if (0 == WideCharToMultiByte(CP_UTF8, 0, arg, -1, arg8, len, NULL, NULL)) {
463 free(arg8);
464 return NULL;
465 }
466 return arg8;
467 }
468
Sutf8_to_wide(const char * arg)469 wchar_t *Sutf8_to_wide(const char *arg) {
470 int len = MultiByteToWideChar(CP_UTF8, 0, arg, -1, NULL, 0);
471 if (0 == len) return NULL;
472 wchar_t* argw = (wchar_t*)malloc(len * sizeof(wchar_t));
473 if (0 == MultiByteToWideChar(CP_UTF8, 0, arg, -1, argw, len)) {
474 free(argw);
475 return NULL;
476 }
477 return argw;
478 }
479
Sgetenv(const char * name)480 char *Sgetenv(const char *name) {
481 wchar_t* wname;
482 DWORD n;
483 wchar_t buffer[256];
484 wname = Sutf8_to_wide(name);
485 if (NULL == wname) return NULL;
486 n = GetEnvironmentVariableW(wname, buffer, 256);
487 if (n == 0) {
488 free(wname);
489 return NULL;
490 } else if (n <= 256) {
491 free(wname);
492 return Swide_to_utf8(buffer);
493 } else {
494 wchar_t* value = (wchar_t*)malloc(n * sizeof(wchar_t));
495 if (0 == GetEnvironmentVariableW(wname, value, n)) {
496 free(wname);
497 free(value);
498 return NULL;
499 } else {
500 char* result = Swide_to_utf8(value);
501 free(wname);
502 free(value);
503 return result;
504 }
505 }
506 }
507