1 /*-
2  * Copyright (c) 2009-2011 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_entry.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 <locale.h>
60 #include <process.h>
61 #include <stdlib.h>
62 #include <wchar.h>
63 #include <windows.h>
64 #include <share.h>
65 
66 #define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
67 
68 #if defined(__LA_LSEEK_NEEDED)
SetFilePointerEx_perso(HANDLE hFile,LARGE_INTEGER liDistanceToMove,PLARGE_INTEGER lpNewFilePointer,DWORD dwMoveMethod)69 static BOOL SetFilePointerEx_perso(HANDLE hFile,
70 				   LARGE_INTEGER liDistanceToMove,
71 				   PLARGE_INTEGER lpNewFilePointer,
72 				   DWORD dwMoveMethod)
73 {
74 	LARGE_INTEGER li;
75 	li.QuadPart = liDistanceToMove.QuadPart;
76 	li.LowPart = SetFilePointer(
77 		hFile, li.LowPart, &li.HighPart, dwMoveMethod);
78 	if(lpNewFilePointer) {
79 		lpNewFilePointer->QuadPart = li.QuadPart;
80 	}
81 	return li.LowPart != -1 || GetLastError() == NO_ERROR;
82 }
83 #endif
84 
85 struct ustat {
86 	int64_t		st_atime;
87 	uint32_t	st_atime_nsec;
88 	int64_t		st_ctime;
89 	uint32_t	st_ctime_nsec;
90 	int64_t		st_mtime;
91 	uint32_t	st_mtime_nsec;
92 	gid_t		st_gid;
93 	/* 64bits ino */
94 	int64_t		st_ino;
95 	mode_t		st_mode;
96 	uint32_t	st_nlink;
97 	uint64_t	st_size;
98 	uid_t		st_uid;
99 	dev_t		st_dev;
100 	dev_t		st_rdev;
101 };
102 
103 /* Transform 64-bits ino into 32-bits by hashing.
104  * You do not forget that really unique number size is 64-bits.
105  */
106 #define INOSIZE (8*sizeof(ino_t)) /* 32 */
107 static __inline ino_t
getino(struct ustat * ub)108 getino(struct ustat *ub)
109 {
110 	ULARGE_INTEGER ino64;
111 	ino64.QuadPart = ub->st_ino;
112 	/* I don't know this hashing is correct way */
113 	return ((ino_t)(ino64.LowPart ^ (ino64.LowPart >> INOSIZE)));
114 }
115 
116 /*
117  * Prepend "\\?\" to the path name and convert it to unicode to permit
118  * an extended-length path for a maximum total path length of 32767
119  * characters.
120  * see also http://msdn.microsoft.com/en-us/library/aa365247.aspx
121  */
122 wchar_t *
__la_win_permissive_name(const char * name)123 __la_win_permissive_name(const char *name)
124 {
125 	wchar_t *wn;
126 	wchar_t *ws;
127 	size_t ll;
128 
129 	ll = strlen(name);
130 	wn = malloc((ll + 1) * sizeof(wchar_t));
131 	if (wn == NULL)
132 		return (NULL);
133 	ll = mbstowcs(wn, name, ll);
134 	if (ll == (size_t)-1) {
135 		free(wn);
136 		return (NULL);
137 	}
138 	wn[ll] = L'\0';
139 	ws = __la_win_permissive_name_w(wn);
140 	free(wn);
141 	return (ws);
142 }
143 
144 wchar_t *
__la_win_permissive_name_w(const wchar_t * wname)145 __la_win_permissive_name_w(const wchar_t *wname)
146 {
147 	wchar_t *wn, *wnp;
148 	wchar_t *ws, *wsp;
149 	DWORD l, len, slen;
150 	int unc;
151 
152 	/* Get a full-pathname. */
153 	l = GetFullPathNameW(wname, 0, NULL, NULL);
154 	if (l == 0)
155 		return (NULL);
156 	/* NOTE: GetFullPathNameW has a bug that if the length of the file
157 	 * name is just 1 then it returns incomplete buffer size. Thus, we
158 	 * have to add three to the size to allocate a sufficient buffer
159 	 * size for the full-pathname of the file name. */
160 	l += 3;
161 	wnp = malloc(l * sizeof(wchar_t));
162 	if (wnp == NULL)
163 		return (NULL);
164 	len = GetFullPathNameW(wname, l, wnp, NULL);
165 	wn = wnp;
166 
167 	if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
168 	    wnp[2] == L'?' && wnp[3] == L'\\')
169 		/* We have already a permissive name. */
170 		return (wn);
171 
172 	if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
173 		wnp[2] == L'.' && wnp[3] == L'\\') {
174 		/* This is a device name */
175 		if (((wnp[4] >= L'a' && wnp[4] <= L'z') ||
176 		     (wnp[4] >= L'A' && wnp[4] <= L'Z')) &&
177 		    wnp[5] == L':' && wnp[6] == L'\\')
178 			wnp[2] = L'?';/* Not device name. */
179 		return (wn);
180 	}
181 
182 	unc = 0;
183 	if (wnp[0] == L'\\' && wnp[1] == L'\\' && wnp[2] != L'\\') {
184 		wchar_t *p = &wnp[2];
185 
186 		/* Skip server-name letters. */
187 		while (*p != L'\\' && *p != L'\0')
188 			++p;
189 		if (*p == L'\\') {
190 			wchar_t *rp = ++p;
191 			/* Skip share-name letters. */
192 			while (*p != L'\\' && *p != L'\0')
193 				++p;
194 			if (*p == L'\\' && p != rp) {
195 				/* Now, match patterns such as
196 				 * "\\server-name\share-name\" */
197 				wnp += 2;
198 				len -= 2;
199 				unc = 1;
200 			}
201 		}
202 	}
203 
204 	slen = 4 + (unc * 4) + len + 1;
205 	ws = wsp = malloc(slen * sizeof(wchar_t));
206 	if (ws == NULL) {
207 		free(wn);
208 		return (NULL);
209 	}
210 	/* prepend "\\?\" */
211 	wcsncpy(wsp, L"\\\\?\\", 4);
212 	wsp += 4;
213 	slen -= 4;
214 	if (unc) {
215 		/* append "UNC\" ---> "\\?\UNC\" */
216 		wcsncpy(wsp, L"UNC\\", 4);
217 		wsp += 4;
218 		slen -= 4;
219 	}
220 	wcsncpy(wsp, wnp, slen);
221 	wsp[slen - 1] = L'\0'; /* Ensure null termination. */
222 	free(wn);
223 	return (ws);
224 }
225 
226 /*
227  * Create a file handle.
228  * This can exceed MAX_PATH limitation.
229  */
230 static HANDLE
la_CreateFile(const char * path,DWORD dwDesiredAccess,DWORD dwShareMode,LPSECURITY_ATTRIBUTES lpSecurityAttributes,DWORD dwCreationDisposition,DWORD dwFlagsAndAttributes,HANDLE hTemplateFile)231 la_CreateFile(const char *path, DWORD dwDesiredAccess, DWORD dwShareMode,
232     LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
233     DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
234 {
235 	wchar_t *wpath;
236 	HANDLE handle;
237 
238 	handle = CreateFileA(path, dwDesiredAccess, dwShareMode,
239 	    lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
240 	    hTemplateFile);
241 	if (handle != INVALID_HANDLE_VALUE)
242 		return (handle);
243 	if (GetLastError() != ERROR_PATH_NOT_FOUND)
244 		return (handle);
245 	wpath = __la_win_permissive_name(path);
246 	if (wpath == NULL)
247 		return (handle);
248 	handle = CreateFileW(wpath, dwDesiredAccess, dwShareMode,
249 	    lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
250 	    hTemplateFile);
251 	free(wpath);
252 	return (handle);
253 }
254 
255 #if defined(__LA_LSEEK_NEEDED)
256 __int64
__la_lseek(int fd,__int64 offset,int whence)257 __la_lseek(int fd, __int64 offset, int whence)
258 {
259 	LARGE_INTEGER distance;
260 	LARGE_INTEGER newpointer;
261 	HANDLE handle;
262 
263 	if (fd < 0) {
264 		errno = EBADF;
265 		return (-1);
266 	}
267 	handle = (HANDLE)_get_osfhandle(fd);
268 	if (GetFileType(handle) != FILE_TYPE_DISK) {
269 		errno = EBADF;
270 		return (-1);
271 	}
272 	distance.QuadPart = offset;
273 	if (!SetFilePointerEx_perso(handle, distance, &newpointer, whence)) {
274 		DWORD lasterr;
275 
276 		lasterr = GetLastError();
277 		if (lasterr == ERROR_BROKEN_PIPE)
278 			return (0);
279 		if (lasterr == ERROR_ACCESS_DENIED)
280 			errno = EBADF;
281 		else
282 			la_dosmaperr(lasterr);
283 		return (-1);
284 	}
285 	return (newpointer.QuadPart);
286 }
287 #endif
288 
289 /* This can exceed MAX_PATH limitation. */
290 int
__la_open(const char * path,int flags,...)291 __la_open(const char *path, int flags, ...)
292 {
293 	va_list ap;
294 	wchar_t *ws;
295 	int r, pmode;
296 	DWORD attr;
297 
298 	va_start(ap, flags);
299 	pmode = va_arg(ap, int);
300 	va_end(ap);
301 	ws = NULL;
302 	if ((flags & ~O_BINARY) == O_RDONLY) {
303 		/*
304 		 * When we open a directory, _open function returns
305 		 * "Permission denied" error.
306 		 */
307 		attr = GetFileAttributesA(path);
308 		if (attr == (DWORD)-1 && GetLastError() == ERROR_PATH_NOT_FOUND) {
309 			ws = __la_win_permissive_name(path);
310 			if (ws == NULL) {
311 				errno = EINVAL;
312 				return (-1);
313 			}
314 			attr = GetFileAttributesW(ws);
315 		}
316 		if (attr == (DWORD)-1) {
317 			la_dosmaperr(GetLastError());
318 			free(ws);
319 			return (-1);
320 		}
321 		if (attr & FILE_ATTRIBUTE_DIRECTORY) {
322 			HANDLE handle;
323 
324 			if (ws != NULL)
325 				handle = CreateFileW(ws, 0, 0, NULL,
326 				    OPEN_EXISTING,
327 				    FILE_FLAG_BACKUP_SEMANTICS |
328 				    FILE_ATTRIBUTE_READONLY,
329 					NULL);
330 			else
331 				handle = CreateFileA(path, 0, 0, NULL,
332 				    OPEN_EXISTING,
333 				    FILE_FLAG_BACKUP_SEMANTICS |
334 				    FILE_ATTRIBUTE_READONLY,
335 					NULL);
336 			free(ws);
337 			if (handle == INVALID_HANDLE_VALUE) {
338 				la_dosmaperr(GetLastError());
339 				return (-1);
340 			}
341 			r = _open_osfhandle((intptr_t)handle, _O_RDONLY);
342 			return (r);
343 		}
344 	}
345 	if (ws == NULL) {
346 #if defined(__BORLANDC__)
347 		/* Borland has no mode argument.
348 		   TODO: Fix mode of new file.  */
349 		r = _open(path, flags);
350 #else
351 		r = _open(path, flags, pmode);
352 #endif
353 		if (r < 0 && errno == EACCES && (flags & O_CREAT) != 0) {
354 			/* Simulate other POSIX system action to pass our test suite. */
355 			attr = GetFileAttributesA(path);
356 			if (attr == (DWORD)-1)
357 				la_dosmaperr(GetLastError());
358 			else if (attr & FILE_ATTRIBUTE_DIRECTORY)
359 				errno = EISDIR;
360 			else
361 				errno = EACCES;
362 			return (-1);
363 		}
364 		if (r >= 0 || errno != ENOENT)
365 			return (r);
366 		ws = __la_win_permissive_name(path);
367 		if (ws == NULL) {
368 			errno = EINVAL;
369 			return (-1);
370 		}
371 	}
372 	r = _wopen(ws, flags, pmode);
373 	if (r < 0 && errno == EACCES && (flags & O_CREAT) != 0) {
374 		/* Simulate other POSIX system action to pass our test suite. */
375 		attr = GetFileAttributesW(ws);
376 		if (attr == (DWORD)-1)
377 			la_dosmaperr(GetLastError());
378 		else if (attr & FILE_ATTRIBUTE_DIRECTORY)
379 			errno = EISDIR;
380 		else
381 			errno = EACCES;
382 	}
383 	free(ws);
384 	return (r);
385 }
386 
387 ssize_t
__la_read(int fd,void * buf,size_t nbytes)388 __la_read(int fd, void *buf, size_t nbytes)
389 {
390 	HANDLE handle;
391 	DWORD bytes_read, lasterr;
392 	int r;
393 
394 #ifdef _WIN64
395 	if (nbytes > UINT32_MAX)
396 		nbytes = UINT32_MAX;
397 #endif
398 	if (fd < 0) {
399 		errno = EBADF;
400 		return (-1);
401 	}
402 	/* Do not pass 0 to third parameter of ReadFile(), read bytes.
403 	 * This will not return to application side. */
404 	if (nbytes == 0)
405 		return (0);
406 	handle = (HANDLE)_get_osfhandle(fd);
407 	r = ReadFile(handle, buf, (uint32_t)nbytes,
408 	    &bytes_read, NULL);
409 	if (r == 0) {
410 		lasterr = GetLastError();
411 		if (lasterr == ERROR_NO_DATA) {
412 			errno = EAGAIN;
413 			return (-1);
414 		}
415 		if (lasterr == ERROR_BROKEN_PIPE)
416 			return (0);
417 		if (lasterr == ERROR_ACCESS_DENIED)
418 			errno = EBADF;
419 		else
420 			la_dosmaperr(lasterr);
421 		return (-1);
422 	}
423 	return ((ssize_t)bytes_read);
424 }
425 
426 /* Convert Windows FILETIME to UTC */
427 __inline static void
fileTimeToUTC(const FILETIME * filetime,time_t * t,long * ns)428 fileTimeToUTC(const FILETIME *filetime, time_t *t, long *ns)
429 {
430 	ULARGE_INTEGER utc;
431 
432 	utc.HighPart = filetime->dwHighDateTime;
433 	utc.LowPart  = filetime->dwLowDateTime;
434 	if (utc.QuadPart >= EPOC_TIME) {
435 		utc.QuadPart -= EPOC_TIME;
436 		*t = (time_t)(utc.QuadPart / 10000000);	/* milli seconds base */
437 		*ns = (long)(utc.QuadPart % 10000000) * 100;/* nano seconds base */
438 	} else {
439 		*t = 0;
440 		*ns = 0;
441 	}
442 }
443 
444 /* Stat by handle
445  * Windows' stat() does not accept the path added "\\?\" especially "?"
446  * character.
447  * It means we cannot access the long name path longer than MAX_PATH.
448  * So I've implemented a function similar to Windows' stat() to access the
449  * long name path.
450  * And I've added some feature.
451  * 1. set st_ino by nFileIndexHigh and nFileIndexLow of
452  *    BY_HANDLE_FILE_INFORMATION.
453  * 2. set st_nlink by nNumberOfLinks of BY_HANDLE_FILE_INFORMATION.
454  * 3. set st_dev by dwVolumeSerialNumber by BY_HANDLE_FILE_INFORMATION.
455  */
456 static int
__hstat(HANDLE handle,struct ustat * st)457 __hstat(HANDLE handle, struct ustat *st)
458 {
459 	BY_HANDLE_FILE_INFORMATION info;
460 	ULARGE_INTEGER ino64;
461 	DWORD ftype;
462 	mode_t mode;
463 	time_t t;
464 	long ns;
465 
466 	switch (ftype = GetFileType(handle)) {
467 	case FILE_TYPE_UNKNOWN:
468 		errno = EBADF;
469 		return (-1);
470 	case FILE_TYPE_CHAR:
471 	case FILE_TYPE_PIPE:
472 		if (ftype == FILE_TYPE_CHAR) {
473 			st->st_mode = S_IFCHR;
474 			st->st_size = 0;
475 		} else {
476 			DWORD avail;
477 
478 			st->st_mode = S_IFIFO;
479 			if (PeekNamedPipe(handle, NULL, 0, NULL, &avail, NULL))
480 				st->st_size = avail;
481 			else
482 				st->st_size = 0;
483 		}
484 		st->st_atime = 0;
485 		st->st_atime_nsec = 0;
486 		st->st_mtime = 0;
487 		st->st_mtime_nsec = 0;
488 		st->st_ctime = 0;
489 		st->st_ctime_nsec = 0;
490 		st->st_ino = 0;
491 		st->st_nlink = 1;
492 		st->st_uid = 0;
493 		st->st_gid = 0;
494 		st->st_rdev = 0;
495 		st->st_dev = 0;
496 		return (0);
497 	case FILE_TYPE_DISK:
498 		break;
499 	default:
500 		/* This ftype is undocumented type. */
501 		la_dosmaperr(GetLastError());
502 		return (-1);
503 	}
504 
505 	ZeroMemory(&info, sizeof(info));
506 	if (!GetFileInformationByHandle (handle, &info)) {
507 		la_dosmaperr(GetLastError());
508 		return (-1);
509 	}
510 
511 	mode = S_IRUSR | S_IRGRP | S_IROTH;
512 	if ((info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0)
513 		mode |= S_IWUSR | S_IWGRP | S_IWOTH;
514 	if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
515 		mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
516 	else
517 		mode |= S_IFREG;
518 	st->st_mode = mode;
519 
520 	fileTimeToUTC(&info.ftLastAccessTime, &t, &ns);
521 	st->st_atime = t;
522 	st->st_atime_nsec = ns;
523 	fileTimeToUTC(&info.ftLastWriteTime, &t, &ns);
524 	st->st_mtime = t;
525 	st->st_mtime_nsec = ns;
526 	fileTimeToUTC(&info.ftCreationTime, &t, &ns);
527 	st->st_ctime = t;
528 	st->st_ctime_nsec = ns;
529 	st->st_size =
530 	    ((int64_t)(info.nFileSizeHigh) * ((int64_t)MAXDWORD + 1))
531 		+ (int64_t)(info.nFileSizeLow);
532 #ifdef SIMULATE_WIN_STAT
533 	st->st_ino = 0;
534 	st->st_nlink = 1;
535 	st->st_dev = 0;
536 #else
537 	/* Getting FileIndex as i-node. We should remove a sequence which
538 	 * is high-16-bits of nFileIndexHigh. */
539 	ino64.HighPart = info.nFileIndexHigh & 0x0000FFFFUL;
540 	ino64.LowPart  = info.nFileIndexLow;
541 	st->st_ino = ino64.QuadPart;
542 	st->st_nlink = info.nNumberOfLinks;
543 	if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
544 		++st->st_nlink;/* Add parent directory. */
545 	st->st_dev = info.dwVolumeSerialNumber;
546 #endif
547 	st->st_uid = 0;
548 	st->st_gid = 0;
549 	st->st_rdev = 0;
550 	return (0);
551 }
552 
553 static void
copy_stat(struct stat * st,struct ustat * us)554 copy_stat(struct stat *st, struct ustat *us)
555 {
556 	st->st_atime = us->st_atime;
557 	st->st_ctime = us->st_ctime;
558 	st->st_mtime = us->st_mtime;
559 	st->st_gid = us->st_gid;
560 	st->st_ino = getino(us);
561 	st->st_mode = us->st_mode;
562 	st->st_nlink = us->st_nlink;
563 	st->st_size = (off_t)us->st_size;
564 	st->st_uid = us->st_uid;
565 	st->st_dev = us->st_dev;
566 	st->st_rdev = us->st_rdev;
567 }
568 
569 /*
570  * TODO: Remove a use of __la_fstat and __la_stat.
571  * We should use GetFileInformationByHandle in place
572  * where We still use the *stat functions.
573  */
574 int
__la_fstat(int fd,struct stat * st)575 __la_fstat(int fd, struct stat *st)
576 {
577 	struct ustat u;
578 	int ret;
579 
580 	if (fd < 0) {
581 		errno = EBADF;
582 		return (-1);
583 	}
584 	ret = __hstat((HANDLE)_get_osfhandle(fd), &u);
585 	if (ret >= 0) {
586 		copy_stat(st, &u);
587 		if (u.st_mode & (S_IFCHR | S_IFIFO)) {
588 			st->st_dev = fd;
589 			st->st_rdev = fd;
590 		}
591 	}
592 	return (ret);
593 }
594 
595 /* This can exceed MAX_PATH limitation. */
596 int
__la_stat(const char * path,struct stat * st)597 __la_stat(const char *path, struct stat *st)
598 {
599 	HANDLE handle;
600 	struct ustat u;
601 	int ret;
602 
603 	handle = la_CreateFile(path, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
604 		FILE_FLAG_BACKUP_SEMANTICS,
605 		NULL);
606 	if (handle == INVALID_HANDLE_VALUE) {
607 		la_dosmaperr(GetLastError());
608 		return (-1);
609 	}
610 	ret = __hstat(handle, &u);
611 	CloseHandle(handle);
612 	if (ret >= 0) {
613 		char *p;
614 
615 		copy_stat(st, &u);
616 		p = strrchr(path, '.');
617 		if (p != NULL && strlen(p) == 4) {
618 			char exttype[4];
619 
620 			++ p;
621 			exttype[0] = toupper(*p++);
622 			exttype[1] = toupper(*p++);
623 			exttype[2] = toupper(*p++);
624 			exttype[3] = '\0';
625 			if (!strcmp(exttype, "EXE") || !strcmp(exttype, "CMD") ||
626 				!strcmp(exttype, "BAT") || !strcmp(exttype, "COM"))
627 				st->st_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
628 		}
629 	}
630 	return (ret);
631 }
632 
633 /*
634  * This waitpid is limited implementation.
635  */
636 pid_t
__la_waitpid(HANDLE child,int * status,int option)637 __la_waitpid(HANDLE child, int *status, int option)
638 {
639 	DWORD cs;
640 
641 	(void)option;/* UNUSED */
642 	do {
643 		if (GetExitCodeProcess(child, &cs) == 0) {
644 			CloseHandle(child);
645 			la_dosmaperr(GetLastError());
646 			*status = 0;
647 			return (-1);
648 		}
649 	} while (cs == STILL_ACTIVE);
650 
651 	*status = (int)(cs & 0xff);
652 	return (0);
653 }
654 
655 ssize_t
__la_write(int fd,const void * buf,size_t nbytes)656 __la_write(int fd, const void *buf, size_t nbytes)
657 {
658 	DWORD bytes_written;
659 
660 #ifdef _WIN64
661 	if (nbytes > UINT32_MAX)
662 		nbytes = UINT32_MAX;
663 #endif
664 	if (fd < 0) {
665 		errno = EBADF;
666 		return (-1);
667 	}
668 	if (!WriteFile((HANDLE)_get_osfhandle(fd), buf, (uint32_t)nbytes,
669 	    &bytes_written, NULL)) {
670 		DWORD lasterr;
671 
672 		lasterr = GetLastError();
673 		if (lasterr == ERROR_ACCESS_DENIED)
674 			errno = EBADF;
675 		else
676 			la_dosmaperr(lasterr);
677 		return (-1);
678 	}
679 	return (bytes_written);
680 }
681 
682 /*
683  * Replace the Windows path separator '\' with '/'.
684  */
685 static int
replace_pathseparator(struct archive_wstring * ws,const wchar_t * wp)686 replace_pathseparator(struct archive_wstring *ws, const wchar_t *wp)
687 {
688 	wchar_t *w;
689 	size_t path_length;
690 
691 	if (wp == NULL)
692 		return(0);
693 	if (wcschr(wp, L'\\') == NULL)
694 		return(0);
695 	path_length = wcslen(wp);
696 	if (archive_wstring_ensure(ws, path_length) == NULL)
697 		return(-1);
698 	archive_wstrncpy(ws, wp, path_length);
699 	for (w = ws->s; *w; w++) {
700 		if (*w == L'\\')
701 			*w = L'/';
702 	}
703 	return(1);
704 }
705 
706 static int
fix_pathseparator(struct archive_entry * entry)707 fix_pathseparator(struct archive_entry *entry)
708 {
709 	struct archive_wstring ws;
710 	const wchar_t *wp;
711 	int ret = ARCHIVE_OK;
712 
713 	archive_string_init(&ws);
714 	wp = archive_entry_pathname_w(entry);
715 	switch (replace_pathseparator(&ws, wp)) {
716 	case 0: /* Not replaced. */
717 		break;
718 	case 1: /* Replaced. */
719 		archive_entry_copy_pathname_w(entry, ws.s);
720 		break;
721 	default:
722 		ret = ARCHIVE_FAILED;
723 	}
724 	wp = archive_entry_hardlink_w(entry);
725 	switch (replace_pathseparator(&ws, wp)) {
726 	case 0: /* Not replaced. */
727 		break;
728 	case 1: /* Replaced. */
729 		archive_entry_copy_hardlink_w(entry, ws.s);
730 		break;
731 	default:
732 		ret = ARCHIVE_FAILED;
733 	}
734 	wp = archive_entry_symlink_w(entry);
735 	switch (replace_pathseparator(&ws, wp)) {
736 	case 0: /* Not replaced. */
737 		break;
738 	case 1: /* Replaced. */
739 		archive_entry_copy_symlink_w(entry, ws.s);
740 		break;
741 	default:
742 		ret = ARCHIVE_FAILED;
743 	}
744 	archive_wstring_free(&ws);
745 	return(ret);
746 }
747 
748 struct archive_entry *
__la_win_entry_in_posix_pathseparator(struct archive_entry * entry)749 __la_win_entry_in_posix_pathseparator(struct archive_entry *entry)
750 {
751 	struct archive_entry *entry_main;
752 	const wchar_t *wp;
753 	int has_backslash = 0;
754 	int ret;
755 
756 	wp = archive_entry_pathname_w(entry);
757 	if (wp != NULL && wcschr(wp, L'\\') != NULL)
758 		has_backslash = 1;
759 	if (!has_backslash) {
760 		wp = archive_entry_hardlink_w(entry);
761 		if (wp != NULL && wcschr(wp, L'\\') != NULL)
762 			has_backslash = 1;
763 	}
764 	if (!has_backslash) {
765 		wp = archive_entry_symlink_w(entry);
766 		if (wp != NULL && wcschr(wp, L'\\') != NULL)
767 			has_backslash = 1;
768 	}
769 	/*
770 	 * If there is no backslash chars, return the original.
771 	 */
772 	if (!has_backslash)
773 		return (entry);
774 
775 	/* Copy entry so we can modify it as needed. */
776 	entry_main = archive_entry_clone(entry);
777 	if (entry_main == NULL)
778 		return (NULL);
779 	/* Replace the Windows path-separator '\' with '/'. */
780 	ret = fix_pathseparator(entry_main);
781 	if (ret < ARCHIVE_WARN) {
782 		archive_entry_free(entry_main);
783 		return (NULL);
784 	}
785 	return (entry_main);
786 }
787 
788 
789 /*
790  * The following function was modified from PostgreSQL sources and is
791  * subject to the copyright below.
792  */
793 /*-------------------------------------------------------------------------
794  *
795  * win32error.c
796  *	  Map win32 error codes to errno values
797  *
798  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
799  *
800  * IDENTIFICATION
801  *	  $PostgreSQL: pgsql/src/port/win32error.c,v 1.4 2008/01/01 19:46:00 momjian Exp $
802  *
803  *-------------------------------------------------------------------------
804  */
805 /*
806 PostgreSQL Database Management System
807 (formerly known as Postgres, then as Postgres95)
808 
809 Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
810 
811 Portions Copyright (c) 1994, The Regents of the University of California
812 
813 Permission to use, copy, modify, and distribute this software and its
814 documentation for any purpose, without fee, and without a written agreement
815 is hereby granted, provided that the above copyright notice and this
816 paragraph and the following two paragraphs appear in all copies.
817 
818 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
819 DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
820 LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
821 DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
822 POSSIBILITY OF SUCH DAMAGE.
823 
824 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
825 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
826 AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
827 ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
828 PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
829 */
830 
831 static const struct {
832 	DWORD		winerr;
833 	int		doserr;
834 } doserrors[] =
835 {
836 	{	ERROR_INVALID_FUNCTION, EINVAL	},
837 	{	ERROR_FILE_NOT_FOUND, ENOENT	},
838 	{	ERROR_PATH_NOT_FOUND, ENOENT	},
839 	{	ERROR_TOO_MANY_OPEN_FILES, EMFILE	},
840 	{	ERROR_ACCESS_DENIED, EACCES	},
841 	{	ERROR_INVALID_HANDLE, EBADF	},
842 	{	ERROR_ARENA_TRASHED, ENOMEM	},
843 	{	ERROR_NOT_ENOUGH_MEMORY, ENOMEM	},
844 	{	ERROR_INVALID_BLOCK, ENOMEM	},
845 	{	ERROR_BAD_ENVIRONMENT, E2BIG	},
846 	{	ERROR_BAD_FORMAT, ENOEXEC	},
847 	{	ERROR_INVALID_ACCESS, EINVAL	},
848 	{	ERROR_INVALID_DATA, EINVAL	},
849 	{	ERROR_INVALID_DRIVE, ENOENT	},
850 	{	ERROR_CURRENT_DIRECTORY, EACCES	},
851 	{	ERROR_NOT_SAME_DEVICE, EXDEV	},
852 	{	ERROR_NO_MORE_FILES, ENOENT	},
853 	{	ERROR_LOCK_VIOLATION, EACCES	},
854 	{	ERROR_SHARING_VIOLATION, EACCES	},
855 	{	ERROR_BAD_NETPATH, ENOENT	},
856 	{	ERROR_NETWORK_ACCESS_DENIED, EACCES	},
857 	{	ERROR_BAD_NET_NAME, ENOENT	},
858 	{	ERROR_FILE_EXISTS, EEXIST	},
859 	{	ERROR_CANNOT_MAKE, EACCES	},
860 	{	ERROR_FAIL_I24, EACCES	},
861 	{	ERROR_INVALID_PARAMETER, EINVAL	},
862 	{	ERROR_NO_PROC_SLOTS, EAGAIN	},
863 	{	ERROR_DRIVE_LOCKED, EACCES	},
864 	{	ERROR_BROKEN_PIPE, EPIPE	},
865 	{	ERROR_DISK_FULL, ENOSPC	},
866 	{	ERROR_INVALID_TARGET_HANDLE, EBADF	},
867 	{	ERROR_INVALID_HANDLE, EINVAL	},
868 	{	ERROR_WAIT_NO_CHILDREN, ECHILD	},
869 	{	ERROR_CHILD_NOT_COMPLETE, ECHILD	},
870 	{	ERROR_DIRECT_ACCESS_HANDLE, EBADF	},
871 	{	ERROR_NEGATIVE_SEEK, EINVAL	},
872 	{	ERROR_SEEK_ON_DEVICE, EACCES	},
873 	{	ERROR_DIR_NOT_EMPTY, ENOTEMPTY	},
874 	{	ERROR_NOT_LOCKED, EACCES	},
875 	{	ERROR_BAD_PATHNAME, ENOENT	},
876 	{	ERROR_MAX_THRDS_REACHED, EAGAIN	},
877 	{	ERROR_LOCK_FAILED, EACCES	},
878 	{	ERROR_ALREADY_EXISTS, EEXIST	},
879 	{	ERROR_FILENAME_EXCED_RANGE, ENOENT	},
880 	{	ERROR_NESTING_NOT_ALLOWED, EAGAIN	},
881 	{	ERROR_NOT_ENOUGH_QUOTA, ENOMEM	}
882 };
883 
884 void
__la_dosmaperr(unsigned long e)885 __la_dosmaperr(unsigned long e)
886 {
887 	int			i;
888 
889 	if (e == 0)
890 	{
891 		errno = 0;
892 		return;
893 	}
894 
895 	for (i = 0; i < (int)(sizeof(doserrors)/sizeof(doserrors[0])); i++)
896 	{
897 		if (doserrors[i].winerr == e)
898 		{
899 			errno = doserrors[i].doserr;
900 			return;
901 		}
902 	}
903 
904 	/* fprintf(stderr, "unrecognized win32 error code: %lu", e); */
905 	errno = EINVAL;
906 	return;
907 }
908 
909 #endif /* _WIN32 && !__CYGWIN__ */
910