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