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