xref: /minix3/external/bsd/libarchive/dist/libarchive/archive_windows.c (revision 543adbed3a3a783ed36434adafbc258b6bde442d)
1 /*-
2  * Copyright (c) 2009 Michihiro NAKAJIMA
3  * Copyright (c) 2003-2007 Kees Zeelenberg
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 /*
30  * A set of compatibility glue for building libarchive on Windows platforms.
31  *
32  * Originally created as "libarchive-nonposix.c" by Kees Zeelenberg
33  * for the GnuWin32 project, trimmed significantly by Tim Kientzle.
34  *
35  * Much of the original file was unnecessary for libarchive, because
36  * many of the features it emulated were not strictly necessary for
37  * libarchive.  I hope for this to shrink further as libarchive
38  * internals are gradually reworked to sit more naturally on both
39  * POSIX and Windows.  Any ideas for this are greatly appreciated.
40  *
41  * The biggest remaining issue is the dev/ino emulation; libarchive
42  * has a couple of public APIs that rely on dev/ino uniquely
43  * identifying a file.  This doesn't match well with Windows.  I'm
44  * considering alternative APIs.
45  */
46 
47 #if defined(_WIN32) && !defined(__CYGWIN__)
48 
49 #include "archive_platform.h"
50 #include "archive_private.h"
51 #include "archive_hash.h"
52 #include <ctype.h>
53 #include <errno.h>
54 #include <stddef.h>
55 #ifdef HAVE_SYS_UTIME_H
56 #include <sys/utime.h>
57 #endif
58 #include <sys/stat.h>
59 #include <process.h>
60 #include <stdlib.h>
61 #include <wchar.h>
62 #include <windows.h>
63 
64 #define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
65 
66 #if defined(_MSC_VER) && _MSC_VER < 1300
67 /* VS 6 does not provide SetFilePointerEx, so define it here.  */
SetFilePointerEx(HANDLE hFile,LARGE_INTEGER liDistanceToMove,PLARGE_INTEGER lpNewFilePointer,DWORD dwMoveMethod)68 static BOOL SetFilePointerEx(HANDLE hFile,
69                              LARGE_INTEGER liDistanceToMove,
70                              PLARGE_INTEGER lpNewFilePointer,
71                              DWORD dwMoveMethod)
72 {
73 	LARGE_INTEGER li;
74 	li.QuadPart = liDistanceToMove.QuadPart;
75 	li.LowPart = SetFilePointer(
76 	    hFile, li.LowPart, &li.HighPart, dwMoveMethod);
77 	if(lpNewFilePointer) {
78 		lpNewFilePointer->QuadPart = li.QuadPart;
79 	}
80 	return li.LowPart != -1 || GetLastError() == NO_ERROR;
81 }
82 #endif
83 
84 struct ustat {
85 	int64_t		st_atime;
86 	uint32_t	st_atime_nsec;
87 	int64_t		st_ctime;
88 	uint32_t	st_ctime_nsec;
89 	int64_t		st_mtime;
90 	uint32_t	st_mtime_nsec;
91 	gid_t		st_gid;
92 	/* 64bits ino */
93 	int64_t		st_ino;
94 	mode_t		st_mode;
95 	uint32_t	st_nlink;
96 	uint64_t	st_size;
97 	uid_t		st_uid;
98 	dev_t		st_dev;
99 	dev_t		st_rdev;
100 };
101 
102 /* Local replacement for undocumented Windows CRT function. */
103 static void la_dosmaperr(unsigned long e);
104 
105 /* Transform 64-bits ino into 32-bits by hashing.
106  * You do not forget that really unique number size is 64-bits.
107  */
108 #define INOSIZE (8*sizeof(ino_t)) /* 32 */
109 static __inline ino_t
getino(struct ustat * ub)110 getino(struct ustat *ub)
111 {
112 	ULARGE_INTEGER ino64;
113 	ino64.QuadPart = ub->st_ino;
114 	/* I don't know this hashing is correct way */
115 	return (ino64.LowPart ^ (ino64.LowPart >> INOSIZE));
116 }
117 
118 /*
119  * Prepend "\\?\" to the path name and convert it to unicode to permit
120  * an extended-length path for a maximum total path length of 32767
121  * characters.
122  * see also http://msdn.microsoft.com/en-us/library/aa365247.aspx
123  */
124 static wchar_t *
permissive_name(const char * name)125 permissive_name(const char *name)
126 {
127 	wchar_t *wn, *wnp;
128 	wchar_t *ws, *wsp;
129 	DWORD l, len, slen;
130 	int unc;
131 
132 	len = (DWORD)strlen(name);
133 	wn = malloc((len + 1) * sizeof(wchar_t));
134 	if (wn == NULL)
135 		return (NULL);
136 	l = MultiByteToWideChar(CP_ACP, 0, name, (int)len, wn, (int)len);
137 	if (l == 0) {
138 		free(wn);
139 		return (NULL);
140 	}
141 	wn[l] = L'\0';
142 
143 	/* Get a full path names */
144 	l = GetFullPathNameW(wn, 0, NULL, NULL);
145 	if (l == 0) {
146 		free(wn);
147 		return (NULL);
148 	}
149 	wnp = malloc(l * sizeof(wchar_t));
150 	if (wnp == NULL) {
151 		free(wn);
152 		return (NULL);
153 	}
154 	len = GetFullPathNameW(wn, l, wnp, NULL);
155 	free(wn);
156 	wn = wnp;
157 
158 	if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
159 	    wnp[2] == L'?' && wnp[3] == L'\\')
160 		/* We have already permissive names. */
161 		return (wn);
162 
163 	if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
164 		wnp[2] == L'.' && wnp[3] == L'\\') {
165 		/* Device names */
166 		if (((wnp[4] >= L'a' && wnp[4] <= L'z') ||
167 		     (wnp[4] >= L'A' && wnp[4] <= L'Z')) &&
168 		    wnp[5] == L':' && wnp[6] == L'\\')
169 			wnp[2] = L'?';/* Not device names. */
170 		return (wn);
171 	}
172 
173 	unc = 0;
174 	if (wnp[0] == L'\\' && wnp[1] == L'\\' && wnp[2] != L'\\') {
175 		wchar_t *p = &wnp[2];
176 
177 		/* Skip server-name letters. */
178 		while (*p != L'\\' && *p != L'\0')
179 			++p;
180 		if (*p == L'\\') {
181 			wchar_t *rp = ++p;
182 			/* Skip share-name letters. */
183 			while (*p != L'\\' && *p != L'\0')
184 				++p;
185 			if (*p == L'\\' && p != rp) {
186 				/* Now, match patterns such as
187 				 * "\\server-name\share-name\" */
188 				wnp += 2;
189 				len -= 2;
190 				unc = 1;
191 			}
192 		}
193 	}
194 
195 	slen = 4 + (unc * 4) + len + 1;
196 	ws = wsp = malloc(slen * sizeof(wchar_t));
197 	if (ws == NULL) {
198 		free(wn);
199 		return (NULL);
200 	}
201 	/* prepend "\\?\" */
202 	wcsncpy(wsp, L"\\\\?\\", 4);
203 	wsp += 4;
204 	slen -= 4;
205 	if (unc) {
206 		/* append "UNC\" ---> "\\?\UNC\" */
207 		wcsncpy(wsp, L"UNC\\", 4);
208 		wsp += 4;
209 		slen -= 4;
210 	}
211 	wcsncpy(wsp, wnp, slen);
212 	wsp[slen - 1] = L'\0'; /* Ensure null termination. */
213 	free(wn);
214 	return (ws);
215 }
216 
217 static HANDLE
la_CreateFile(const char * path,DWORD dwDesiredAccess,DWORD dwShareMode,LPSECURITY_ATTRIBUTES lpSecurityAttributes,DWORD dwCreationDisposition,DWORD dwFlagsAndAttributes,HANDLE hTemplateFile)218 la_CreateFile(const char *path, DWORD dwDesiredAccess, DWORD dwShareMode,
219     LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
220     DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
221 {
222 	wchar_t *wpath;
223 	HANDLE handle;
224 
225 	handle = CreateFileA(path, dwDesiredAccess, dwShareMode,
226 	    lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
227 	    hTemplateFile);
228 	if (handle != INVALID_HANDLE_VALUE)
229 		return (handle);
230 	if (GetLastError() != ERROR_PATH_NOT_FOUND)
231 		return (handle);
232 	wpath = permissive_name(path);
233 	if (wpath == NULL)
234 		return (handle);
235 	handle = CreateFileW(wpath, dwDesiredAccess, dwShareMode,
236 	    lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
237 	    hTemplateFile);
238 	free(wpath);
239 	return (handle);
240 }
241 
242 static void *
la_GetFunctionKernel32(const char * name)243 la_GetFunctionKernel32(const char *name)
244 {
245 	static HINSTANCE lib;
246 	static int set;
247 	if (!set) {
248 		set = 1;
249 		lib = LoadLibrary("kernel32.dll");
250 	}
251 	if (lib == NULL) {
252 		fprintf(stderr, "Can't load kernel32.dll?!\n");
253 		exit(1);
254 	}
255 	return (void *)GetProcAddress(lib, name);
256 }
257 
258 static int
la_CreateHardLinkW(wchar_t * linkname,wchar_t * target)259 la_CreateHardLinkW(wchar_t *linkname, wchar_t *target)
260 {
261 	static BOOLEAN (WINAPI *f)(LPWSTR, LPWSTR, LPSECURITY_ATTRIBUTES);
262 	static int set;
263 	if (!set) {
264 		set = 1;
265 		f = la_GetFunctionKernel32("CreateHardLinkW");
266 	}
267 	return f == NULL ? 0 : (*f)(linkname, target, NULL);
268 }
269 
270 
271 /* Make a link to src called dst.  */
272 static int
__link(const char * src,const char * dst)273 __link(const char *src, const char *dst)
274 {
275 	wchar_t *wsrc, *wdst;
276 	int res, retval;
277 	DWORD attr;
278 
279 	if (src == NULL || dst == NULL) {
280 		set_errno (EINVAL);
281 		return -1;
282 	}
283 
284 	wsrc = permissive_name(src);
285 	wdst = permissive_name(dst);
286 	if (wsrc == NULL || wdst == NULL) {
287 		free(wsrc);
288 		free(wdst);
289 		set_errno (EINVAL);
290 		return -1;
291 	}
292 
293 	if ((attr = GetFileAttributesW(wsrc)) != (DWORD)-1) {
294 		res = la_CreateHardLinkW(wdst, wsrc);
295 	} else {
296 		/* wsrc does not exist; try src prepend it with the dirname of wdst */
297 		wchar_t *wnewsrc, *slash;
298 		int i, n, slen, wlen;
299 
300 		if (strlen(src) >= 3 && isalpha((unsigned char)src[0]) &&
301 		    src[1] == ':' && src[2] == '\\') {
302 			/* Original src name is already full-path */
303 			retval = -1;
304 			goto exit;
305 		}
306 		if (src[0] == '\\') {
307 			/* Original src name is almost full-path
308 			 * (maybe src name is without drive) */
309 			retval = -1;
310 			goto exit;
311 		}
312 
313 		wnewsrc = malloc ((wcslen(wsrc) + wcslen(wdst) + 1) * sizeof(wchar_t));
314 		if (wnewsrc == NULL) {
315 			errno = ENOMEM;
316 			retval = -1;
317 			goto exit;
318 		}
319 		/* Copying a dirname of wdst */
320 		wcscpy(wnewsrc, wdst);
321 		slash = wcsrchr(wnewsrc, L'\\');
322 		if (slash != NULL)
323 			*++slash = L'\0';
324 		else
325 			wcscat(wnewsrc, L"\\");
326 		/* Converting multi-byte src to wide-char src */
327 		wlen = (int)wcslen(wsrc);
328 		slen = (int)strlen(src);
329 		n = MultiByteToWideChar(CP_ACP, 0, src, slen, wsrc, wlen);
330 		if (n == 0) {
331 			free (wnewsrc);
332 			retval = -1;
333 			goto exit;
334 		}
335 		for (i = 0; i < n; i++)
336 			if (wsrc[i] == L'/')
337 				wsrc[i] = L'\\';
338 		wcsncat(wnewsrc, wsrc, n);
339 		/* Check again */
340 		attr = GetFileAttributesW(wnewsrc);
341 		if (attr == (DWORD)-1 || (attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
342 			if (attr == (DWORD)-1)
343 				la_dosmaperr(GetLastError());
344 			else
345 				errno = EPERM;
346 			free (wnewsrc);
347 			retval = -1;
348 			goto exit;
349 		}
350 		res = la_CreateHardLinkW(wdst, wnewsrc);
351 		free (wnewsrc);
352 	}
353 	if (res == 0) {
354 		la_dosmaperr(GetLastError());
355 		retval = -1;
356 	} else
357 		retval = 0;
358 exit:
359 	free(wsrc);
360 	free(wdst);
361 	return (retval);
362 }
363 
364 /* Make a hard link to src called dst.  */
365 int
__la_link(const char * src,const char * dst)366 __la_link(const char *src, const char *dst)
367 {
368 	return __link(src, dst);
369 }
370 
371 int
__la_ftruncate(int fd,off_t length)372 __la_ftruncate(int fd, off_t length)
373 {
374 	LARGE_INTEGER distance;
375 	HANDLE handle;
376 
377 	if (fd < 0) {
378 		errno = EBADF;
379 		return (-1);
380 	}
381 	handle = (HANDLE)_get_osfhandle(fd);
382 	if (GetFileType(handle) != FILE_TYPE_DISK) {
383 		errno = EBADF;
384 		return (-1);
385 	}
386 	distance.QuadPart = length;
387 	if (!SetFilePointerEx(handle, distance, NULL, FILE_BEGIN)) {
388 		la_dosmaperr(GetLastError());
389 		return (-1);
390 	}
391 	if (!SetEndOfFile(handle)) {
392 		la_dosmaperr(GetLastError());
393 		return (-1);
394 	}
395 	return (0);
396 }
397 
398 #define WINTIME(sec, usec)	((Int32x32To64(sec, 10000000) + EPOC_TIME) + (usec * 10))
399 static int
__hutimes(HANDLE handle,const struct __timeval * times)400 __hutimes(HANDLE handle, const struct __timeval *times)
401 {
402 	ULARGE_INTEGER wintm;
403 	FILETIME fatime, fmtime;
404 
405 	wintm.QuadPart = WINTIME(times[0].tv_sec, times[0].tv_usec);
406 	fatime.dwLowDateTime = wintm.LowPart;
407 	fatime.dwHighDateTime = wintm.HighPart;
408 	wintm.QuadPart = WINTIME(times[1].tv_sec, times[1].tv_usec);
409 	fmtime.dwLowDateTime = wintm.LowPart;
410 	fmtime.dwHighDateTime = wintm.HighPart;
411 	if (SetFileTime(handle, NULL, &fatime, &fmtime) == 0) {
412 		errno = EINVAL;
413 		return (-1);
414 	}
415 	return (0);
416 }
417 
418 int
__la_futimes(int fd,const struct __timeval * times)419 __la_futimes(int fd, const struct __timeval *times)
420 {
421 
422 	return (__hutimes((HANDLE)_get_osfhandle(fd), times));
423 }
424 
425 int
__la_utimes(const char * name,const struct __timeval * times)426 __la_utimes(const char *name, const struct __timeval *times)
427 {
428 	int ret;
429 	HANDLE handle;
430 
431 	handle = la_CreateFile(name, GENERIC_READ | GENERIC_WRITE,
432 	    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
433 	    FILE_FLAG_BACKUP_SEMANTICS, NULL);
434 	if (handle == INVALID_HANDLE_VALUE) {
435 		la_dosmaperr(GetLastError());
436 		return (-1);
437 	}
438 	ret = __hutimes(handle, times);
439 	CloseHandle(handle);
440 	return (ret);
441 }
442 
443 int
__la_chdir(const char * path)444 __la_chdir(const char *path)
445 {
446 	wchar_t *ws;
447 	int r;
448 
449 	r = SetCurrentDirectoryA(path);
450 	if (r == 0) {
451 		if (GetLastError() != ERROR_FILE_NOT_FOUND) {
452 			la_dosmaperr(GetLastError());
453 			return (-1);
454 		}
455 	} else
456 		return (0);
457 	ws = permissive_name(path);
458 	if (ws == NULL) {
459 		errno = EINVAL;
460 		return (-1);
461 	}
462 	r = SetCurrentDirectoryW(ws);
463 	free(ws);
464 	if (r == 0) {
465 		la_dosmaperr(GetLastError());
466 		return (-1);
467 	}
468 	return (0);
469 }
470 
471 int
__la_chmod(const char * path,mode_t mode)472 __la_chmod(const char *path, mode_t mode)
473 {
474 	wchar_t *ws;
475 	DWORD attr;
476 	BOOL r;
477 
478 	ws = NULL;
479 	attr = GetFileAttributesA(path);
480 	if (attr == (DWORD)-1) {
481 		if (GetLastError() != ERROR_FILE_NOT_FOUND) {
482 			la_dosmaperr(GetLastError());
483 			return (-1);
484 		}
485 		ws = permissive_name(path);
486 		if (ws == NULL) {
487 			errno = EINVAL;
488 			return (-1);
489 		}
490 		attr = GetFileAttributesW(ws);
491 		if (attr == (DWORD)-1) {
492 			free(ws);
493 			la_dosmaperr(GetLastError());
494 			return (-1);
495 		}
496 	}
497 	if (mode & _S_IWRITE)
498 		attr &= ~FILE_ATTRIBUTE_READONLY;
499 	else
500 		attr |= FILE_ATTRIBUTE_READONLY;
501 	if (ws == NULL)
502 		r = SetFileAttributesA(path, attr);
503 	else {
504 		r = SetFileAttributesW(ws, attr);
505 		free(ws);
506 	}
507 	if (r == 0) {
508 		la_dosmaperr(GetLastError());
509 		return (-1);
510 	}
511 	return (0);
512 }
513 
514 /*
515  * This fcntl is limited implemention.
516  */
517 int
__la_fcntl(int fd,int cmd,int val)518 __la_fcntl(int fd, int cmd, int val)
519 {
520 	HANDLE handle;
521 
522 	handle = (HANDLE)_get_osfhandle(fd);
523 	if (GetFileType(handle) == FILE_TYPE_PIPE) {
524 		if (cmd == F_SETFL && val == 0) {
525 			DWORD mode = PIPE_WAIT;
526 			if (SetNamedPipeHandleState(
527 			    handle, &mode, NULL, NULL) != 0)
528 				return (0);
529 		}
530 	}
531 	errno = EINVAL;
532 	return (-1);
533 }
534 
535 __int64
__la_lseek(int fd,__int64 offset,int whence)536 __la_lseek(int fd, __int64 offset, int whence)
537 {
538 	LARGE_INTEGER distance;
539 	LARGE_INTEGER newpointer;
540 	HANDLE handle;
541 
542 	if (fd < 0) {
543 		errno = EBADF;
544 		return (-1);
545 	}
546 	handle = (HANDLE)_get_osfhandle(fd);
547 	if (GetFileType(handle) != FILE_TYPE_DISK) {
548 		errno = EBADF;
549 		return (-1);
550 	}
551 	distance.QuadPart = offset;
552 	if (!SetFilePointerEx(handle, distance, &newpointer, whence)) {
553 		DWORD lasterr;
554 
555 		lasterr = GetLastError();
556 		if (lasterr == ERROR_BROKEN_PIPE)
557 			return (0);
558 		if (lasterr == ERROR_ACCESS_DENIED)
559 			errno = EBADF;
560 		else
561 			la_dosmaperr(lasterr);
562 		return (-1);
563 	}
564 	return (newpointer.QuadPart);
565 }
566 
567 int
__la_mkdir(const char * path,mode_t mode)568 __la_mkdir(const char *path, mode_t mode)
569 {
570 	wchar_t *ws;
571 	int r;
572 
573 	(void)mode;/* UNUSED */
574 	r = CreateDirectoryA(path, NULL);
575 	if (r == 0) {
576 		DWORD lasterr = GetLastError();
577 		if (lasterr != ERROR_FILENAME_EXCED_RANGE &&
578 			lasterr != ERROR_PATH_NOT_FOUND) {
579 			la_dosmaperr(GetLastError());
580 			return (-1);
581 		}
582 	} else
583 		return (0);
584 	ws = permissive_name(path);
585 	if (ws == NULL) {
586 		errno = EINVAL;
587 		return (-1);
588 	}
589 	r = CreateDirectoryW(ws, NULL);
590 	free(ws);
591 	if (r == 0) {
592 		la_dosmaperr(GetLastError());
593 		return (-1);
594 	}
595 	return (0);
596 }
597 
598 /* Windows' mbstowcs is differrent error handling from other unix mbstowcs.
599  * That one is using MultiByteToWideChar function with MB_PRECOMPOSED and
600  * MB_ERR_INVALID_CHARS flags.
601  * This implements for only to pass libarchive_test.
602  */
603 size_t
__la_mbstowcs(wchar_t * wcstr,const char * mbstr,size_t nwchars)604 __la_mbstowcs(wchar_t *wcstr, const char *mbstr, size_t nwchars)
605 {
606 
607 	return (MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS,
608 	    mbstr, (int)strlen(mbstr), wcstr,
609 	    (int)nwchars));
610 }
611 
612 int
__la_open(const char * path,int flags,...)613 __la_open(const char *path, int flags, ...)
614 {
615 	va_list ap;
616 	wchar_t *ws;
617 	int r, pmode;
618 	DWORD attr;
619 
620 	va_start(ap, flags);
621 	pmode = va_arg(ap, int);
622 	va_end(ap);
623 	ws = NULL;
624 	if ((flags & ~O_BINARY) == O_RDONLY) {
625 		/*
626 		 * When we open a directory, _open function returns
627 		 * "Permission denied" error.
628 		 */
629 		attr = GetFileAttributesA(path);
630 		if (attr == (DWORD)-1 && GetLastError() == ERROR_PATH_NOT_FOUND) {
631 			ws = permissive_name(path);
632 			if (ws == NULL) {
633 				errno = EINVAL;
634 				return (-1);
635 			}
636 			attr = GetFileAttributesW(ws);
637 		}
638 		if (attr == (DWORD)-1) {
639 			la_dosmaperr(GetLastError());
640 			free(ws);
641 			return (-1);
642 		}
643 		if (attr & FILE_ATTRIBUTE_DIRECTORY) {
644 			HANDLE handle;
645 
646 			if (ws != NULL)
647 				handle = CreateFileW(ws, 0, 0, NULL,
648 				    OPEN_EXISTING,
649 				    FILE_FLAG_BACKUP_SEMANTICS |
650 				    FILE_ATTRIBUTE_READONLY,
651 					NULL);
652 			else
653 				handle = CreateFileA(path, 0, 0, NULL,
654 				    OPEN_EXISTING,
655 				    FILE_FLAG_BACKUP_SEMANTICS |
656 				    FILE_ATTRIBUTE_READONLY,
657 					NULL);
658 			free(ws);
659 			if (handle == INVALID_HANDLE_VALUE) {
660 				la_dosmaperr(GetLastError());
661 				return (-1);
662 			}
663 			r = _open_osfhandle((intptr_t)handle, _O_RDONLY);
664 			return (r);
665 		}
666 	}
667 	if (ws == NULL) {
668 #if defined(__BORLANDC__)
669 		/* Borland has no mode argument.
670 		   TODO: Fix mode of new file.  */
671 		r = _open(path, flags);
672 #else
673 		r = _open(path, flags, pmode);
674 #endif
675 		if (r < 0 && errno == EACCES && (flags & O_CREAT) != 0) {
676 			/* simular other POSIX system action to pass a test */
677 			attr = GetFileAttributesA(path);
678 			if (attr == (DWORD)-1)
679 				la_dosmaperr(GetLastError());
680 			else if (attr & FILE_ATTRIBUTE_DIRECTORY)
681 				errno = EISDIR;
682 			else
683 				errno = EACCES;
684 			return (-1);
685 		}
686 		if (r >= 0 || errno != ENOENT)
687 			return (r);
688 		ws = permissive_name(path);
689 		if (ws == NULL) {
690 			errno = EINVAL;
691 			return (-1);
692 		}
693 	}
694 	r = _wopen(ws, flags, pmode);
695 	if (r < 0 && errno == EACCES && (flags & O_CREAT) != 0) {
696 		/* simular other POSIX system action to pass a test */
697 		attr = GetFileAttributesW(ws);
698 		if (attr == (DWORD)-1)
699 			la_dosmaperr(GetLastError());
700 		else if (attr & FILE_ATTRIBUTE_DIRECTORY)
701 			errno = EISDIR;
702 		else
703 			errno = EACCES;
704 	}
705 	free(ws);
706 	return (r);
707 }
708 
709 ssize_t
__la_read(int fd,void * buf,size_t nbytes)710 __la_read(int fd, void *buf, size_t nbytes)
711 {
712 	HANDLE handle;
713 	DWORD bytes_read, lasterr;
714 	int r;
715 
716 #ifdef _WIN64
717 	if (nbytes > UINT32_MAX)
718 		nbytes = UINT32_MAX;
719 #endif
720 	if (fd < 0) {
721 		errno = EBADF;
722 		return (-1);
723 	}
724 	handle = (HANDLE)_get_osfhandle(fd);
725 	if (GetFileType(handle) == FILE_TYPE_PIPE) {
726 		DWORD sta;
727 		if (GetNamedPipeHandleState(
728 		    handle, &sta, NULL, NULL, NULL, NULL, 0) != 0 &&
729 		    (sta & PIPE_NOWAIT) == 0) {
730 			DWORD avail = -1;
731 			int cnt = 3;
732 
733 			while (PeekNamedPipe(
734 			    handle, NULL, 0, NULL, &avail, NULL) != 0 &&
735 			    avail == 0 && --cnt)
736 				Sleep(100);
737 			if (avail == 0)
738 				return (0);
739 		}
740 	}
741 	r = ReadFile(handle, buf, (uint32_t)nbytes,
742 	    &bytes_read, NULL);
743 	if (r == 0) {
744 		lasterr = GetLastError();
745 		if (lasterr == ERROR_NO_DATA) {
746 			errno = EAGAIN;
747 			return (-1);
748 		}
749 		if (lasterr == ERROR_BROKEN_PIPE)
750 			return (0);
751 		if (lasterr == ERROR_ACCESS_DENIED)
752 			errno = EBADF;
753 		else
754 			la_dosmaperr(lasterr);
755 		return (-1);
756 	}
757 	return ((ssize_t)bytes_read);
758 }
759 
760 /* Remove directory */
761 int
__la_rmdir(const char * path)762 __la_rmdir(const char *path)
763 {
764 	wchar_t *ws;
765 	int r;
766 
767 	r = _rmdir(path);
768 	if (r >= 0 || errno != ENOENT)
769 		return (r);
770 	ws = permissive_name(path);
771 	if (ws == NULL) {
772 		errno = EINVAL;
773 		return (-1);
774 	}
775 	r = _wrmdir(ws);
776 	free(ws);
777 	return (r);
778 }
779 
780 /* Convert Windows FILETIME to UTC */
781 __inline static void
fileTimeToUTC(const FILETIME * filetime,time_t * time,long * ns)782 fileTimeToUTC(const FILETIME *filetime, time_t *time, long *ns)
783 {
784 	ULARGE_INTEGER utc;
785 
786 	utc.HighPart = filetime->dwHighDateTime;
787 	utc.LowPart  = filetime->dwLowDateTime;
788 	if (utc.QuadPart >= EPOC_TIME) {
789 		utc.QuadPart -= EPOC_TIME;
790 		*time = (time_t)(utc.QuadPart / 10000000);	/* milli seconds base */
791 		*ns = (long)(utc.QuadPart % 10000000) * 100;/* nano seconds base */
792 	} else {
793 		*time = 0;
794 		*ns = 0;
795 	}
796 }
797 
798 /* Stat by handle
799  * Windows' stat() does not accept path which is added "\\?\" especially "?"
800  * character.
801  * It means we cannot access a long name path(which is longer than MAX_PATH).
802  * So I've implemented simular Windows' stat() to access the long name path.
803  * And I've added some feature.
804  * 1. set st_ino by nFileIndexHigh and nFileIndexLow of
805  *    BY_HANDLE_FILE_INFORMATION.
806  * 2. set st_nlink by nNumberOfLinks of BY_HANDLE_FILE_INFORMATION.
807  * 3. set st_dev by dwVolumeSerialNumber by BY_HANDLE_FILE_INFORMATION.
808  */
809 static int
__hstat(HANDLE handle,struct ustat * st)810 __hstat(HANDLE handle, struct ustat *st)
811 {
812 	BY_HANDLE_FILE_INFORMATION info;
813 	ULARGE_INTEGER ino64;
814 	DWORD ftype;
815 	mode_t mode;
816 	time_t time;
817 	long ns;
818 
819 	switch (ftype = GetFileType(handle)) {
820 	case FILE_TYPE_UNKNOWN:
821 		errno = EBADF;
822 		return (-1);
823 	case FILE_TYPE_CHAR:
824 	case FILE_TYPE_PIPE:
825 		if (ftype == FILE_TYPE_CHAR) {
826 			st->st_mode = S_IFCHR;
827 			st->st_size = 0;
828 		} else {
829 			DWORD avail;
830 
831 			st->st_mode = S_IFIFO;
832 			if (PeekNamedPipe(handle, NULL, 0, NULL, &avail, NULL))
833 				st->st_size = avail;
834 			else
835 				st->st_size = 0;
836 		}
837 		st->st_atime = 0;
838 		st->st_atime_nsec = 0;
839 		st->st_mtime = 0;
840 		st->st_mtime_nsec = 0;
841 		st->st_ctime = 0;
842 		st->st_ctime_nsec = 0;
843 		st->st_ino = 0;
844 		st->st_nlink = 1;
845 		st->st_uid = 0;
846 		st->st_gid = 0;
847 		st->st_rdev = 0;
848 		st->st_dev = 0;
849 		return (0);
850 	case FILE_TYPE_DISK:
851 		break;
852 	default:
853 		/* This ftype is undocumented type. */
854 		la_dosmaperr(GetLastError());
855 		return (-1);
856 	}
857 
858 	ZeroMemory(&info, sizeof(info));
859 	if (!GetFileInformationByHandle (handle, &info)) {
860 		la_dosmaperr(GetLastError());
861 		return (-1);
862 	}
863 
864 	mode = S_IRUSR | S_IRGRP | S_IROTH;
865 	if ((info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0)
866 		mode |= S_IWUSR | S_IWGRP | S_IWOTH;
867 	if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
868 		mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
869 	else
870 		mode |= S_IFREG;
871 	st->st_mode = mode;
872 
873 	fileTimeToUTC(&info.ftLastAccessTime, &time, &ns);
874 	st->st_atime = time;
875 	st->st_atime_nsec = ns;
876 	fileTimeToUTC(&info.ftLastWriteTime, &time, &ns);
877 	st->st_mtime = time;
878 	st->st_mtime_nsec = ns;
879 	fileTimeToUTC(&info.ftCreationTime, &time, &ns);
880 	st->st_ctime = time;
881 	st->st_ctime_nsec = ns;
882 	st->st_size =
883 	    ((int64_t)(info.nFileSizeHigh) * ((int64_t)MAXDWORD + 1))
884 		+ (int64_t)(info.nFileSizeLow);
885 #ifdef SIMULATE_WIN_STAT
886 	st->st_ino = 0;
887 	st->st_nlink = 1;
888 	st->st_dev = 0;
889 #else
890 	/* Getting FileIndex as i-node. We have to remove a sequence which
891 	 * is high-16-bits of nFileIndexHigh. */
892 	ino64.HighPart = info.nFileIndexHigh & 0x0000FFFFUL;
893 	ino64.LowPart  = info.nFileIndexLow;
894 	st->st_ino = ino64.QuadPart;
895 	st->st_nlink = info.nNumberOfLinks;
896 	if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
897 		++st->st_nlink;/* Add parent directory. */
898 	st->st_dev = info.dwVolumeSerialNumber;
899 #endif
900 	st->st_uid = 0;
901 	st->st_gid = 0;
902 	st->st_rdev = 0;
903 	return (0);
904 }
905 
906 static void
copy_stat(struct stat * st,struct ustat * us)907 copy_stat(struct stat *st, struct ustat *us)
908 {
909 	st->st_atime = us->st_atime;
910 	st->st_ctime = us->st_ctime;
911 	st->st_mtime = us->st_mtime;
912 	st->st_gid = us->st_gid;
913 	st->st_ino = getino(us);
914 	st->st_mode = us->st_mode;
915 	st->st_nlink = us->st_nlink;
916 	st->st_size = us->st_size;
917 	st->st_uid = us->st_uid;
918 	st->st_dev = us->st_dev;
919 	st->st_rdev = us->st_rdev;
920 }
921 
922 int
__la_fstat(int fd,struct stat * st)923 __la_fstat(int fd, struct stat *st)
924 {
925 	struct ustat u;
926 	int ret;
927 
928 	if (fd < 0) {
929 		errno = EBADF;
930 		return (-1);
931 	}
932 	ret = __hstat((HANDLE)_get_osfhandle(fd), &u);
933 	if (ret >= 0) {
934 		copy_stat(st, &u);
935 		if (u.st_mode & (S_IFCHR | S_IFIFO)) {
936 			st->st_dev = fd;
937 			st->st_rdev = fd;
938 		}
939 	}
940 	return (ret);
941 }
942 
943 int
__la_stat(const char * path,struct stat * st)944 __la_stat(const char *path, struct stat *st)
945 {
946 	HANDLE handle;
947 	struct ustat u;
948 	int ret;
949 
950 	handle = la_CreateFile(path, 0, 0, NULL, OPEN_EXISTING,
951 		FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_READONLY,
952 		NULL);
953 	if (handle == INVALID_HANDLE_VALUE) {
954 		la_dosmaperr(GetLastError());
955 		return (-1);
956 	}
957 	ret = __hstat(handle, &u);
958 	CloseHandle(handle);
959 	if (ret >= 0) {
960 		char *p;
961 
962 		copy_stat(st, &u);
963 		p = strrchr(path, '.');
964 		if (p != NULL && strlen(p) == 4) {
965 			char exttype[4];
966 
967 			++ p;
968 			exttype[0] = toupper(*p++);
969 			exttype[1] = toupper(*p++);
970 			exttype[2] = toupper(*p++);
971 			exttype[3] = '\0';
972 			if (!strcmp(exttype, "EXE") || !strcmp(exttype, "CMD") ||
973 				!strcmp(exttype, "BAT") || !strcmp(exttype, "COM"))
974 				st->st_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
975 		}
976 	}
977 	return (ret);
978 }
979 
980 int
__la_unlink(const char * path)981 __la_unlink(const char *path)
982 {
983 	wchar_t *ws;
984 	int r;
985 
986 	r = _unlink(path);
987 	if (r >= 0 || errno != ENOENT)
988 		return (r);
989 	ws = permissive_name(path);
990 	if (ws == NULL) {
991 		errno = EINVAL;
992 		return (-1);
993 	}
994 	r = _wunlink(ws);
995 	free(ws);
996 	return (r);
997 }
998 
999 /*
1000  * This waitpid is limited implemention.
1001  */
1002 pid_t
__la_waitpid(pid_t wpid,int * status,int option)1003 __la_waitpid(pid_t wpid, int *status, int option)
1004 {
1005 	HANDLE child;
1006 	DWORD cs, ret;
1007 
1008 	(void)option;/* UNUSED */
1009 	child = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, wpid);
1010 	if (child == NULL) {
1011 		la_dosmaperr(GetLastError());
1012 		return (-1);
1013 	}
1014 	ret = WaitForSingleObject(child, INFINITE);
1015 	if (ret == WAIT_FAILED) {
1016 		CloseHandle(child);
1017 		la_dosmaperr(GetLastError());
1018 		return (-1);
1019 	}
1020 	if (GetExitCodeProcess(child, &cs) == 0) {
1021 		CloseHandle(child);
1022 		la_dosmaperr(GetLastError());
1023 		return (-1);
1024 	}
1025 	if (cs == STILL_ACTIVE)
1026 		*status = 0x100;
1027 	else
1028 		*status = (int)(cs & 0xff);
1029 	CloseHandle(child);
1030 	return (wpid);
1031 }
1032 
1033 ssize_t
__la_write(int fd,const void * buf,size_t nbytes)1034 __la_write(int fd, const void *buf, size_t nbytes)
1035 {
1036 	DWORD bytes_written;
1037 
1038 #ifdef _WIN64
1039 	if (nbytes > UINT32_MAX)
1040 		nbytes = UINT32_MAX;
1041 #endif
1042 	if (fd < 0) {
1043 		errno = EBADF;
1044 		return (-1);
1045 	}
1046 	if (!WriteFile((HANDLE)_get_osfhandle(fd), buf, (uint32_t)nbytes,
1047 	    &bytes_written, NULL)) {
1048 		DWORD lasterr;
1049 
1050 		lasterr = GetLastError();
1051 		if (lasterr == ERROR_ACCESS_DENIED)
1052 			errno = EBADF;
1053 		else
1054 			la_dosmaperr(lasterr);
1055 		return (-1);
1056 	}
1057 	return (bytes_written);
1058 }
1059 
1060 /*
1061  * The following function was modified from PostgreSQL sources and is
1062  * subject to the copyright below.
1063  */
1064 /*-------------------------------------------------------------------------
1065  *
1066  * win32error.c
1067  *	  Map win32 error codes to errno values
1068  *
1069  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
1070  *
1071  * IDENTIFICATION
1072  *	  $PostgreSQL: pgsql/src/port/win32error.c,v 1.4 2008/01/01 19:46:00 momjian Exp $
1073  *
1074  *-------------------------------------------------------------------------
1075  */
1076 /*
1077 PostgreSQL Database Management System
1078 (formerly known as Postgres, then as Postgres95)
1079 
1080 Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
1081 
1082 Portions Copyright (c) 1994, The Regents of the University of California
1083 
1084 Permission to use, copy, modify, and distribute this software and its
1085 documentation for any purpose, without fee, and without a written agreement
1086 is hereby granted, provided that the above copyright notice and this
1087 paragraph and the following two paragraphs appear in all copies.
1088 
1089 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
1090 DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
1091 LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
1092 DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
1093 POSSIBILITY OF SUCH DAMAGE.
1094 
1095 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
1096 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
1097 AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
1098 ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
1099 PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1100 */
1101 
1102 static const struct {
1103 	DWORD		winerr;
1104 	int		doserr;
1105 } doserrors[] =
1106 {
1107 	{	ERROR_INVALID_FUNCTION, EINVAL	},
1108 	{	ERROR_FILE_NOT_FOUND, ENOENT	},
1109 	{	ERROR_PATH_NOT_FOUND, ENOENT	},
1110 	{	ERROR_TOO_MANY_OPEN_FILES, EMFILE	},
1111 	{	ERROR_ACCESS_DENIED, EACCES	},
1112 	{	ERROR_INVALID_HANDLE, EBADF	},
1113 	{	ERROR_ARENA_TRASHED, ENOMEM	},
1114 	{	ERROR_NOT_ENOUGH_MEMORY, ENOMEM	},
1115 	{	ERROR_INVALID_BLOCK, ENOMEM	},
1116 	{	ERROR_BAD_ENVIRONMENT, E2BIG	},
1117 	{	ERROR_BAD_FORMAT, ENOEXEC	},
1118 	{	ERROR_INVALID_ACCESS, EINVAL	},
1119 	{	ERROR_INVALID_DATA, EINVAL	},
1120 	{	ERROR_INVALID_DRIVE, ENOENT	},
1121 	{	ERROR_CURRENT_DIRECTORY, EACCES	},
1122 	{	ERROR_NOT_SAME_DEVICE, EXDEV	},
1123 	{	ERROR_NO_MORE_FILES, ENOENT	},
1124 	{	ERROR_LOCK_VIOLATION, EACCES	},
1125 	{	ERROR_SHARING_VIOLATION, EACCES	},
1126 	{	ERROR_BAD_NETPATH, ENOENT	},
1127 	{	ERROR_NETWORK_ACCESS_DENIED, EACCES	},
1128 	{	ERROR_BAD_NET_NAME, ENOENT	},
1129 	{	ERROR_FILE_EXISTS, EEXIST	},
1130 	{	ERROR_CANNOT_MAKE, EACCES	},
1131 	{	ERROR_FAIL_I24, EACCES	},
1132 	{	ERROR_INVALID_PARAMETER, EINVAL	},
1133 	{	ERROR_NO_PROC_SLOTS, EAGAIN	},
1134 	{	ERROR_DRIVE_LOCKED, EACCES	},
1135 	{	ERROR_BROKEN_PIPE, EPIPE	},
1136 	{	ERROR_DISK_FULL, ENOSPC	},
1137 	{	ERROR_INVALID_TARGET_HANDLE, EBADF	},
1138 	{	ERROR_INVALID_HANDLE, EINVAL	},
1139 	{	ERROR_WAIT_NO_CHILDREN, ECHILD	},
1140 	{	ERROR_CHILD_NOT_COMPLETE, ECHILD	},
1141 	{	ERROR_DIRECT_ACCESS_HANDLE, EBADF	},
1142 	{	ERROR_NEGATIVE_SEEK, EINVAL	},
1143 	{	ERROR_SEEK_ON_DEVICE, EACCES	},
1144 	{	ERROR_DIR_NOT_EMPTY, ENOTEMPTY	},
1145 	{	ERROR_NOT_LOCKED, EACCES	},
1146 	{	ERROR_BAD_PATHNAME, ENOENT	},
1147 	{	ERROR_MAX_THRDS_REACHED, EAGAIN	},
1148 	{	ERROR_LOCK_FAILED, EACCES	},
1149 	{	ERROR_ALREADY_EXISTS, EEXIST	},
1150 	{	ERROR_FILENAME_EXCED_RANGE, ENOENT	},
1151 	{	ERROR_NESTING_NOT_ALLOWED, EAGAIN	},
1152 	{	ERROR_NOT_ENOUGH_QUOTA, ENOMEM	}
1153 };
1154 
1155 static void
la_dosmaperr(unsigned long e)1156 la_dosmaperr(unsigned long e)
1157 {
1158 	int			i;
1159 
1160 	if (e == 0)
1161 	{
1162 		errno = 0;
1163 		return;
1164 	}
1165 
1166 	for (i = 0; i < sizeof(doserrors); i++)
1167 	{
1168 		if (doserrors[i].winerr == e)
1169 		{
1170 			errno = doserrors[i].doserr;
1171 			return;
1172 		}
1173 	}
1174 
1175 	/* fprintf(stderr, "unrecognized win32 error code: %lu", e); */
1176 	errno = EINVAL;
1177 	return;
1178 }
1179 
1180 #if defined(ARCHIVE_HASH_MD5_WIN)    ||\
1181     defined(ARCHIVE_HASH_SHA1_WIN)   || defined(ARCHIVE_HASH_SHA256_WIN) ||\
1182     defined(ARCHIVE_HASH_SHA384_WIN) || defined(ARCHIVE_HASH_SHA512_WIN)
1183 /*
1184  * Message digest functions.
1185  */
1186 void
__la_hash_Init(Digest_CTX * ctx,ALG_ID algId)1187 __la_hash_Init(Digest_CTX *ctx, ALG_ID algId)
1188 {
1189 
1190 	ctx->valid = 0;
1191 	if (!CryptAcquireContext(&ctx->cryptProv, NULL, NULL,
1192 	    PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
1193 		if (GetLastError() != (DWORD)NTE_BAD_KEYSET)
1194 			return;
1195 		if (!CryptAcquireContext(&ctx->cryptProv, NULL, NULL,
1196 		    PROV_RSA_FULL, CRYPT_NEWKEYSET))
1197 			return;
1198 	}
1199 
1200 	if (!CryptCreateHash(ctx->cryptProv, algId, 0, 0, &ctx->hash)) {
1201 		CryptReleaseContext(ctx->cryptProv, 0);
1202 		return;
1203 	}
1204 
1205 	ctx->valid = 1;
1206 }
1207 
1208 void
__la_hash_Update(Digest_CTX * ctx,const unsigned char * buf,size_t len)1209 __la_hash_Update(Digest_CTX *ctx, const unsigned char *buf, size_t len)
1210 {
1211 
1212 	if (!ctx->valid)
1213 	return;
1214 
1215 	CryptHashData(ctx->hash,
1216 		      (unsigned char *)(uintptr_t)buf,
1217 		      (DWORD)len, 0);
1218 }
1219 
1220 void
__la_hash_Final(unsigned char * buf,size_t bufsize,Digest_CTX * ctx)1221 __la_hash_Final(unsigned char *buf, size_t bufsize, Digest_CTX *ctx)
1222 {
1223 	DWORD siglen = bufsize;
1224 
1225 	if (!ctx->valid)
1226 		return;
1227 
1228 	CryptGetHashParam(ctx->hash, HP_HASHVAL, buf, &siglen, 0);
1229 	CryptDestroyHash(ctx->hash);
1230 	CryptReleaseContext(ctx->cryptProv, 0);
1231 	ctx->valid = 0;
1232 }
1233 
1234 #endif /* defined(ARCHIVE_HASH_*_WIN) */
1235 
1236 #endif /* _WIN32 && !__CYGWIN__ */
1237