1/*-
2 * Copyright (c) 2007 Joerg Sonnenberger
3 * Copyright (c) 2012 Michihiro NAKAJIMA
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "archive_platform.h"
28__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_program.c 201104 2009-12-28 03:14:30Z kientzle $");
29
30#ifdef HAVE_SYS_WAIT_H
31#  include <sys/wait.h>
32#endif
33#ifdef HAVE_ERRNO_H
34#  include <errno.h>
35#endif
36#ifdef HAVE_FCNTL_H
37#  include <fcntl.h>
38#endif
39#ifdef HAVE_STDLIB_H
40#  include <stdlib.h>
41#endif
42#ifdef HAVE_STRING_H
43#  include <string.h>
44#endif
45
46#include "archive.h"
47#include "archive_private.h"
48#include "archive_string.h"
49#include "archive_write_private.h"
50#include "filter_fork.h"
51
52#if ARCHIVE_VERSION_NUMBER < 4000000
53int
54archive_write_set_compression_program(struct archive *a, const char *cmd)
55{
56	__archive_write_filters_free(a);
57	return (archive_write_add_filter_program(a, cmd));
58}
59#endif
60
61struct archive_write_program_data {
62#if defined(_WIN32) && !defined(__CYGWIN__)
63	HANDLE		 child;
64#else
65	pid_t		 child;
66#endif
67	int		 child_stdin, child_stdout;
68
69	char		*child_buf;
70	size_t		 child_buf_len, child_buf_avail;
71};
72
73struct private_data {
74	struct archive_write_program_data *pdata;
75	struct archive_string description;
76	char		*cmd;
77};
78
79static int archive_compressor_program_open(struct archive_write_filter *);
80static int archive_compressor_program_write(struct archive_write_filter *,
81		    const void *, size_t);
82static int archive_compressor_program_close(struct archive_write_filter *);
83static int archive_compressor_program_free(struct archive_write_filter *);
84
85/*
86 * Add a filter to this write handle that passes all data through an
87 * external program.
88 */
89int
90archive_write_add_filter_program(struct archive *_a, const char *cmd)
91{
92	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
93	struct private_data *data;
94	static const char *prefix = "Program: ";
95
96	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
97	    ARCHIVE_STATE_NEW, "archive_write_add_filter_program");
98
99	f->data = calloc(1, sizeof(*data));
100	if (f->data == NULL)
101		goto memerr;
102	data = (struct private_data *)f->data;
103
104	data->cmd = strdup(cmd);
105	if (data->cmd == NULL)
106		goto memerr;
107
108	data->pdata = __archive_write_program_allocate();
109	if (data->pdata == NULL)
110		goto memerr;
111
112	/* Make up a description string. */
113	if (archive_string_ensure(&data->description,
114	    strlen(prefix) + strlen(cmd) + 1) == NULL)
115		goto memerr;
116	archive_strcpy(&data->description, prefix);
117	archive_strcat(&data->description, cmd);
118
119	f->name = data->description.s;
120	f->code = ARCHIVE_FILTER_PROGRAM;
121	f->open = archive_compressor_program_open;
122	f->write = archive_compressor_program_write;
123	f->close = archive_compressor_program_close;
124	f->free = archive_compressor_program_free;
125	return (ARCHIVE_OK);
126memerr:
127	archive_compressor_program_free(f);
128	archive_set_error(_a, ENOMEM,
129	    "Can't allocate memory for filter program");
130	return (ARCHIVE_FATAL);
131}
132
133static int
134archive_compressor_program_open(struct archive_write_filter *f)
135{
136	struct private_data *data = (struct private_data *)f->data;
137
138	return __archive_write_program_open(f, data->pdata, data->cmd);
139}
140
141static int
142archive_compressor_program_write(struct archive_write_filter *f,
143    const void *buff, size_t length)
144{
145	struct private_data *data = (struct private_data *)f->data;
146
147	return __archive_write_program_write(f, data->pdata, buff, length);
148}
149
150static int
151archive_compressor_program_close(struct archive_write_filter *f)
152{
153	struct private_data *data = (struct private_data *)f->data;
154
155	return __archive_write_program_close(f, data->pdata);
156}
157
158static int
159archive_compressor_program_free(struct archive_write_filter *f)
160{
161	struct private_data *data = (struct private_data *)f->data;
162
163	if (data) {
164		free(data->cmd);
165		archive_string_free(&data->description);
166		__archive_write_program_free(data->pdata);
167		free(data);
168		f->data = NULL;
169	}
170	return (ARCHIVE_OK);
171}
172
173/*
174 * Allocate resources for executing an external program.
175 */
176struct archive_write_program_data *
177__archive_write_program_allocate(void)
178{
179	struct archive_write_program_data *data;
180
181	data = calloc(1, sizeof(struct archive_write_program_data));
182	if (data == NULL)
183		return (data);
184	data->child_stdin = -1;
185	data->child_stdout = -1;
186	return (data);
187}
188
189/*
190 * Release the resources.
191 */
192int
193__archive_write_program_free(struct archive_write_program_data *data)
194{
195
196	if (data) {
197#if defined(_WIN32) && !defined(__CYGWIN__)
198		if (data->child)
199			CloseHandle(data->child);
200#endif
201		free(data->child_buf);
202		free(data);
203	}
204	return (ARCHIVE_OK);
205}
206
207int
208__archive_write_program_open(struct archive_write_filter *f,
209    struct archive_write_program_data *data, const char *cmd)
210{
211	pid_t child;
212	int ret;
213
214	ret = __archive_write_open_filter(f->next_filter);
215	if (ret != ARCHIVE_OK)
216		return (ret);
217
218	if (data->child_buf == NULL) {
219		data->child_buf_len = 65536;
220		data->child_buf_avail = 0;
221		data->child_buf = malloc(data->child_buf_len);
222
223		if (data->child_buf == NULL) {
224			archive_set_error(f->archive, ENOMEM,
225			    "Can't allocate compression buffer");
226			return (ARCHIVE_FATAL);
227		}
228	}
229
230	child = __archive_create_child(cmd, &data->child_stdin,
231		    &data->child_stdout);
232	if (child == -1) {
233		archive_set_error(f->archive, EINVAL,
234		    "Can't initialise filter");
235		return (ARCHIVE_FATAL);
236	}
237#if defined(_WIN32) && !defined(__CYGWIN__)
238	data->child = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, child);
239	if (data->child == NULL) {
240		close(data->child_stdin);
241		data->child_stdin = -1;
242		close(data->child_stdout);
243		data->child_stdout = -1;
244		archive_set_error(f->archive, EINVAL,
245		    "Can't initialise filter");
246		return (ARCHIVE_FATAL);
247	}
248#else
249	data->child = child;
250#endif
251	return (ARCHIVE_OK);
252}
253
254static ssize_t
255child_write(struct archive_write_filter *f,
256    struct archive_write_program_data *data, const char *buf, size_t buf_len)
257{
258	ssize_t ret;
259
260	if (data->child_stdin == -1)
261		return (-1);
262
263	if (buf_len == 0)
264		return (-1);
265
266	for (;;) {
267		do {
268			ret = write(data->child_stdin, buf, buf_len);
269		} while (ret == -1 && errno == EINTR);
270
271		if (ret > 0)
272			return (ret);
273		if (ret == 0) {
274			close(data->child_stdin);
275			data->child_stdin = -1;
276			fcntl(data->child_stdout, F_SETFL, 0);
277			return (0);
278		}
279		if (ret == -1 && errno != EAGAIN)
280			return (-1);
281
282		if (data->child_stdout == -1) {
283			fcntl(data->child_stdin, F_SETFL, 0);
284			__archive_check_child(data->child_stdin,
285				data->child_stdout);
286			continue;
287		}
288
289		do {
290			ret = read(data->child_stdout,
291			    data->child_buf + data->child_buf_avail,
292			    data->child_buf_len - data->child_buf_avail);
293		} while (ret == -1 && errno == EINTR);
294
295		if (ret == 0 || (ret == -1 && errno == EPIPE)) {
296			close(data->child_stdout);
297			data->child_stdout = -1;
298			fcntl(data->child_stdin, F_SETFL, 0);
299			continue;
300		}
301		if (ret == -1 && errno == EAGAIN) {
302			__archive_check_child(data->child_stdin,
303				data->child_stdout);
304			continue;
305		}
306		if (ret == -1)
307			return (-1);
308
309		data->child_buf_avail += ret;
310
311		ret = __archive_write_filter(f->next_filter,
312		    data->child_buf, data->child_buf_avail);
313		if (ret != ARCHIVE_OK)
314			return (-1);
315		data->child_buf_avail = 0;
316	}
317}
318
319/*
320 * Write data to the filter stream.
321 */
322int
323__archive_write_program_write(struct archive_write_filter *f,
324    struct archive_write_program_data *data, const void *buff, size_t length)
325{
326	ssize_t ret;
327	const char *buf;
328
329	if (data->child == 0)
330		return (ARCHIVE_OK);
331
332	buf = buff;
333	while (length > 0) {
334		ret = child_write(f, data, buf, length);
335		if (ret == -1 || ret == 0) {
336			archive_set_error(f->archive, EIO,
337			    "Can't write to filter");
338			return (ARCHIVE_FATAL);
339		}
340		length -= ret;
341		buf += ret;
342	}
343	return (ARCHIVE_OK);
344}
345
346/*
347 * Finish the filtering...
348 */
349int
350__archive_write_program_close(struct archive_write_filter *f,
351    struct archive_write_program_data *data)
352{
353	int ret, r1, status;
354	ssize_t bytes_read;
355
356	if (data->child == 0)
357		return __archive_write_close_filter(f->next_filter);
358
359	ret = 0;
360	close(data->child_stdin);
361	data->child_stdin = -1;
362	fcntl(data->child_stdout, F_SETFL, 0);
363
364	for (;;) {
365		do {
366			bytes_read = read(data->child_stdout,
367			    data->child_buf + data->child_buf_avail,
368			    data->child_buf_len - data->child_buf_avail);
369		} while (bytes_read == -1 && errno == EINTR);
370
371		if (bytes_read == 0 || (bytes_read == -1 && errno == EPIPE))
372			break;
373
374		if (bytes_read == -1) {
375			archive_set_error(f->archive, errno,
376			    "Read from filter failed unexpectedly.");
377			ret = ARCHIVE_FATAL;
378			goto cleanup;
379		}
380		data->child_buf_avail += bytes_read;
381
382		ret = __archive_write_filter(f->next_filter,
383		    data->child_buf, data->child_buf_avail);
384		if (ret != ARCHIVE_OK) {
385			ret = ARCHIVE_FATAL;
386			goto cleanup;
387		}
388		data->child_buf_avail = 0;
389	}
390
391cleanup:
392	/* Shut down the child. */
393	if (data->child_stdin != -1)
394		close(data->child_stdin);
395	if (data->child_stdout != -1)
396		close(data->child_stdout);
397	while (waitpid(data->child, &status, 0) == -1 && errno == EINTR)
398		continue;
399#if defined(_WIN32) && !defined(__CYGWIN__)
400	CloseHandle(data->child);
401#endif
402	data->child = 0;
403
404	if (status != 0) {
405		archive_set_error(f->archive, EIO,
406		    "Filter exited with failure.");
407		ret = ARCHIVE_FATAL;
408	}
409	r1 = __archive_write_close_filter(f->next_filter);
410	return (r1 < ret ? r1 : ret);
411}
412
413