xref: /netbsd-src/external/bsd/libarchive/dist/libarchive/filter_fork_windows.c (revision 65e637ab3a9cc7c3e7749c941a1011ecd65517e6)
1 /*-
2  * Copyright (c) 2009-2012 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 #include "archive_platform.h"
27 
28 #if defined(_WIN32) && !defined(__CYGWIN__)
29 #include "archive_cmdline_private.h"
30 #include "archive_string.h"
31 
32 #include "filter_fork.h"
33 
34 #if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
35 /* There are some editions of Windows ("nano server," for example) that
36  * do not host user32.dll. If we want to keep running on those editions,
37  * we need to delay-load WaitForInputIdle. */
38 static void *
la_GetFunctionUser32(const char * name)39 la_GetFunctionUser32(const char *name)
40 {
41 	static HINSTANCE lib;
42 	static int set;
43 	if (!set) {
44 		set = 1;
45 		lib = LoadLibrary(TEXT("user32.dll"));
46 	}
47 	if (lib == NULL) {
48 		return NULL;
49 	}
50 	return (void *)GetProcAddress(lib, name);
51 }
52 
53 static int
la_WaitForInputIdle(HANDLE hProcess,DWORD dwMilliseconds)54 la_WaitForInputIdle(HANDLE hProcess, DWORD dwMilliseconds)
55 {
56 	static DWORD (WINAPI *f)(HANDLE, DWORD);
57 	static int set;
58 
59 	if (!set) {
60 		set = 1;
61 		f = la_GetFunctionUser32("WaitForInputIdle");
62 	}
63 
64 	if (!f) {
65 		/* An inability to wait for input idle is
66 		 * not _good_, but it is not catastrophic. */
67 		return WAIT_FAILED;
68 	}
69 	return (*f)(hProcess, dwMilliseconds);
70 }
71 
72 int
__archive_create_child(const char * cmd,int * child_stdin,int * child_stdout,HANDLE * out_child)73 __archive_create_child(const char *cmd, int *child_stdin, int *child_stdout,
74 		HANDLE *out_child)
75 {
76 	HANDLE childStdout[2], childStdin[2],childStderr;
77 	SECURITY_ATTRIBUTES secAtts;
78 	STARTUPINFOA staInfo;
79 	PROCESS_INFORMATION childInfo;
80 	struct archive_string cmdline;
81 	struct archive_string fullpath;
82 	struct archive_cmdline *acmd;
83 	char *arg0, *ext;
84 	int i, l;
85 	DWORD fl, fl_old;
86 	HANDLE child;
87 
88 	childStdout[0] = childStdout[1] = INVALID_HANDLE_VALUE;
89 	childStdin[0] = childStdin[1] = INVALID_HANDLE_VALUE;
90 	childStderr = INVALID_HANDLE_VALUE;
91 	archive_string_init(&cmdline);
92 	archive_string_init(&fullpath);
93 
94 	acmd = __archive_cmdline_allocate();
95 	if (acmd == NULL)
96 		goto fail;
97 	if (__archive_cmdline_parse(acmd, cmd) != ARCHIVE_OK)
98 		goto fail;
99 
100 	/*
101 	 * Search the full path of 'path'.
102 	 * NOTE: This does not need if we give CreateProcessA 'path' as
103 	 * a part of the cmdline and give CreateProcessA NULL as first
104 	 * parameter, but I do not like that way.
105 	 */
106 	ext = strrchr(acmd->path, '.');
107 	if (ext == NULL || strlen(ext) > 4)
108 		/* 'path' does not have a proper extension, so we have to
109 		 * give SearchPath() ".exe" as the extension. */
110 		ext = ".exe";
111 	else
112 		ext = NULL;/* 'path' has an extension. */
113 
114 	fl = MAX_PATH;
115 	do {
116 		if (archive_string_ensure(&fullpath, fl) == NULL)
117 			goto fail;
118 		fl_old = fl;
119 		fl = SearchPathA(NULL, acmd->path, ext, fl, fullpath.s,
120 			&arg0);
121 	} while (fl != 0 && fl > fl_old);
122 	if (fl == 0)
123 		goto fail;
124 
125 	/*
126 	 * Make a command line.
127 	 */
128 	for (l = 0, i = 0;  acmd->argv[i] != NULL; i++) {
129 		if (i == 0)
130 			continue;
131 		l += (int)strlen(acmd->argv[i]) + 1;
132 	}
133 	if (archive_string_ensure(&cmdline, l + 1) == NULL)
134 		goto fail;
135 	for (i = 0;  acmd->argv[i] != NULL; i++) {
136 		if (i == 0) {
137 			const char *p, *sp;
138 
139 			if ((p = strchr(acmd->argv[i], '/')) != NULL ||
140 			    (p = strchr(acmd->argv[i], '\\')) != NULL)
141 				p++;
142 			else
143 				p = acmd->argv[i];
144 			if ((sp = strchr(p, ' ')) != NULL)
145 				archive_strappend_char(&cmdline, '"');
146 			archive_strcat(&cmdline, p);
147 			if (sp != NULL)
148 				archive_strappend_char(&cmdline, '"');
149 		} else {
150 			archive_strappend_char(&cmdline, ' ');
151 			archive_strcat(&cmdline, acmd->argv[i]);
152 		}
153 	}
154 	if (i <= 1) {
155 		const char *sp;
156 
157 		if ((sp = strchr(arg0, ' ')) != NULL)
158 			archive_strappend_char(&cmdline, '"');
159 		archive_strcat(&cmdline, arg0);
160 		if (sp != NULL)
161 			archive_strappend_char(&cmdline, '"');
162 	}
163 
164 	secAtts.nLength = sizeof(SECURITY_ATTRIBUTES);
165 	secAtts.bInheritHandle = TRUE;
166 	secAtts.lpSecurityDescriptor = NULL;
167 	if (CreatePipe(&childStdout[0], &childStdout[1], &secAtts, 0) == 0)
168 		goto fail;
169 	if (!SetHandleInformation(childStdout[0], HANDLE_FLAG_INHERIT, 0))
170 		goto fail;
171 	if (CreatePipe(&childStdin[0], &childStdin[1], &secAtts, 0) == 0)
172 		goto fail;
173 	if (!SetHandleInformation(childStdin[1], HANDLE_FLAG_INHERIT, 0))
174 		goto fail;
175 	if (DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_ERROR_HANDLE),
176 	    GetCurrentProcess(), &childStderr, 0, TRUE,
177 	    DUPLICATE_SAME_ACCESS) == 0)
178 		goto fail;
179 
180 	memset(&staInfo, 0, sizeof(staInfo));
181 	staInfo.cb = sizeof(staInfo);
182 	staInfo.hStdError = childStderr;
183 	staInfo.hStdOutput = childStdout[1];
184 	staInfo.hStdInput = childStdin[0];
185 	staInfo.wShowWindow = SW_HIDE;
186 	staInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
187 	if (CreateProcessA(fullpath.s, cmdline.s, NULL, NULL, TRUE, 0,
188 	      NULL, NULL, &staInfo, &childInfo) == 0)
189 		goto fail;
190 	la_WaitForInputIdle(childInfo.hProcess, INFINITE);
191 	CloseHandle(childInfo.hProcess);
192 	CloseHandle(childInfo.hThread);
193 
194 	*child_stdout = _open_osfhandle((intptr_t)childStdout[0], _O_RDONLY);
195 	*child_stdin = _open_osfhandle((intptr_t)childStdin[1], _O_WRONLY);
196 
197 	child = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE,
198 		childInfo.dwProcessId);
199 	if (child == NULL) // INVALID_HANDLE_VALUE ?
200 		goto fail;
201 
202 	*out_child = child;
203 
204 	CloseHandle(childStdout[1]);
205 	CloseHandle(childStdin[0]);
206 
207 	archive_string_free(&cmdline);
208 	archive_string_free(&fullpath);
209 	__archive_cmdline_free(acmd);
210 	return ARCHIVE_OK;
211 
212 fail:
213 	if (childStdout[0] != INVALID_HANDLE_VALUE)
214 		CloseHandle(childStdout[0]);
215 	if (childStdout[1] != INVALID_HANDLE_VALUE)
216 		CloseHandle(childStdout[1]);
217 	if (childStdin[0] != INVALID_HANDLE_VALUE)
218 		CloseHandle(childStdin[0]);
219 	if (childStdin[1] != INVALID_HANDLE_VALUE)
220 		CloseHandle(childStdin[1]);
221 	if (childStderr != INVALID_HANDLE_VALUE)
222 		CloseHandle(childStderr);
223 	archive_string_free(&cmdline);
224 	archive_string_free(&fullpath);
225 	__archive_cmdline_free(acmd);
226 	return ARCHIVE_FAILED;
227 }
228 #else /* !WINAPI_PARTITION_DESKTOP */
229 int
__archive_create_child(const char * cmd,int * child_stdin,int * child_stdout,HANDLE * out_child)230 __archive_create_child(const char *cmd, int *child_stdin, int *child_stdout, HANDLE *out_child)
231 {
232 	(void)cmd; (void)child_stdin; (void) child_stdout; (void) out_child;
233 	return ARCHIVE_FAILED;
234 }
235 #endif /* !WINAPI_PARTITION_DESKTOP */
236 
237 void
__archive_check_child(int in,int out)238 __archive_check_child(int in, int out)
239 {
240 	(void)in; /* UNUSED */
241 	(void)out; /* UNUSED */
242 	Sleep(100);
243 }
244 
245 #endif /* _WIN32 && !__CYGWIN__ */
246