bsdtar_windows.c revision 1.1.1.3
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 "bsdtar_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 "bsdtar.h"
45#include "err.h"
46
47/* This may actually not be needed anymore.
48 * TODO: Review the error handling for chdir() failures and
49 * simply dump this if it's not really needed. */
50static void __tar_dosmaperr(unsigned long);
51
52/*
53 * Prepend "\\?\" to the path name and convert it to unicode to permit
54 * an extended-length path for a maximum total path length of 32767
55 * characters.
56 * see also http://msdn.microsoft.com/en-us/library/aa365247.aspx
57 */
58static wchar_t *
59permissive_name(const char *name)
60{
61	wchar_t *wn, *wnp;
62	wchar_t *ws, *wsp;
63	DWORD l, len, slen, alloclen;
64	int unc;
65
66	len = (DWORD)strlen(name);
67	wn = malloc((len + 1) * sizeof(wchar_t));
68	if (wn == NULL)
69		return (NULL);
70	l = MultiByteToWideChar(CP_ACP, 0, name, len, wn, len);
71	if (l == 0) {
72		free(wn);
73		return (NULL);
74	}
75	wn[l] = L'\0';
76
77	/* Get a full path names */
78	l = GetFullPathNameW(wn, 0, NULL, NULL);
79	if (l == 0) {
80		free(wn);
81		return (NULL);
82	}
83	wnp = malloc(l * sizeof(wchar_t));
84	if (wnp == NULL) {
85		free(wn);
86		return (NULL);
87	}
88	len = GetFullPathNameW(wn, l, wnp, NULL);
89	free(wn);
90	wn = wnp;
91
92	if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
93	    wnp[2] == L'?' && wnp[3] == L'\\')
94		/* We have already permissive names. */
95		return (wn);
96
97	if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
98		wnp[2] == L'.' && wnp[3] == L'\\') {
99		/* Device names */
100		if (((wnp[4] >= L'a' && wnp[4] <= L'z') ||
101		     (wnp[4] >= L'A' && wnp[4] <= L'Z')) &&
102		    wnp[5] == L':' && wnp[6] == L'\\')
103			wnp[2] = L'?';/* Not device names. */
104		return (wn);
105	}
106
107	unc = 0;
108	if (wnp[0] == L'\\' && wnp[1] == L'\\' && wnp[2] != L'\\') {
109		wchar_t *p = &wnp[2];
110
111		/* Skip server-name letters. */
112		while (*p != L'\\' && *p != L'\0')
113			++p;
114		if (*p == L'\\') {
115			wchar_t *rp = ++p;
116			/* Skip share-name letters. */
117			while (*p != L'\\' && *p != L'\0')
118				++p;
119			if (*p == L'\\' && p != rp) {
120				/* Now, match patterns such as
121				 * "\\server-name\share-name\" */
122				wnp += 2;
123				len -= 2;
124				unc = 1;
125			}
126		}
127	}
128
129	alloclen = slen = 4 + (unc * 4) + len + 1;
130	ws = wsp = malloc(slen * sizeof(wchar_t));
131	if (ws == NULL) {
132		free(wn);
133		return (NULL);
134	}
135	/* prepend "\\?\" */
136	wcsncpy(wsp, L"\\\\?\\", 4);
137	wsp += 4;
138	slen -= 4;
139	if (unc) {
140		/* append "UNC\" ---> "\\?\UNC\" */
141		wcsncpy(wsp, L"UNC\\", 4);
142		wsp += 4;
143		slen -= 4;
144	}
145	wcsncpy(wsp, wnp, slen);
146	free(wn);
147	ws[alloclen - 1] = L'\0';
148	return (ws);
149}
150
151int
152__tar_chdir(const char *path)
153{
154	wchar_t *ws;
155	int r;
156
157	r = SetCurrentDirectoryA(path);
158	if (r == 0) {
159		if (GetLastError() != ERROR_FILE_NOT_FOUND) {
160			__tar_dosmaperr(GetLastError());
161			return (-1);
162		}
163	} else
164		return (0);
165	ws = permissive_name(path);
166	if (ws == NULL) {
167		errno = EINVAL;
168		return (-1);
169	}
170	r = SetCurrentDirectoryW(ws);
171	free(ws);
172	if (r == 0) {
173		__tar_dosmaperr(GetLastError());
174		return (-1);
175	}
176	return (0);
177}
178
179/*
180 * The following function was modified from PostgreSQL sources and is
181 * subject to the copyright below.
182 */
183/*-------------------------------------------------------------------------
184 *
185 * win32error.c
186 *	  Map win32 error codes to errno values
187 *
188 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
189 *
190 * IDENTIFICATION
191 *	  $PostgreSQL: pgsql/src/port/win32error.c,v 1.4 2008/01/01 19:46:00 momjian Exp $
192 *
193 *-------------------------------------------------------------------------
194 */
195/*
196PostgreSQL Database Management System
197(formerly known as Postgres, then as Postgres95)
198
199Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
200
201Portions Copyright (c) 1994, The Regents of the University of California
202
203Permission to use, copy, modify, and distribute this software and its
204documentation for any purpose, without fee, and without a written agreement
205is hereby granted, provided that the above copyright notice and this
206paragraph and the following two paragraphs appear in all copies.
207
208IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
209DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
210LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
211DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
212POSSIBILITY OF SUCH DAMAGE.
213
214THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
215INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
216AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
217ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
218PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
219*/
220
221static const struct {
222	DWORD		winerr;
223	int		doserr;
224} doserrors[] =
225{
226	{	ERROR_INVALID_FUNCTION, EINVAL	},
227	{	ERROR_FILE_NOT_FOUND, ENOENT	},
228	{	ERROR_PATH_NOT_FOUND, ENOENT	},
229	{	ERROR_TOO_MANY_OPEN_FILES, EMFILE	},
230	{	ERROR_ACCESS_DENIED, EACCES	},
231	{	ERROR_INVALID_HANDLE, EBADF	},
232	{	ERROR_ARENA_TRASHED, ENOMEM	},
233	{	ERROR_NOT_ENOUGH_MEMORY, ENOMEM	},
234	{	ERROR_INVALID_BLOCK, ENOMEM	},
235	{	ERROR_BAD_ENVIRONMENT, E2BIG	},
236	{	ERROR_BAD_FORMAT, ENOEXEC	},
237	{	ERROR_INVALID_ACCESS, EINVAL	},
238	{	ERROR_INVALID_DATA, EINVAL	},
239	{	ERROR_INVALID_DRIVE, ENOENT	},
240	{	ERROR_CURRENT_DIRECTORY, EACCES	},
241	{	ERROR_NOT_SAME_DEVICE, EXDEV	},
242	{	ERROR_NO_MORE_FILES, ENOENT	},
243	{	ERROR_LOCK_VIOLATION, EACCES	},
244	{	ERROR_SHARING_VIOLATION, EACCES	},
245	{	ERROR_BAD_NETPATH, ENOENT	},
246	{	ERROR_NETWORK_ACCESS_DENIED, EACCES	},
247	{	ERROR_BAD_NET_NAME, ENOENT	},
248	{	ERROR_FILE_EXISTS, EEXIST	},
249	{	ERROR_CANNOT_MAKE, EACCES	},
250	{	ERROR_FAIL_I24, EACCES	},
251	{	ERROR_INVALID_PARAMETER, EINVAL	},
252	{	ERROR_NO_PROC_SLOTS, EAGAIN	},
253	{	ERROR_DRIVE_LOCKED, EACCES	},
254	{	ERROR_BROKEN_PIPE, EPIPE	},
255	{	ERROR_DISK_FULL, ENOSPC	},
256	{	ERROR_INVALID_TARGET_HANDLE, EBADF	},
257	{	ERROR_INVALID_HANDLE, EINVAL	},
258	{	ERROR_WAIT_NO_CHILDREN, ECHILD	},
259	{	ERROR_CHILD_NOT_COMPLETE, ECHILD	},
260	{	ERROR_DIRECT_ACCESS_HANDLE, EBADF	},
261	{	ERROR_NEGATIVE_SEEK, EINVAL	},
262	{	ERROR_SEEK_ON_DEVICE, EACCES	},
263	{	ERROR_DIR_NOT_EMPTY, ENOTEMPTY	},
264	{	ERROR_NOT_LOCKED, EACCES	},
265	{	ERROR_BAD_PATHNAME, ENOENT	},
266	{	ERROR_MAX_THRDS_REACHED, EAGAIN	},
267	{	ERROR_LOCK_FAILED, EACCES	},
268	{	ERROR_ALREADY_EXISTS, EEXIST	},
269	{	ERROR_FILENAME_EXCED_RANGE, ENOENT	},
270	{	ERROR_NESTING_NOT_ALLOWED, EAGAIN	},
271	{	ERROR_NOT_ENOUGH_QUOTA, ENOMEM	}
272};
273
274static void
275__tar_dosmaperr(unsigned long e)
276{
277	int			i;
278
279	if (e == 0)	{
280		errno = 0;
281		return;
282	}
283
284	for (i = 0; i < (int)sizeof(doserrors); i++) {
285		if (doserrors[i].winerr == e) {
286			errno = doserrors[i].doserr;
287			return;
288		}
289	}
290
291	/* fprintf(stderr, "unrecognized win32 error code: %lu", e); */
292	errno = EINVAL;
293	return;
294}
295
296#endif
297