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