xref: /netbsd-src/external/bsd/libarchive/dist/tar/bsdtar_windows.c (revision c42dbd0ed2e61fe6eda8590caa852ccf34719964)
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. */
50 static 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  */
58 static wchar_t *
59 permissive_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 
151 int
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 /*
196 PostgreSQL Database Management System
197 (formerly known as Postgres, then as Postgres95)
198 
199 Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
200 
201 Portions Copyright (c) 1994, The Regents of the University of California
202 
203 Permission to use, copy, modify, and distribute this software and its
204 documentation for any purpose, without fee, and without a written agreement
205 is hereby granted, provided that the above copyright notice and this
206 paragraph and the following two paragraphs appear in all copies.
207 
208 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
209 DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
210 LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
211 DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
212 POSSIBILITY OF SUCH DAMAGE.
213 
214 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
215 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
216 AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
217 ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
218 PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
219 */
220 
221 static 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 
274 static 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