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