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