1/*-
2 * Copyright (c) 2009 Michihiro NAKAJIMA
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#if defined(_WIN32) && !defined(__CYGWIN__)
27
28#include "cpio_platform.h"
29#include <ctype.h>
30#include <errno.h>
31#include <fcntl.h>
32#include <io.h>
33#include <stddef.h>
34#ifdef HAVE_SYS_UTIME_H
35#include <sys/utime.h>
36#endif
37#include <sys/stat.h>
38#include <process.h>
39#include <stdlib.h>
40#include <wchar.h>
41#include <windows.h>
42#include <sddl.h>
43
44#include "cpio.h"
45#include "err.h"
46
47#define EPOC_TIME	(116444736000000000ULL)
48
49static void cpio_dosmaperr(unsigned long);
50
51/*
52 * Prepend "\\?\" to the path name and convert it to unicode to permit
53 * an extended-length path for a maximum total path length of 32767
54 * characters.
55 * see also http://msdn.microsoft.com/en-us/library/aa365247.aspx
56 */
57static wchar_t *
58permissive_name(const char *name)
59{
60	wchar_t *wn, *wnp;
61	wchar_t *ws, *wsp;
62	DWORD l, len, slen, alloclen;
63	int unc;
64
65	len = (DWORD)strlen(name);
66	wn = malloc((len + 1) * sizeof(wchar_t));
67	if (wn == NULL)
68		return (NULL);
69	l = MultiByteToWideChar(CP_ACP, 0, name, len, wn, len);
70	if (l == 0) {
71		free(wn);
72		return (NULL);
73	}
74	wn[l] = L'\0';
75
76	/* Get a full path names */
77	l = GetFullPathNameW(wn, 0, NULL, NULL);
78	if (l == 0) {
79		free(wn);
80		return (NULL);
81	}
82	wnp = malloc(l * sizeof(wchar_t));
83	if (wnp == NULL) {
84		free(wn);
85		return (NULL);
86	}
87	len = GetFullPathNameW(wn, l, wnp, NULL);
88	free(wn);
89	wn = wnp;
90
91	if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
92	    wnp[2] == L'?' && wnp[3] == L'\\')
93		/* We have already permissive names. */
94		return (wn);
95
96	if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
97		wnp[2] == L'.' && wnp[3] == L'\\') {
98		/* Device names */
99		if (((wnp[4] >= L'a' && wnp[4] <= L'z') ||
100		     (wnp[4] >= L'A' && wnp[4] <= L'Z')) &&
101		    wnp[5] == L':' && wnp[6] == L'\\')
102			wnp[2] = L'?';/* Not device names. */
103		return (wn);
104	}
105
106	unc = 0;
107	if (wnp[0] == L'\\' && wnp[1] == L'\\' && wnp[2] != L'\\') {
108		wchar_t *p = &wnp[2];
109
110		/* Skip server-name letters. */
111		while (*p != L'\\' && *p != L'\0')
112			++p;
113		if (*p == L'\\') {
114			wchar_t *rp = ++p;
115			/* Skip share-name letters. */
116			while (*p != L'\\' && *p != L'\0')
117				++p;
118			if (*p == L'\\' && p != rp) {
119				/* Now, match patterns such as
120				 * "\\server-name\share-name\" */
121				wnp += 2;
122				len -= 2;
123				unc = 1;
124			}
125		}
126	}
127
128	alloclen = slen = 4 + (unc * 4) + len + 1;
129	ws = wsp = malloc(slen * sizeof(wchar_t));
130	if (ws == NULL) {
131		free(wn);
132		return (NULL);
133	}
134	/* prepend "\\?\" */
135	wcsncpy(wsp, L"\\\\?\\", 4);
136	wsp += 4;
137	slen -= 4;
138	if (unc) {
139		/* append "UNC\" ---> "\\?\UNC\" */
140		wcsncpy(wsp, L"UNC\\", 4);
141		wsp += 4;
142		slen -= 4;
143	}
144	wcsncpy(wsp, wnp, slen);
145	free(wn);
146	ws[alloclen - 1] = L'\0';
147	return (ws);
148}
149
150static HANDLE
151cpio_CreateFile(const char *path, DWORD dwDesiredAccess, DWORD dwShareMode,
152    LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
153    DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
154{
155	wchar_t *wpath;
156	HANDLE handle;
157# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
158	CREATEFILE2_EXTENDED_PARAMETERS createExParams;
159#endif
160
161#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
162	handle = CreateFileA(path, dwDesiredAccess, dwShareMode,
163	    lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
164	    hTemplateFile);
165	if (handle != INVALID_HANDLE_VALUE)
166		return (handle);
167	if (GetLastError() != ERROR_PATH_NOT_FOUND)
168		return (handle);
169#endif
170	wpath = permissive_name(path);
171	if (wpath == NULL)
172		return INVALID_HANDLE_VALUE;
173# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
174	ZeroMemory(&createExParams, sizeof(createExParams));
175	createExParams.dwSize = sizeof(createExParams);
176	createExParams.dwFileAttributes = dwFlagsAndAttributes & 0xFFFF;
177	createExParams.dwFileFlags = dwFlagsAndAttributes & 0xFFF00000;
178	createExParams.dwSecurityQosFlags = dwFlagsAndAttributes & 0x000F0000;
179	createExParams.lpSecurityAttributes = lpSecurityAttributes;
180	createExParams.hTemplateFile = hTemplateFile;
181	handle = CreateFile2(wpath, dwDesiredAccess, dwShareMode,
182	    dwCreationDisposition, &createExParams);
183#else
184	handle = CreateFileW(wpath, dwDesiredAccess, dwShareMode,
185	    lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
186	    hTemplateFile);
187#endif
188	free(wpath);
189	return (handle);
190}
191
192#define WINTIME(sec, usec)	((Int32x32To64(sec, 10000000) + EPOC_TIME) + (usec * 10))
193static int
194__hutimes(HANDLE handle, const struct __timeval *times)
195{
196	ULARGE_INTEGER wintm;
197	FILETIME fatime, fmtime;
198
199	wintm.QuadPart = WINTIME(times[0].tv_sec, times[0].tv_usec);
200	fatime.dwLowDateTime = wintm.LowPart;
201	fatime.dwHighDateTime = wintm.HighPart;
202	wintm.QuadPart = WINTIME(times[1].tv_sec, times[1].tv_usec);
203	fmtime.dwLowDateTime = wintm.LowPart;
204	fmtime.dwHighDateTime = wintm.HighPart;
205	if (SetFileTime(handle, NULL, &fatime, &fmtime) == 0) {
206		errno = EINVAL;
207		return (-1);
208	}
209	return (0);
210}
211
212int
213futimes(int fd, const struct __timeval *times)
214{
215
216	return (__hutimes((HANDLE)_get_osfhandle(fd), times));
217}
218
219int
220utimes(const char *name, const struct __timeval *times)
221{
222	int ret;
223	HANDLE handle;
224
225	handle = cpio_CreateFile(name, GENERIC_READ | GENERIC_WRITE,
226	    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
227	    FILE_FLAG_BACKUP_SEMANTICS, NULL);
228	if (handle == INVALID_HANDLE_VALUE) {
229		cpio_dosmaperr(GetLastError());
230		return (-1);
231	}
232	ret = __hutimes(handle, times);
233	CloseHandle(handle);
234	return (ret);
235}
236
237/*
238 * The following function was modified from PostgreSQL sources and is
239 * subject to the copyright below.
240 */
241/*-------------------------------------------------------------------------
242 *
243 * win32error.c
244 *	  Map win32 error codes to errno values
245 *
246 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
247 *
248 * IDENTIFICATION
249 *	  $PostgreSQL: pgsql/src/port/win32error.c,v 1.4 2008/01/01 19:46:00 momjian Exp $
250 *
251 *-------------------------------------------------------------------------
252 */
253/*
254PostgreSQL Database Management System
255(formerly known as Postgres, then as Postgres95)
256
257Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
258
259Portions Copyright (c) 1994, The Regents of the University of California
260
261Permission to use, copy, modify, and distribute this software and its
262documentation for any purpose, without fee, and without a written agreement
263is hereby granted, provided that the above copyright notice and this
264paragraph and the following two paragraphs appear in all copies.
265
266IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
267DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
268LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
269DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
270POSSIBILITY OF SUCH DAMAGE.
271
272THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
273INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
274AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
275ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
276PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
277*/
278
279static const struct {
280	DWORD		winerr;
281	int		doserr;
282} doserrors[] =
283{
284	{	ERROR_INVALID_FUNCTION, EINVAL	},
285	{	ERROR_FILE_NOT_FOUND, ENOENT	},
286	{	ERROR_PATH_NOT_FOUND, ENOENT	},
287	{	ERROR_TOO_MANY_OPEN_FILES, EMFILE	},
288	{	ERROR_ACCESS_DENIED, EACCES	},
289	{	ERROR_INVALID_HANDLE, EBADF	},
290	{	ERROR_ARENA_TRASHED, ENOMEM	},
291	{	ERROR_NOT_ENOUGH_MEMORY, ENOMEM	},
292	{	ERROR_INVALID_BLOCK, ENOMEM	},
293	{	ERROR_BAD_ENVIRONMENT, E2BIG	},
294	{	ERROR_BAD_FORMAT, ENOEXEC	},
295	{	ERROR_INVALID_ACCESS, EINVAL	},
296	{	ERROR_INVALID_DATA, EINVAL	},
297	{	ERROR_INVALID_DRIVE, ENOENT	},
298	{	ERROR_CURRENT_DIRECTORY, EACCES	},
299	{	ERROR_NOT_SAME_DEVICE, EXDEV	},
300	{	ERROR_NO_MORE_FILES, ENOENT	},
301	{	ERROR_LOCK_VIOLATION, EACCES	},
302	{	ERROR_SHARING_VIOLATION, EACCES	},
303	{	ERROR_BAD_NETPATH, ENOENT	},
304	{	ERROR_NETWORK_ACCESS_DENIED, EACCES	},
305	{	ERROR_BAD_NET_NAME, ENOENT	},
306	{	ERROR_FILE_EXISTS, EEXIST	},
307	{	ERROR_CANNOT_MAKE, EACCES	},
308	{	ERROR_FAIL_I24, EACCES	},
309	{	ERROR_INVALID_PARAMETER, EINVAL	},
310	{	ERROR_NO_PROC_SLOTS, EAGAIN	},
311	{	ERROR_DRIVE_LOCKED, EACCES	},
312	{	ERROR_BROKEN_PIPE, EPIPE	},
313	{	ERROR_DISK_FULL, ENOSPC	},
314	{	ERROR_INVALID_TARGET_HANDLE, EBADF	},
315	{	ERROR_INVALID_HANDLE, EINVAL	},
316	{	ERROR_WAIT_NO_CHILDREN, ECHILD	},
317	{	ERROR_CHILD_NOT_COMPLETE, ECHILD	},
318	{	ERROR_DIRECT_ACCESS_HANDLE, EBADF	},
319	{	ERROR_NEGATIVE_SEEK, EINVAL	},
320	{	ERROR_SEEK_ON_DEVICE, EACCES	},
321	{	ERROR_DIR_NOT_EMPTY, ENOTEMPTY	},
322	{	ERROR_NOT_LOCKED, EACCES	},
323	{	ERROR_BAD_PATHNAME, ENOENT	},
324	{	ERROR_MAX_THRDS_REACHED, EAGAIN	},
325	{	ERROR_LOCK_FAILED, EACCES	},
326	{	ERROR_ALREADY_EXISTS, EEXIST	},
327	{	ERROR_FILENAME_EXCED_RANGE, ENOENT	},
328	{	ERROR_NESTING_NOT_ALLOWED, EAGAIN	},
329	{	ERROR_NOT_ENOUGH_QUOTA, ENOMEM	}
330};
331
332static void
333cpio_dosmaperr(unsigned long e)
334{
335	int			i;
336
337	if (e == 0)	{
338		errno = 0;
339		return;
340	}
341
342	for (i = 0; i < (int)sizeof(doserrors); i++) {
343		if (doserrors[i].winerr == e) {
344			errno = doserrors[i].doserr;
345			return;
346		}
347	}
348
349	/* fprintf(stderr, "unrecognized win32 error code: %lu", e); */
350	errno = EINVAL;
351	return;
352}
353#endif
354