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