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 simular Windows' stat() to access the long name path.
449  * And I've added some feature.
450  * 1. set st_ino by nFileIndexHigh and nFileIndexLow of
451  *    BY_HANDLE_FILE_INFORMATION.
452  * 2. set st_nlink by nNumberOfLinks of BY_HANDLE_FILE_INFORMATION.
453  * 3. set st_dev by dwVolumeSerialNumber by BY_HANDLE_FILE_INFORMATION.
454  */
455 static int
__hstat(HANDLE handle,struct ustat * st)456 __hstat(HANDLE handle, struct ustat *st)
457 {
458 	BY_HANDLE_FILE_INFORMATION info;
459 	ULARGE_INTEGER ino64;
460 	DWORD ftype;
461 	mode_t mode;
462 	time_t t;
463 	long ns;
464 
465 	switch (ftype = GetFileType(handle)) {
466 	case FILE_TYPE_UNKNOWN:
467 		errno = EBADF;
468 		return (-1);
469 	case FILE_TYPE_CHAR:
470 	case FILE_TYPE_PIPE:
471 		if (ftype == FILE_TYPE_CHAR) {
472 			st->st_mode = S_IFCHR;
473 			st->st_size = 0;
474 		} else {
475 			DWORD avail;
476 
477 			st->st_mode = S_IFIFO;
478 			if (PeekNamedPipe(handle, NULL, 0, NULL, &avail, NULL))
479 				st->st_size = avail;
480 			else
481 				st->st_size = 0;
482 		}
483 		st->st_atime = 0;
484 		st->st_atime_nsec = 0;
485 		st->st_mtime = 0;
486 		st->st_mtime_nsec = 0;
487 		st->st_ctime = 0;
488 		st->st_ctime_nsec = 0;
489 		st->st_ino = 0;
490 		st->st_nlink = 1;
491 		st->st_uid = 0;
492 		st->st_gid = 0;
493 		st->st_rdev = 0;
494 		st->st_dev = 0;
495 		return (0);
496 	case FILE_TYPE_DISK:
497 		break;
498 	default:
499 		/* This ftype is undocumented type. */
500 		la_dosmaperr(GetLastError());
501 		return (-1);
502 	}
503 
504 	ZeroMemory(&info, sizeof(info));
505 	if (!GetFileInformationByHandle (handle, &info)) {
506 		la_dosmaperr(GetLastError());
507 		return (-1);
508 	}
509 
510 	mode = S_IRUSR | S_IRGRP | S_IROTH;
511 	if ((info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0)
512 		mode |= S_IWUSR | S_IWGRP | S_IWOTH;
513 	if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
514 		mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
515 	else
516 		mode |= S_IFREG;
517 	st->st_mode = mode;
518 
519 	fileTimeToUTC(&info.ftLastAccessTime, &t, &ns);
520 	st->st_atime = t;
521 	st->st_atime_nsec = ns;
522 	fileTimeToUTC(&info.ftLastWriteTime, &t, &ns);
523 	st->st_mtime = t;
524 	st->st_mtime_nsec = ns;
525 	fileTimeToUTC(&info.ftCreationTime, &t, &ns);
526 	st->st_ctime = t;
527 	st->st_ctime_nsec = ns;
528 	st->st_size =
529 	    ((int64_t)(info.nFileSizeHigh) * ((int64_t)MAXDWORD + 1))
530 		+ (int64_t)(info.nFileSizeLow);
531 #ifdef SIMULATE_WIN_STAT
532 	st->st_ino = 0;
533 	st->st_nlink = 1;
534 	st->st_dev = 0;
535 #else
536 	/* Getting FileIndex as i-node. We should remove a sequence which
537 	 * is high-16-bits of nFileIndexHigh. */
538 	ino64.HighPart = info.nFileIndexHigh & 0x0000FFFFUL;
539 	ino64.LowPart  = info.nFileIndexLow;
540 	st->st_ino = ino64.QuadPart;
541 	st->st_nlink = info.nNumberOfLinks;
542 	if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
543 		++st->st_nlink;/* Add parent directory. */
544 	st->st_dev = info.dwVolumeSerialNumber;
545 #endif
546 	st->st_uid = 0;
547 	st->st_gid = 0;
548 	st->st_rdev = 0;
549 	return (0);
550 }
551 
552 static void
copy_stat(struct stat * st,struct ustat * us)553 copy_stat(struct stat *st, struct ustat *us)
554 {
555 	st->st_atime = us->st_atime;
556 	st->st_ctime = us->st_ctime;
557 	st->st_mtime = us->st_mtime;
558 	st->st_gid = us->st_gid;
559 	st->st_ino = getino(us);
560 	st->st_mode = us->st_mode;
561 	st->st_nlink = us->st_nlink;
562 	st->st_size = (off_t)us->st_size;
563 	st->st_uid = us->st_uid;
564 	st->st_dev = us->st_dev;
565 	st->st_rdev = us->st_rdev;
566 }
567 
568 /*
569  * TODO: Remove a use of __la_fstat and __la_stat.
570  * We should use GetFileInformationByHandle in place
571  * where We still use the *stat functions.
572  */
573 int
__la_fstat(int fd,struct stat * st)574 __la_fstat(int fd, struct stat *st)
575 {
576 	struct ustat u;
577 	int ret;
578 
579 	if (fd < 0) {
580 		errno = EBADF;
581 		return (-1);
582 	}
583 	ret = __hstat((HANDLE)_get_osfhandle(fd), &u);
584 	if (ret >= 0) {
585 		copy_stat(st, &u);
586 		if (u.st_mode & (S_IFCHR | S_IFIFO)) {
587 			st->st_dev = fd;
588 			st->st_rdev = fd;
589 		}
590 	}
591 	return (ret);
592 }
593 
594 /* This can exceed MAX_PATH limitation. */
595 int
__la_stat(const char * path,struct stat * st)596 __la_stat(const char *path, struct stat *st)
597 {
598 	HANDLE handle;
599 	struct ustat u;
600 	int ret;
601 
602 	handle = la_CreateFile(path, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
603 		FILE_FLAG_BACKUP_SEMANTICS,
604 		NULL);
605 	if (handle == INVALID_HANDLE_VALUE) {
606 		la_dosmaperr(GetLastError());
607 		return (-1);
608 	}
609 	ret = __hstat(handle, &u);
610 	CloseHandle(handle);
611 	if (ret >= 0) {
612 		char *p;
613 
614 		copy_stat(st, &u);
615 		p = strrchr(path, '.');
616 		if (p != NULL && strlen(p) == 4) {
617 			char exttype[4];
618 
619 			++ p;
620 			exttype[0] = toupper(*p++);
621 			exttype[1] = toupper(*p++);
622 			exttype[2] = toupper(*p++);
623 			exttype[3] = '\0';
624 			if (!strcmp(exttype, "EXE") || !strcmp(exttype, "CMD") ||
625 				!strcmp(exttype, "BAT") || !strcmp(exttype, "COM"))
626 				st->st_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
627 		}
628 	}
629 	return (ret);
630 }
631 
632 /*
633  * This waitpid is limited implementation.
634  */
635 pid_t
__la_waitpid(HANDLE child,int * status,int option)636 __la_waitpid(HANDLE child, int *status, int option)
637 {
638 	DWORD cs;
639 
640 	(void)option;/* UNUSED */
641 	do {
642 		if (GetExitCodeProcess(child, &cs) == 0) {
643 			CloseHandle(child);
644 			la_dosmaperr(GetLastError());
645 			*status = 0;
646 			return (-1);
647 		}
648 	} while (cs == STILL_ACTIVE);
649 
650 	*status = (int)(cs & 0xff);
651 	return (0);
652 }
653 
654 ssize_t
__la_write(int fd,const void * buf,size_t nbytes)655 __la_write(int fd, const void *buf, size_t nbytes)
656 {
657 	DWORD bytes_written;
658 
659 #ifdef _WIN64
660 	if (nbytes > UINT32_MAX)
661 		nbytes = UINT32_MAX;
662 #endif
663 	if (fd < 0) {
664 		errno = EBADF;
665 		return (-1);
666 	}
667 	if (!WriteFile((HANDLE)_get_osfhandle(fd), buf, (uint32_t)nbytes,
668 	    &bytes_written, NULL)) {
669 		DWORD lasterr;
670 
671 		lasterr = GetLastError();
672 		if (lasterr == ERROR_ACCESS_DENIED)
673 			errno = EBADF;
674 		else
675 			la_dosmaperr(lasterr);
676 		return (-1);
677 	}
678 	return (bytes_written);
679 }
680 
681 /*
682  * Replace the Windows path separator '\' with '/'.
683  */
684 static int
replace_pathseparator(struct archive_wstring * ws,const wchar_t * wp)685 replace_pathseparator(struct archive_wstring *ws, const wchar_t *wp)
686 {
687 	wchar_t *w;
688 	size_t path_length;
689 
690 	if (wp == NULL)
691 		return(0);
692 	if (wcschr(wp, L'\\') == NULL)
693 		return(0);
694 	path_length = wcslen(wp);
695 	if (archive_wstring_ensure(ws, path_length) == NULL)
696 		return(-1);
697 	archive_wstrncpy(ws, wp, path_length);
698 	for (w = ws->s; *w; w++) {
699 		if (*w == L'\\')
700 			*w = L'/';
701 	}
702 	return(1);
703 }
704 
705 static int
fix_pathseparator(struct archive_entry * entry)706 fix_pathseparator(struct archive_entry *entry)
707 {
708 	struct archive_wstring ws;
709 	const wchar_t *wp;
710 	int ret = ARCHIVE_OK;
711 
712 	archive_string_init(&ws);
713 	wp = archive_entry_pathname_w(entry);
714 	switch (replace_pathseparator(&ws, wp)) {
715 	case 0: /* Not replaced. */
716 		break;
717 	case 1: /* Replaced. */
718 		archive_entry_copy_pathname_w(entry, ws.s);
719 		break;
720 	default:
721 		ret = ARCHIVE_FAILED;
722 	}
723 	wp = archive_entry_hardlink_w(entry);
724 	switch (replace_pathseparator(&ws, wp)) {
725 	case 0: /* Not replaced. */
726 		break;
727 	case 1: /* Replaced. */
728 		archive_entry_copy_hardlink_w(entry, ws.s);
729 		break;
730 	default:
731 		ret = ARCHIVE_FAILED;
732 	}
733 	wp = archive_entry_symlink_w(entry);
734 	switch (replace_pathseparator(&ws, wp)) {
735 	case 0: /* Not replaced. */
736 		break;
737 	case 1: /* Replaced. */
738 		archive_entry_copy_symlink_w(entry, ws.s);
739 		break;
740 	default:
741 		ret = ARCHIVE_FAILED;
742 	}
743 	archive_wstring_free(&ws);
744 	return(ret);
745 }
746 
747 struct archive_entry *
__la_win_entry_in_posix_pathseparator(struct archive_entry * entry)748 __la_win_entry_in_posix_pathseparator(struct archive_entry *entry)
749 {
750 	struct archive_entry *entry_main;
751 	const wchar_t *wp;
752 	int has_backslash = 0;
753 	int ret;
754 
755 	wp = archive_entry_pathname_w(entry);
756 	if (wp != NULL && wcschr(wp, L'\\') != NULL)
757 		has_backslash = 1;
758 	if (!has_backslash) {
759 		wp = archive_entry_hardlink_w(entry);
760 		if (wp != NULL && wcschr(wp, L'\\') != NULL)
761 			has_backslash = 1;
762 	}
763 	if (!has_backslash) {
764 		wp = archive_entry_symlink_w(entry);
765 		if (wp != NULL && wcschr(wp, L'\\') != NULL)
766 			has_backslash = 1;
767 	}
768 	/*
769 	 * If there is no backslash chars, return the original.
770 	 */
771 	if (!has_backslash)
772 		return (entry);
773 
774 	/* Copy entry so we can modify it as needed. */
775 	entry_main = archive_entry_clone(entry);
776 	if (entry_main == NULL)
777 		return (NULL);
778 	/* Replace the Windows path-separator '\' with '/'. */
779 	ret = fix_pathseparator(entry_main);
780 	if (ret < ARCHIVE_WARN) {
781 		archive_entry_free(entry_main);
782 		return (NULL);
783 	}
784 	return (entry_main);
785 }
786 
787 
788 /*
789  * The following function was modified from PostgreSQL sources and is
790  * subject to the copyright below.
791  */
792 /*-------------------------------------------------------------------------
793  *
794  * win32error.c
795  *	  Map win32 error codes to errno values
796  *
797  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
798  *
799  * IDENTIFICATION
800  *	  $PostgreSQL: pgsql/src/port/win32error.c,v 1.4 2008/01/01 19:46:00 momjian Exp $
801  *
802  *-------------------------------------------------------------------------
803  */
804 /*
805 PostgreSQL Database Management System
806 (formerly known as Postgres, then as Postgres95)
807 
808 Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
809 
810 Portions Copyright (c) 1994, The Regents of the University of California
811 
812 Permission to use, copy, modify, and distribute this software and its
813 documentation for any purpose, without fee, and without a written agreement
814 is hereby granted, provided that the above copyright notice and this
815 paragraph and the following two paragraphs appear in all copies.
816 
817 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
818 DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
819 LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
820 DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
821 POSSIBILITY OF SUCH DAMAGE.
822 
823 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
824 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
825 AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
826 ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
827 PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
828 */
829 
830 static const struct {
831 	DWORD		winerr;
832 	int		doserr;
833 } doserrors[] =
834 {
835 	{	ERROR_INVALID_FUNCTION, EINVAL	},
836 	{	ERROR_FILE_NOT_FOUND, ENOENT	},
837 	{	ERROR_PATH_NOT_FOUND, ENOENT	},
838 	{	ERROR_TOO_MANY_OPEN_FILES, EMFILE	},
839 	{	ERROR_ACCESS_DENIED, EACCES	},
840 	{	ERROR_INVALID_HANDLE, EBADF	},
841 	{	ERROR_ARENA_TRASHED, ENOMEM	},
842 	{	ERROR_NOT_ENOUGH_MEMORY, ENOMEM	},
843 	{	ERROR_INVALID_BLOCK, ENOMEM	},
844 	{	ERROR_BAD_ENVIRONMENT, E2BIG	},
845 	{	ERROR_BAD_FORMAT, ENOEXEC	},
846 	{	ERROR_INVALID_ACCESS, EINVAL	},
847 	{	ERROR_INVALID_DATA, EINVAL	},
848 	{	ERROR_INVALID_DRIVE, ENOENT	},
849 	{	ERROR_CURRENT_DIRECTORY, EACCES	},
850 	{	ERROR_NOT_SAME_DEVICE, EXDEV	},
851 	{	ERROR_NO_MORE_FILES, ENOENT	},
852 	{	ERROR_LOCK_VIOLATION, EACCES	},
853 	{	ERROR_SHARING_VIOLATION, EACCES	},
854 	{	ERROR_BAD_NETPATH, ENOENT	},
855 	{	ERROR_NETWORK_ACCESS_DENIED, EACCES	},
856 	{	ERROR_BAD_NET_NAME, ENOENT	},
857 	{	ERROR_FILE_EXISTS, EEXIST	},
858 	{	ERROR_CANNOT_MAKE, EACCES	},
859 	{	ERROR_FAIL_I24, EACCES	},
860 	{	ERROR_INVALID_PARAMETER, EINVAL	},
861 	{	ERROR_NO_PROC_SLOTS, EAGAIN	},
862 	{	ERROR_DRIVE_LOCKED, EACCES	},
863 	{	ERROR_BROKEN_PIPE, EPIPE	},
864 	{	ERROR_DISK_FULL, ENOSPC	},
865 	{	ERROR_INVALID_TARGET_HANDLE, EBADF	},
866 	{	ERROR_INVALID_HANDLE, EINVAL	},
867 	{	ERROR_WAIT_NO_CHILDREN, ECHILD	},
868 	{	ERROR_CHILD_NOT_COMPLETE, ECHILD	},
869 	{	ERROR_DIRECT_ACCESS_HANDLE, EBADF	},
870 	{	ERROR_NEGATIVE_SEEK, EINVAL	},
871 	{	ERROR_SEEK_ON_DEVICE, EACCES	},
872 	{	ERROR_DIR_NOT_EMPTY, ENOTEMPTY	},
873 	{	ERROR_NOT_LOCKED, EACCES	},
874 	{	ERROR_BAD_PATHNAME, ENOENT	},
875 	{	ERROR_MAX_THRDS_REACHED, EAGAIN	},
876 	{	ERROR_LOCK_FAILED, EACCES	},
877 	{	ERROR_ALREADY_EXISTS, EEXIST	},
878 	{	ERROR_FILENAME_EXCED_RANGE, ENOENT	},
879 	{	ERROR_NESTING_NOT_ALLOWED, EAGAIN	},
880 	{	ERROR_NOT_ENOUGH_QUOTA, ENOMEM	}
881 };
882 
883 void
__la_dosmaperr(unsigned long e)884 __la_dosmaperr(unsigned long e)
885 {
886 	int			i;
887 
888 	if (e == 0)
889 	{
890 		errno = 0;
891 		return;
892 	}
893 
894 	for (i = 0; i < (int)(sizeof(doserrors)/sizeof(doserrors[0])); i++)
895 	{
896 		if (doserrors[i].winerr == e)
897 		{
898 			errno = doserrors[i].doserr;
899 			return;
900 		}
901 	}
902 
903 	/* fprintf(stderr, "unrecognized win32 error code: %lu", e); */
904 	errno = EINVAL;
905 	return;
906 }
907 
908 #endif /* _WIN32 && !__CYGWIN__ */
909