#if defined(_WIN32) && !defined(__CYGWIN__)
#include "cpio_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 "cpio.h"
#include "err.h"
#define EPOC_TIME (116444736000000000ULL)
static void cpio_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);
}
static HANDLE
cpio_CreateFile(const char *path, DWORD dwDesiredAccess, DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
{
wchar_t *wpath;
HANDLE handle;
handle = CreateFileA(path, dwDesiredAccess, dwShareMode,
lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
hTemplateFile);
if (handle != INVALID_HANDLE_VALUE)
return (handle);
if (GetLastError() != ERROR_PATH_NOT_FOUND)
return (handle);
wpath = permissive_name(path);
if (wpath == NULL)
return (handle);
handle = CreateFileW(wpath, dwDesiredAccess, dwShareMode,
lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
hTemplateFile);
free(wpath);
return (handle);
}
#define WINTIME(sec, usec) ((Int32x32To64(sec, 10000000) + EPOC_TIME) + (usec * 10))
static int
__hutimes(HANDLE handle, const struct __timeval *times)
{
ULARGE_INTEGER wintm;
FILETIME fatime, fmtime;
wintm.QuadPart = WINTIME(times[0].tv_sec, times[0].tv_usec);
fatime.dwLowDateTime = wintm.LowPart;
fatime.dwHighDateTime = wintm.HighPart;
wintm.QuadPart = WINTIME(times[1].tv_sec, times[1].tv_usec);
fmtime.dwLowDateTime = wintm.LowPart;
fmtime.dwHighDateTime = wintm.HighPart;
if (SetFileTime(handle, NULL, &fatime, &fmtime) == 0) {
errno = EINVAL;
return (-1);
}
return (0);
}
int
futimes(int fd, const struct __timeval *times)
{
return (__hutimes((HANDLE)_get_osfhandle(fd), times));
}
int
utimes(const char *name, const struct __timeval *times)
{
int ret;
HANDLE handle;
handle = cpio_CreateFile(name, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (handle == INVALID_HANDLE_VALUE) {
cpio_dosmaperr(GetLastError());
return (-1);
}
ret = __hutimes(handle, times);
CloseHandle(handle);
return (ret);
}
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
cpio_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