#if defined(_WIN32) && !defined(__CYGWIN__)
#include "bsdtar_platform.h"
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <io.h>
#include <stddef.h>
#ifdef HAVE_SYS_UTIME_H
#include <sys/utime.h>
#endif
#include <sys/stat.h>
#include <process.h>
#include <stdlib.h>
#include <wchar.h>
#include <windows.h>
#include <sddl.h>
#include "bsdtar.h"
#include "err.h"
static void __tar_dosmaperr(unsigned long);
static wchar_t *
permissive_name(const char *name)
{
wchar_t *wn, *wnp;
wchar_t *ws, *wsp;
DWORD l, len, slen, alloclen;
int unc;
len = (DWORD)strlen(name);
wn = malloc((len + 1) * sizeof(wchar_t));
if (wn == NULL)
return (NULL);
l = MultiByteToWideChar(CP_ACP, 0, name, len, wn, len);
if (l == 0) {
free(wn);
return (NULL);
}
wn[l] = L'\0';
l = GetFullPathNameW(wn, 0, NULL, NULL);
if (l == 0) {
free(wn);
return (NULL);
}
wnp = malloc(l * sizeof(wchar_t));
if (wnp == NULL) {
free(wn);
return (NULL);
}
len = GetFullPathNameW(wn, l, wnp, NULL);
free(wn);
wn = wnp;
if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
wnp[2] == L'?' && wnp[3] == L'\\')
return (wn);
if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
wnp[2] == L'.' && wnp[3] == L'\\') {
if (((wnp[4] >= L'a' && wnp[4] <= L'z') ||
(wnp[4] >= L'A' && wnp[4] <= L'Z')) &&
wnp[5] == L':' && wnp[6] == L'\\')
wnp[2] = L'?';
return (wn);
}
unc = 0;
if (wnp[0] == L'\\' && wnp[1] == L'\\' && wnp[2] != L'\\') {
wchar_t *p = &wnp[2];
while (*p != L'\\' && *p != L'\0')
++p;
if (*p == L'\\') {
wchar_t *rp = ++p;
while (*p != L'\\' && *p != L'\0')
++p;
if (*p == L'\\' && p != rp) {
wnp += 2;
len -= 2;
unc = 1;
}
}
}
alloclen = slen = 4 + (unc * 4) + len + 1;
ws = wsp = malloc(slen * sizeof(wchar_t));
if (ws == NULL) {
free(wn);
return (NULL);
}
wcsncpy(wsp, L"\\\\?\\", 4);
wsp += 4;
slen -= 4;
if (unc) {
wcsncpy(wsp, L"UNC\\", 4);
wsp += 4;
slen -= 4;
}
wcsncpy(wsp, wnp, slen);
free(wn);
ws[alloclen - 1] = L'\0';
return (ws);
}
int
__tar_chdir(const char *path)
{
wchar_t *ws;
int r;
r = SetCurrentDirectoryA(path);
if (r == 0) {
if (GetLastError() != ERROR_FILE_NOT_FOUND) {
__tar_dosmaperr(GetLastError());
return (-1);
}
} else
return (0);
ws = permissive_name(path);
if (ws == NULL) {
errno = EINVAL;
return (-1);
}
r = SetCurrentDirectoryW(ws);
free(ws);
if (r == 0) {
__tar_dosmaperr(GetLastError());
return (-1);
}
return (0);
}
static const struct {
DWORD winerr;
int doserr;
} doserrors[] =
{
{ ERROR_INVALID_FUNCTION, EINVAL },
{ ERROR_FILE_NOT_FOUND, ENOENT },
{ ERROR_PATH_NOT_FOUND, ENOENT },
{ ERROR_TOO_MANY_OPEN_FILES, EMFILE },
{ ERROR_ACCESS_DENIED, EACCES },
{ ERROR_INVALID_HANDLE, EBADF },
{ ERROR_ARENA_TRASHED, ENOMEM },
{ ERROR_NOT_ENOUGH_MEMORY, ENOMEM },
{ ERROR_INVALID_BLOCK, ENOMEM },
{ ERROR_BAD_ENVIRONMENT, E2BIG },
{ ERROR_BAD_FORMAT, ENOEXEC },
{ ERROR_INVALID_ACCESS, EINVAL },
{ ERROR_INVALID_DATA, EINVAL },
{ ERROR_INVALID_DRIVE, ENOENT },
{ ERROR_CURRENT_DIRECTORY, EACCES },
{ ERROR_NOT_SAME_DEVICE, EXDEV },
{ ERROR_NO_MORE_FILES, ENOENT },
{ ERROR_LOCK_VIOLATION, EACCES },
{ ERROR_SHARING_VIOLATION, EACCES },
{ ERROR_BAD_NETPATH, ENOENT },
{ ERROR_NETWORK_ACCESS_DENIED, EACCES },
{ ERROR_BAD_NET_NAME, ENOENT },
{ ERROR_FILE_EXISTS, EEXIST },
{ ERROR_CANNOT_MAKE, EACCES },
{ ERROR_FAIL_I24, EACCES },
{ ERROR_INVALID_PARAMETER, EINVAL },
{ ERROR_NO_PROC_SLOTS, EAGAIN },
{ ERROR_DRIVE_LOCKED, EACCES },
{ ERROR_BROKEN_PIPE, EPIPE },
{ ERROR_DISK_FULL, ENOSPC },
{ ERROR_INVALID_TARGET_HANDLE, EBADF },
{ ERROR_INVALID_HANDLE, EINVAL },
{ ERROR_WAIT_NO_CHILDREN, ECHILD },
{ ERROR_CHILD_NOT_COMPLETE, ECHILD },
{ ERROR_DIRECT_ACCESS_HANDLE, EBADF },
{ ERROR_NEGATIVE_SEEK, EINVAL },
{ ERROR_SEEK_ON_DEVICE, EACCES },
{ ERROR_DIR_NOT_EMPTY, ENOTEMPTY },
{ ERROR_NOT_LOCKED, EACCES },
{ ERROR_BAD_PATHNAME, ENOENT },
{ ERROR_MAX_THRDS_REACHED, EAGAIN },
{ ERROR_LOCK_FAILED, EACCES },
{ ERROR_ALREADY_EXISTS, EEXIST },
{ ERROR_FILENAME_EXCED_RANGE, ENOENT },
{ ERROR_NESTING_NOT_ALLOWED, EAGAIN },
{ ERROR_NOT_ENOUGH_QUOTA, ENOMEM }
};
static void
__tar_dosmaperr(unsigned long e)
{
int i;
if (e == 0) {
errno = 0;
return;
}
for (i = 0; i < sizeof(doserrors); i++) {
if (doserrors[i].winerr == e) {
errno = doserrors[i].doserr;
return;
}
}
errno = EINVAL;
return;
}
#endif