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