xref: /netbsd-src/external/bsd/libarchive/dist/libarchive/archive_windows.c (revision 65e637ab3a9cc7c3e7749c941a1011ecd65517e6)
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_private.h"
49 #include "archive_entry.h"
50 #include <ctype.h>
51 #include <errno.h>
52 #include <stddef.h>
53 #ifdef HAVE_SYS_UTIME_H
54 #include <sys/utime.h>
55 #endif
56 #include <sys/stat.h>
57 #include <locale.h>
58 #include <process.h>
59 #include <stdlib.h>
60 #include <wchar.h>
61 #include <windows.h>
62 #include <share.h>
63 
64 #define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
65 
66 #if defined(__LA_LSEEK_NEEDED)
SetFilePointerEx_perso(HANDLE hFile,LARGE_INTEGER liDistanceToMove,PLARGE_INTEGER lpNewFilePointer,DWORD dwMoveMethod)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
getino(struct ustat * ub)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 *
__la_win_permissive_name(const char * name)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 *
__la_win_permissive_name_w(const wchar_t * wname)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
la_CreateFile(const char * path,DWORD dwDesiredAccess,DWORD dwShareMode,LPSECURITY_ATTRIBUTES lpSecurityAttributes,DWORD dwCreationDisposition,DWORD dwFlagsAndAttributes,HANDLE hTemplateFile)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 & 0x000F00000;
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
__la_lseek(int fd,__int64 offset,int whence)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
__la_open(const char * path,int flags,...)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 	if ((flags & ~O_BINARY) == O_RDONLY) {
318 		/*
319 		 * When we open a directory, _open function returns
320 		 * "Permission denied" error.
321 		 */
322 		attr = GetFileAttributesA(path);
323 #if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
324 		if (attr == (DWORD)-1 && GetLastError() == ERROR_PATH_NOT_FOUND)
325 #endif
326 		{
327 			ws = __la_win_permissive_name(path);
328 			if (ws == NULL) {
329 				errno = EINVAL;
330 				return (-1);
331 			}
332 			attr = GetFileAttributesW(ws);
333 		}
334 		if (attr == (DWORD)-1) {
335 			la_dosmaperr(GetLastError());
336 			free(ws);
337 			return (-1);
338 		}
339 		if (attr & FILE_ATTRIBUTE_DIRECTORY) {
340 			HANDLE handle;
341 #if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
342 			if (ws != NULL)
343 				handle = CreateFileW(ws, 0, 0, NULL,
344 				    OPEN_EXISTING,
345 				    FILE_FLAG_BACKUP_SEMANTICS |
346 				    FILE_ATTRIBUTE_READONLY,
347 					NULL);
348 			else
349 				handle = CreateFileA(path, 0, 0, NULL,
350 				    OPEN_EXISTING,
351 				    FILE_FLAG_BACKUP_SEMANTICS |
352 				    FILE_ATTRIBUTE_READONLY,
353 					NULL);
354 #else /* !WINAPI_PARTITION_DESKTOP */
355 			CREATEFILE2_EXTENDED_PARAMETERS createExParams;
356 			ZeroMemory(&createExParams, sizeof(createExParams));
357 			createExParams.dwSize = sizeof(createExParams);
358 			createExParams.dwFileAttributes = FILE_ATTRIBUTE_READONLY;
359 			createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
360 			handle = CreateFile2(ws, 0, 0,
361 				OPEN_EXISTING, &createExParams);
362 #endif /* !WINAPI_PARTITION_DESKTOP */
363 			free(ws);
364 			if (handle == INVALID_HANDLE_VALUE) {
365 				la_dosmaperr(GetLastError());
366 				return (-1);
367 			}
368 			r = _open_osfhandle((intptr_t)handle, _O_RDONLY);
369 			return (r);
370 		}
371 	}
372 	if (ws == NULL) {
373 #if defined(__BORLANDC__)
374 		/* Borland has no mode argument.
375 		   TODO: Fix mode of new file.  */
376 		r = _open(path, flags);
377 #else
378 		r = _open(path, flags, pmode);
379 #endif
380 		if (r < 0 && errno == EACCES && (flags & O_CREAT) != 0) {
381 			/* Simulate other POSIX system action to pass our test suite. */
382 			attr = GetFileAttributesA(path);
383 			if (attr == (DWORD)-1)
384 				la_dosmaperr(GetLastError());
385 			else if (attr & FILE_ATTRIBUTE_DIRECTORY)
386 				errno = EISDIR;
387 			else
388 				errno = EACCES;
389 			return (-1);
390 		}
391 		if (r >= 0 || errno != ENOENT)
392 			return (r);
393 		ws = __la_win_permissive_name(path);
394 		if (ws == NULL) {
395 			errno = EINVAL;
396 			return (-1);
397 		}
398 	}
399 	r = _wopen(ws, flags, pmode);
400 	if (r < 0 && errno == EACCES && (flags & O_CREAT) != 0) {
401 		/* Simulate other POSIX system action to pass our test suite. */
402 		attr = GetFileAttributesW(ws);
403 		if (attr == (DWORD)-1)
404 			la_dosmaperr(GetLastError());
405 		else if (attr & FILE_ATTRIBUTE_DIRECTORY)
406 			errno = EISDIR;
407 		else
408 			errno = EACCES;
409 	}
410 	free(ws);
411 	return (r);
412 }
413 
414 ssize_t
__la_read(int fd,void * buf,size_t nbytes)415 __la_read(int fd, void *buf, size_t nbytes)
416 {
417 	HANDLE handle;
418 	DWORD bytes_read, lasterr;
419 	int r;
420 
421 #ifdef _WIN64
422 	if (nbytes > UINT32_MAX)
423 		nbytes = UINT32_MAX;
424 #endif
425 	if (fd < 0) {
426 		errno = EBADF;
427 		return (-1);
428 	}
429 	/* Do not pass 0 to third parameter of ReadFile(), read bytes.
430 	 * This will not return to application side. */
431 	if (nbytes == 0)
432 		return (0);
433 	handle = (HANDLE)_get_osfhandle(fd);
434 	r = ReadFile(handle, buf, (uint32_t)nbytes,
435 	    &bytes_read, NULL);
436 	if (r == 0) {
437 		lasterr = GetLastError();
438 		if (lasterr == ERROR_NO_DATA) {
439 			errno = EAGAIN;
440 			return (-1);
441 		}
442 		if (lasterr == ERROR_BROKEN_PIPE)
443 			return (0);
444 		if (lasterr == ERROR_ACCESS_DENIED)
445 			errno = EBADF;
446 		else
447 			la_dosmaperr(lasterr);
448 		return (-1);
449 	}
450 	return ((ssize_t)bytes_read);
451 }
452 
453 /* Convert Windows FILETIME to UTC */
454 __inline static void
fileTimeToUTC(const FILETIME * filetime,time_t * t,long * ns)455 fileTimeToUTC(const FILETIME *filetime, time_t *t, long *ns)
456 {
457 	ULARGE_INTEGER utc;
458 
459 	utc.HighPart = filetime->dwHighDateTime;
460 	utc.LowPart  = filetime->dwLowDateTime;
461 	if (utc.QuadPart >= EPOC_TIME) {
462 		utc.QuadPart -= EPOC_TIME;
463 		*t = (time_t)(utc.QuadPart / 10000000);	/* milli seconds base */
464 		*ns = (long)(utc.QuadPart % 10000000) * 100;/* nano seconds base */
465 	} else {
466 		*t = 0;
467 		*ns = 0;
468 	}
469 }
470 
471 /* Stat by handle
472  * Windows' stat() does not accept the path added "\\?\" especially "?"
473  * character.
474  * It means we cannot access the long name path longer than MAX_PATH.
475  * So I've implemented a function similar to Windows' stat() to access the
476  * long name path.
477  * And I've added some feature.
478  * 1. set st_ino by nFileIndexHigh and nFileIndexLow of
479  *    BY_HANDLE_FILE_INFORMATION.
480  * 2. set st_nlink by nNumberOfLinks of BY_HANDLE_FILE_INFORMATION.
481  * 3. set st_dev by dwVolumeSerialNumber by BY_HANDLE_FILE_INFORMATION.
482  */
483 static int
__hstat(HANDLE handle,struct ustat * st)484 __hstat(HANDLE handle, struct ustat *st)
485 {
486 	BY_HANDLE_FILE_INFORMATION info;
487 	ULARGE_INTEGER ino64;
488 	DWORD ftype;
489 	mode_t mode;
490 	time_t t;
491 	long ns;
492 
493 	switch (ftype = GetFileType(handle)) {
494 	case FILE_TYPE_UNKNOWN:
495 		errno = EBADF;
496 		return (-1);
497 	case FILE_TYPE_CHAR:
498 	case FILE_TYPE_PIPE:
499 		if (ftype == FILE_TYPE_CHAR) {
500 			st->st_mode = S_IFCHR;
501 			st->st_size = 0;
502 		} else {
503 			DWORD avail;
504 
505 			st->st_mode = S_IFIFO;
506 			if (PeekNamedPipe(handle, NULL, 0, NULL, &avail, NULL))
507 				st->st_size = avail;
508 			else
509 				st->st_size = 0;
510 		}
511 		st->st_atime = 0;
512 		st->st_atime_nsec = 0;
513 		st->st_mtime = 0;
514 		st->st_mtime_nsec = 0;
515 		st->st_ctime = 0;
516 		st->st_ctime_nsec = 0;
517 		st->st_ino = 0;
518 		st->st_nlink = 1;
519 		st->st_uid = 0;
520 		st->st_gid = 0;
521 		st->st_rdev = 0;
522 		st->st_dev = 0;
523 		return (0);
524 	case FILE_TYPE_DISK:
525 		break;
526 	default:
527 		/* This ftype is undocumented type. */
528 		la_dosmaperr(GetLastError());
529 		return (-1);
530 	}
531 
532 	ZeroMemory(&info, sizeof(info));
533 	if (!GetFileInformationByHandle (handle, &info)) {
534 		la_dosmaperr(GetLastError());
535 		return (-1);
536 	}
537 
538 	mode = S_IRUSR | S_IRGRP | S_IROTH;
539 	if ((info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0)
540 		mode |= S_IWUSR | S_IWGRP | S_IWOTH;
541 	if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
542 		mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
543 	else
544 		mode |= S_IFREG;
545 	st->st_mode = mode;
546 
547 	fileTimeToUTC(&info.ftLastAccessTime, &t, &ns);
548 	st->st_atime = t;
549 	st->st_atime_nsec = ns;
550 	fileTimeToUTC(&info.ftLastWriteTime, &t, &ns);
551 	st->st_mtime = t;
552 	st->st_mtime_nsec = ns;
553 	fileTimeToUTC(&info.ftCreationTime, &t, &ns);
554 	st->st_ctime = t;
555 	st->st_ctime_nsec = ns;
556 	st->st_size =
557 	    ((int64_t)(info.nFileSizeHigh) * ((int64_t)MAXDWORD + 1))
558 		+ (int64_t)(info.nFileSizeLow);
559 #ifdef SIMULATE_WIN_STAT
560 	st->st_ino = 0;
561 	st->st_nlink = 1;
562 	st->st_dev = 0;
563 #else
564 	/* Getting FileIndex as i-node. We should remove a sequence which
565 	 * is high-16-bits of nFileIndexHigh. */
566 	ino64.HighPart = info.nFileIndexHigh & 0x0000FFFFUL;
567 	ino64.LowPart  = info.nFileIndexLow;
568 	st->st_ino = ino64.QuadPart;
569 	st->st_nlink = info.nNumberOfLinks;
570 	if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
571 		++st->st_nlink;/* Add parent directory. */
572 	st->st_dev = info.dwVolumeSerialNumber;
573 #endif
574 	st->st_uid = 0;
575 	st->st_gid = 0;
576 	st->st_rdev = 0;
577 	return (0);
578 }
579 
580 static void
copy_stat(struct stat * st,struct ustat * us)581 copy_stat(struct stat *st, struct ustat *us)
582 {
583 	st->st_atime = us->st_atime;
584 	st->st_ctime = us->st_ctime;
585 	st->st_mtime = us->st_mtime;
586 	st->st_gid = us->st_gid;
587 	st->st_ino = getino(us);
588 	st->st_mode = us->st_mode;
589 	st->st_nlink = us->st_nlink;
590 	st->st_size = (off_t)us->st_size;
591 	st->st_uid = us->st_uid;
592 	st->st_dev = us->st_dev;
593 	st->st_rdev = us->st_rdev;
594 }
595 
596 /*
597  * TODO: Remove a use of __la_fstat and __la_stat.
598  * We should use GetFileInformationByHandle in place
599  * where We still use the *stat functions.
600  */
601 int
__la_fstat(int fd,struct stat * st)602 __la_fstat(int fd, struct stat *st)
603 {
604 	struct ustat u;
605 	int ret;
606 
607 	if (fd < 0) {
608 		errno = EBADF;
609 		return (-1);
610 	}
611 	ret = __hstat((HANDLE)_get_osfhandle(fd), &u);
612 	if (ret >= 0) {
613 		copy_stat(st, &u);
614 		if (u.st_mode & (S_IFCHR | S_IFIFO)) {
615 			st->st_dev = fd;
616 			st->st_rdev = fd;
617 		}
618 	}
619 	return (ret);
620 }
621 
622 /* This can exceed MAX_PATH limitation. */
623 int
__la_stat(const char * path,struct stat * st)624 __la_stat(const char *path, struct stat *st)
625 {
626 	HANDLE handle;
627 	struct ustat u;
628 	int ret;
629 
630 	handle = la_CreateFile(path, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
631 		FILE_FLAG_BACKUP_SEMANTICS,
632 		NULL);
633 	if (handle == INVALID_HANDLE_VALUE) {
634 		la_dosmaperr(GetLastError());
635 		return (-1);
636 	}
637 	ret = __hstat(handle, &u);
638 	CloseHandle(handle);
639 	if (ret >= 0) {
640 		char *p;
641 
642 		copy_stat(st, &u);
643 		p = strrchr(path, '.');
644 		if (p != NULL && strlen(p) == 4) {
645 			char exttype[4];
646 
647 			++ p;
648 			exttype[0] = toupper(*p++);
649 			exttype[1] = toupper(*p++);
650 			exttype[2] = toupper(*p++);
651 			exttype[3] = '\0';
652 			if (!strcmp(exttype, "EXE") || !strcmp(exttype, "CMD") ||
653 				!strcmp(exttype, "BAT") || !strcmp(exttype, "COM"))
654 				st->st_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
655 		}
656 	}
657 	return (ret);
658 }
659 
660 /*
661  * This waitpid is limited implementation.
662  */
663 pid_t
__la_waitpid(HANDLE child,int * status,int option)664 __la_waitpid(HANDLE child, int *status, int option)
665 {
666 	DWORD cs;
667 
668 	(void)option;/* UNUSED */
669 	do {
670 		if (GetExitCodeProcess(child, &cs) == 0) {
671 			CloseHandle(child);
672 			la_dosmaperr(GetLastError());
673 			*status = 0;
674 			return (-1);
675 		}
676 	} while (cs == STILL_ACTIVE);
677 
678 	*status = (int)(cs & 0xff);
679 	return (0);
680 }
681 
682 ssize_t
__la_write(int fd,const void * buf,size_t nbytes)683 __la_write(int fd, const void *buf, size_t nbytes)
684 {
685 	DWORD bytes_written;
686 
687 #ifdef _WIN64
688 	if (nbytes > UINT32_MAX)
689 		nbytes = UINT32_MAX;
690 #endif
691 	if (fd < 0) {
692 		errno = EBADF;
693 		return (-1);
694 	}
695 	if (!WriteFile((HANDLE)_get_osfhandle(fd), buf, (uint32_t)nbytes,
696 	    &bytes_written, NULL)) {
697 		DWORD lasterr;
698 
699 		lasterr = GetLastError();
700 		if (lasterr == ERROR_ACCESS_DENIED)
701 			errno = EBADF;
702 		else
703 			la_dosmaperr(lasterr);
704 		return (-1);
705 	}
706 	return (bytes_written);
707 }
708 
709 /*
710  * Replace the Windows path separator '\' with '/'.
711  */
712 static int
replace_pathseparator(struct archive_wstring * ws,const wchar_t * wp)713 replace_pathseparator(struct archive_wstring *ws, const wchar_t *wp)
714 {
715 	wchar_t *w;
716 	size_t path_length;
717 
718 	if (wp == NULL)
719 		return(0);
720 	if (wcschr(wp, L'\\') == NULL)
721 		return(0);
722 	path_length = wcslen(wp);
723 	if (archive_wstring_ensure(ws, path_length) == NULL)
724 		return(-1);
725 	archive_wstrncpy(ws, wp, path_length);
726 	for (w = ws->s; *w; w++) {
727 		if (*w == L'\\')
728 			*w = L'/';
729 	}
730 	return(1);
731 }
732 
733 static int
fix_pathseparator(struct archive_entry * entry)734 fix_pathseparator(struct archive_entry *entry)
735 {
736 	struct archive_wstring ws;
737 	const wchar_t *wp;
738 	int ret = ARCHIVE_OK;
739 
740 	archive_string_init(&ws);
741 	wp = archive_entry_pathname_w(entry);
742 	switch (replace_pathseparator(&ws, wp)) {
743 	case 0: /* Not replaced. */
744 		break;
745 	case 1: /* Replaced. */
746 		archive_entry_copy_pathname_w(entry, ws.s);
747 		break;
748 	default:
749 		ret = ARCHIVE_FAILED;
750 	}
751 	wp = archive_entry_hardlink_w(entry);
752 	switch (replace_pathseparator(&ws, wp)) {
753 	case 0: /* Not replaced. */
754 		break;
755 	case 1: /* Replaced. */
756 		archive_entry_copy_hardlink_w(entry, ws.s);
757 		break;
758 	default:
759 		ret = ARCHIVE_FAILED;
760 	}
761 	wp = archive_entry_symlink_w(entry);
762 	switch (replace_pathseparator(&ws, wp)) {
763 	case 0: /* Not replaced. */
764 		break;
765 	case 1: /* Replaced. */
766 		archive_entry_copy_symlink_w(entry, ws.s);
767 		break;
768 	default:
769 		ret = ARCHIVE_FAILED;
770 	}
771 	archive_wstring_free(&ws);
772 	return(ret);
773 }
774 
775 struct archive_entry *
__la_win_entry_in_posix_pathseparator(struct archive_entry * entry)776 __la_win_entry_in_posix_pathseparator(struct archive_entry *entry)
777 {
778 	struct archive_entry *entry_main;
779 	const wchar_t *wp;
780 	int has_backslash = 0;
781 	int ret;
782 
783 	wp = archive_entry_pathname_w(entry);
784 	if (wp != NULL && wcschr(wp, L'\\') != NULL)
785 		has_backslash = 1;
786 	if (!has_backslash) {
787 		wp = archive_entry_hardlink_w(entry);
788 		if (wp != NULL && wcschr(wp, L'\\') != NULL)
789 			has_backslash = 1;
790 	}
791 	if (!has_backslash) {
792 		wp = archive_entry_symlink_w(entry);
793 		if (wp != NULL && wcschr(wp, L'\\') != NULL)
794 			has_backslash = 1;
795 	}
796 	/*
797 	 * If there is no backslash chars, return the original.
798 	 */
799 	if (!has_backslash)
800 		return (entry);
801 
802 	/* Copy entry so we can modify it as needed. */
803 	entry_main = archive_entry_clone(entry);
804 	if (entry_main == NULL)
805 		return (NULL);
806 	/* Replace the Windows path-separator '\' with '/'. */
807 	ret = fix_pathseparator(entry_main);
808 	if (ret < ARCHIVE_WARN) {
809 		archive_entry_free(entry_main);
810 		return (NULL);
811 	}
812 	return (entry_main);
813 }
814 
815 
816 /*
817  * The following function was modified from PostgreSQL sources and is
818  * subject to the copyright below.
819  */
820 /*-------------------------------------------------------------------------
821  *
822  * win32error.c
823  *	  Map win32 error codes to errno values
824  *
825  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
826  *
827  * IDENTIFICATION
828  *	  $PostgreSQL: pgsql/src/port/win32error.c,v 1.4 2008/01/01 19:46:00 momjian Exp $
829  *
830  *-------------------------------------------------------------------------
831  */
832 /*
833 PostgreSQL Database Management System
834 (formerly known as Postgres, then as Postgres95)
835 
836 Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
837 
838 Portions Copyright (c) 1994, The Regents of the University of California
839 
840 Permission to use, copy, modify, and distribute this software and its
841 documentation for any purpose, without fee, and without a written agreement
842 is hereby granted, provided that the above copyright notice and this
843 paragraph and the following two paragraphs appear in all copies.
844 
845 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
846 DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
847 LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
848 DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
849 POSSIBILITY OF SUCH DAMAGE.
850 
851 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
852 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
853 AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
854 ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
855 PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
856 */
857 
858 static const struct {
859 	DWORD		winerr;
860 	int		doserr;
861 } doserrors[] =
862 {
863 	{	ERROR_INVALID_FUNCTION, EINVAL	},
864 	{	ERROR_FILE_NOT_FOUND, ENOENT	},
865 	{	ERROR_PATH_NOT_FOUND, ENOENT	},
866 	{	ERROR_TOO_MANY_OPEN_FILES, EMFILE	},
867 	{	ERROR_ACCESS_DENIED, EACCES	},
868 	{	ERROR_INVALID_HANDLE, EBADF	},
869 	{	ERROR_ARENA_TRASHED, ENOMEM	},
870 	{	ERROR_NOT_ENOUGH_MEMORY, ENOMEM	},
871 	{	ERROR_INVALID_BLOCK, ENOMEM	},
872 	{	ERROR_BAD_ENVIRONMENT, E2BIG	},
873 	{	ERROR_BAD_FORMAT, ENOEXEC	},
874 	{	ERROR_INVALID_ACCESS, EINVAL	},
875 	{	ERROR_INVALID_DATA, EINVAL	},
876 	{	ERROR_INVALID_DRIVE, ENOENT	},
877 	{	ERROR_CURRENT_DIRECTORY, EACCES	},
878 	{	ERROR_NOT_SAME_DEVICE, EXDEV	},
879 	{	ERROR_NO_MORE_FILES, ENOENT	},
880 	{	ERROR_LOCK_VIOLATION, EACCES	},
881 	{	ERROR_SHARING_VIOLATION, EACCES	},
882 	{	ERROR_BAD_NETPATH, ENOENT	},
883 	{	ERROR_NETWORK_ACCESS_DENIED, EACCES	},
884 	{	ERROR_BAD_NET_NAME, ENOENT	},
885 	{	ERROR_FILE_EXISTS, EEXIST	},
886 	{	ERROR_CANNOT_MAKE, EACCES	},
887 	{	ERROR_FAIL_I24, EACCES	},
888 	{	ERROR_INVALID_PARAMETER, EINVAL	},
889 	{	ERROR_NO_PROC_SLOTS, EAGAIN	},
890 	{	ERROR_DRIVE_LOCKED, EACCES	},
891 	{	ERROR_BROKEN_PIPE, EPIPE	},
892 	{	ERROR_DISK_FULL, ENOSPC	},
893 	{	ERROR_INVALID_TARGET_HANDLE, EBADF	},
894 	{	ERROR_INVALID_HANDLE, EINVAL	},
895 	{	ERROR_WAIT_NO_CHILDREN, ECHILD	},
896 	{	ERROR_CHILD_NOT_COMPLETE, ECHILD	},
897 	{	ERROR_DIRECT_ACCESS_HANDLE, EBADF	},
898 	{	ERROR_NEGATIVE_SEEK, EINVAL	},
899 	{	ERROR_SEEK_ON_DEVICE, EACCES	},
900 	{	ERROR_DIR_NOT_EMPTY, ENOTEMPTY	},
901 	{	ERROR_NOT_LOCKED, EACCES	},
902 	{	ERROR_BAD_PATHNAME, ENOENT	},
903 	{	ERROR_MAX_THRDS_REACHED, EAGAIN	},
904 	{	ERROR_LOCK_FAILED, EACCES	},
905 	{	ERROR_ALREADY_EXISTS, EEXIST	},
906 	{	ERROR_FILENAME_EXCED_RANGE, ENOENT	},
907 	{	ERROR_NESTING_NOT_ALLOWED, EAGAIN	},
908 	{	ERROR_NOT_ENOUGH_QUOTA, ENOMEM	}
909 };
910 
911 void
__la_dosmaperr(unsigned long e)912 __la_dosmaperr(unsigned long e)
913 {
914 	int			i;
915 
916 	if (e == 0)
917 	{
918 		errno = 0;
919 		return;
920 	}
921 
922 	for (i = 0; i < (int)(sizeof(doserrors)/sizeof(doserrors[0])); i++)
923 	{
924 		if (doserrors[i].winerr == e)
925 		{
926 			errno = doserrors[i].doserr;
927 			return;
928 		}
929 	}
930 
931 	/* fprintf(stderr, "unrecognized win32 error code: %lu", e); */
932 	errno = EINVAL;
933 	return;
934 }
935 
936 #endif /* _WIN32 && !__CYGWIN__ */
937