1228753Smm/*-
2228753Smm * Copyright (c) 2007 Joerg Sonnenberger
3248616Smm * Copyright (c) 2012 Michihiro NAKAJIMA
4228753Smm * All rights reserved.
5228753Smm *
6228753Smm * Redistribution and use in source and binary forms, with or without
7228753Smm * modification, are permitted provided that the following conditions
8228753Smm * are met:
9228753Smm * 1. Redistributions of source code must retain the above copyright
10228753Smm *    notice, this list of conditions and the following disclaimer.
11228753Smm * 2. Redistributions in binary form must reproduce the above copyright
12228753Smm *    notice, this list of conditions and the following disclaimer in the
13228753Smm *    documentation and/or other materials provided with the distribution.
14228753Smm *
15228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25228753Smm */
26228753Smm
27228753Smm#include "archive_platform.h"
28228753Smm__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_program.c 201104 2009-12-28 03:14:30Z kientzle $");
29228753Smm
30228753Smm#ifdef HAVE_SYS_WAIT_H
31228753Smm#  include <sys/wait.h>
32228753Smm#endif
33228753Smm#ifdef HAVE_ERRNO_H
34228753Smm#  include <errno.h>
35228753Smm#endif
36228753Smm#ifdef HAVE_FCNTL_H
37228753Smm#  include <fcntl.h>
38228753Smm#endif
39228753Smm#ifdef HAVE_STDLIB_H
40228753Smm#  include <stdlib.h>
41228753Smm#endif
42228753Smm#ifdef HAVE_STRING_H
43228753Smm#  include <string.h>
44228753Smm#endif
45228753Smm
46228753Smm#include "archive.h"
47228753Smm#include "archive_private.h"
48248616Smm#include "archive_string.h"
49228753Smm#include "archive_write_private.h"
50248616Smm#include "filter_fork.h"
51228753Smm
52231200Smm#if ARCHIVE_VERSION_NUMBER < 4000000
53231200Smmint
54231200Smmarchive_write_set_compression_program(struct archive *a, const char *cmd)
55231200Smm{
56231200Smm	__archive_write_filters_free(a);
57231200Smm	return (archive_write_add_filter_program(a, cmd));
58231200Smm}
59231200Smm#endif
60231200Smm
61248616Smmstruct archive_write_program_data {
62248616Smm#if defined(_WIN32) && !defined(__CYGWIN__)
63248616Smm	HANDLE		 child;
64231200Smm#else
65228753Smm	pid_t		 child;
66248616Smm#endif
67228753Smm	int		 child_stdin, child_stdout;
68228753Smm
69228753Smm	char		*child_buf;
70228753Smm	size_t		 child_buf_len, child_buf_avail;
71228753Smm};
72228753Smm
73248616Smmstruct private_data {
74248616Smm	struct archive_write_program_data *pdata;
75248616Smm	struct archive_string description;
76248616Smm	char		*cmd;
77248616Smm};
78248616Smm
79231200Smmstatic int archive_compressor_program_open(struct archive_write_filter *);
80231200Smmstatic int archive_compressor_program_write(struct archive_write_filter *,
81228753Smm		    const void *, size_t);
82231200Smmstatic int archive_compressor_program_close(struct archive_write_filter *);
83231200Smmstatic int archive_compressor_program_free(struct archive_write_filter *);
84228753Smm
85228753Smm/*
86231200Smm * Add a filter to this write handle that passes all data through an
87231200Smm * external program.
88228753Smm */
89228753Smmint
90231200Smmarchive_write_add_filter_program(struct archive *_a, const char *cmd)
91228753Smm{
92231200Smm	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
93231200Smm	struct private_data *data;
94231200Smm	static const char *prefix = "Program: ";
95248616Smm
96248616Smm	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
97231200Smm	    ARCHIVE_STATE_NEW, "archive_write_add_filter_program");
98248616Smm
99248616Smm	f->data = calloc(1, sizeof(*data));
100248616Smm	if (f->data == NULL)
101248616Smm		goto memerr;
102248616Smm	data = (struct private_data *)f->data;
103248616Smm
104231200Smm	data->cmd = strdup(cmd);
105248616Smm	if (data->cmd == NULL)
106248616Smm		goto memerr;
107231200Smm
108248616Smm	data->pdata = __archive_write_program_allocate();
109248616Smm	if (data->pdata == NULL)
110248616Smm		goto memerr;
111248616Smm
112248616Smm	/* Make up a description string. */
113248616Smm	if (archive_string_ensure(&data->description,
114248616Smm	    strlen(prefix) + strlen(cmd) + 1) == NULL)
115248616Smm		goto memerr;
116248616Smm	archive_strcpy(&data->description, prefix);
117248616Smm	archive_strcat(&data->description, cmd);
118248616Smm
119248616Smm	f->name = data->description.s;
120248616Smm	f->code = ARCHIVE_FILTER_PROGRAM;
121248616Smm	f->open = archive_compressor_program_open;
122248616Smm	f->write = archive_compressor_program_write;
123248616Smm	f->close = archive_compressor_program_close;
124248616Smm	f->free = archive_compressor_program_free;
125228753Smm	return (ARCHIVE_OK);
126248616Smmmemerr:
127248616Smm	archive_compressor_program_free(f);
128248616Smm	archive_set_error(_a, ENOMEM,
129248616Smm	    "Can't allocate memory for filter program");
130248616Smm	return (ARCHIVE_FATAL);
131228753Smm}
132228753Smm
133228753Smmstatic int
134231200Smmarchive_compressor_program_open(struct archive_write_filter *f)
135228753Smm{
136231200Smm	struct private_data *data = (struct private_data *)f->data;
137248616Smm
138248616Smm	return __archive_write_program_open(f, data->pdata, data->cmd);
139248616Smm}
140248616Smm
141248616Smmstatic int
142248616Smmarchive_compressor_program_write(struct archive_write_filter *f,
143248616Smm    const void *buff, size_t length)
144248616Smm{
145248616Smm	struct private_data *data = (struct private_data *)f->data;
146248616Smm
147248616Smm	return __archive_write_program_write(f, data->pdata, buff, length);
148248616Smm}
149248616Smm
150248616Smmstatic int
151248616Smmarchive_compressor_program_close(struct archive_write_filter *f)
152248616Smm{
153248616Smm	struct private_data *data = (struct private_data *)f->data;
154248616Smm
155248616Smm	return __archive_write_program_close(f, data->pdata);
156248616Smm}
157248616Smm
158248616Smmstatic int
159248616Smmarchive_compressor_program_free(struct archive_write_filter *f)
160248616Smm{
161248616Smm	struct private_data *data = (struct private_data *)f->data;
162248616Smm
163248616Smm	if (data) {
164248616Smm		free(data->cmd);
165248616Smm		archive_string_free(&data->description);
166248616Smm		__archive_write_program_free(data->pdata);
167248616Smm		free(data);
168248616Smm		f->data = NULL;
169248616Smm	}
170248616Smm	return (ARCHIVE_OK);
171248616Smm}
172248616Smm
173248616Smm/*
174248616Smm * Allocate resources for executing an external program.
175248616Smm */
176248616Smmstruct archive_write_program_data *
177248616Smm__archive_write_program_allocate(void)
178248616Smm{
179248616Smm	struct archive_write_program_data *data;
180248616Smm
181248616Smm	data = calloc(1, sizeof(struct archive_write_program_data));
182248616Smm	if (data == NULL)
183248616Smm		return (data);
184248616Smm	data->child_stdin = -1;
185248616Smm	data->child_stdout = -1;
186248616Smm	return (data);
187248616Smm}
188248616Smm
189248616Smm/*
190248616Smm * Release the resources.
191248616Smm */
192248616Smmint
193248616Smm__archive_write_program_free(struct archive_write_program_data *data)
194248616Smm{
195248616Smm
196248616Smm	if (data) {
197248616Smm#if defined(_WIN32) && !defined(__CYGWIN__)
198248616Smm		if (data->child)
199248616Smm			CloseHandle(data->child);
200248616Smm#endif
201248616Smm		free(data->child_buf);
202248616Smm		free(data);
203248616Smm	}
204248616Smm	return (ARCHIVE_OK);
205248616Smm}
206248616Smm
207248616Smmint
208248616Smm__archive_write_program_open(struct archive_write_filter *f,
209248616Smm    struct archive_write_program_data *data, const char *cmd)
210248616Smm{
211248616Smm	pid_t child;
212228753Smm	int ret;
213228753Smm
214231200Smm	ret = __archive_write_open_filter(f->next_filter);
215231200Smm	if (ret != ARCHIVE_OK)
216231200Smm		return (ret);
217228753Smm
218231200Smm	if (data->child_buf == NULL) {
219231200Smm		data->child_buf_len = 65536;
220231200Smm		data->child_buf_avail = 0;
221231200Smm		data->child_buf = malloc(data->child_buf_len);
222228753Smm
223231200Smm		if (data->child_buf == NULL) {
224231200Smm			archive_set_error(f->archive, ENOMEM,
225231200Smm			    "Can't allocate compression buffer");
226231200Smm			return (ARCHIVE_FATAL);
227231200Smm		}
228228753Smm	}
229228753Smm
230248616Smm	child = __archive_create_child(cmd, &data->child_stdin,
231248616Smm		    &data->child_stdout);
232248616Smm	if (child == -1) {
233231200Smm		archive_set_error(f->archive, EINVAL,
234228753Smm		    "Can't initialise filter");
235228753Smm		return (ARCHIVE_FATAL);
236228753Smm	}
237248616Smm#if defined(_WIN32) && !defined(__CYGWIN__)
238248616Smm	data->child = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, child);
239248616Smm	if (data->child == NULL) {
240248616Smm		close(data->child_stdin);
241248616Smm		data->child_stdin = -1;
242248616Smm		close(data->child_stdout);
243248616Smm		data->child_stdout = -1;
244248616Smm		archive_set_error(f->archive, EINVAL,
245248616Smm		    "Can't initialise filter");
246248616Smm		return (ARCHIVE_FATAL);
247248616Smm	}
248248616Smm#else
249248616Smm	data->child = child;
250248616Smm#endif
251248616Smm	return (ARCHIVE_OK);
252228753Smm}
253228753Smm
254228753Smmstatic ssize_t
255248616Smmchild_write(struct archive_write_filter *f,
256248616Smm    struct archive_write_program_data *data, const char *buf, size_t buf_len)
257228753Smm{
258228753Smm	ssize_t ret;
259228753Smm
260231200Smm	if (data->child_stdin == -1)
261228753Smm		return (-1);
262228753Smm
263228753Smm	if (buf_len == 0)
264228753Smm		return (-1);
265228753Smm
266238856Smm	for (;;) {
267238856Smm		do {
268238856Smm			ret = write(data->child_stdin, buf, buf_len);
269238856Smm		} while (ret == -1 && errno == EINTR);
270228753Smm
271238856Smm		if (ret > 0)
272238856Smm			return (ret);
273238856Smm		if (ret == 0) {
274238856Smm			close(data->child_stdin);
275238856Smm			data->child_stdin = -1;
276238856Smm			fcntl(data->child_stdout, F_SETFL, 0);
277238856Smm			return (0);
278238856Smm		}
279238856Smm		if (ret == -1 && errno != EAGAIN)
280238856Smm			return (-1);
281228753Smm
282238856Smm		if (data->child_stdout == -1) {
283238856Smm			fcntl(data->child_stdin, F_SETFL, 0);
284238856Smm			__archive_check_child(data->child_stdin,
285238856Smm				data->child_stdout);
286238856Smm			continue;
287238856Smm		}
288228753Smm
289238856Smm		do {
290238856Smm			ret = read(data->child_stdout,
291238856Smm			    data->child_buf + data->child_buf_avail,
292238856Smm			    data->child_buf_len - data->child_buf_avail);
293238856Smm		} while (ret == -1 && errno == EINTR);
294228753Smm
295238856Smm		if (ret == 0 || (ret == -1 && errno == EPIPE)) {
296238856Smm			close(data->child_stdout);
297238856Smm			data->child_stdout = -1;
298238856Smm			fcntl(data->child_stdin, F_SETFL, 0);
299238856Smm			continue;
300238856Smm		}
301238856Smm		if (ret == -1 && errno == EAGAIN) {
302238856Smm			__archive_check_child(data->child_stdin,
303238856Smm				data->child_stdout);
304238856Smm			continue;
305238856Smm		}
306238856Smm		if (ret == -1)
307238856Smm			return (-1);
308228753Smm
309238856Smm		data->child_buf_avail += ret;
310228753Smm
311238856Smm		ret = __archive_write_filter(f->next_filter,
312238856Smm		    data->child_buf, data->child_buf_avail);
313248616Smm		if (ret != ARCHIVE_OK)
314238856Smm			return (-1);
315248616Smm		data->child_buf_avail = 0;
316228753Smm	}
317228753Smm}
318228753Smm
319228753Smm/*
320248616Smm * Write data to the filter stream.
321228753Smm */
322248616Smmint
323248616Smm__archive_write_program_write(struct archive_write_filter *f,
324248616Smm    struct archive_write_program_data *data, const void *buff, size_t length)
325228753Smm{
326228753Smm	ssize_t ret;
327228753Smm	const char *buf;
328228753Smm
329248616Smm	if (data->child == 0)
330248616Smm		return (ARCHIVE_OK);
331248616Smm
332228753Smm	buf = buff;
333228753Smm	while (length > 0) {
334248616Smm		ret = child_write(f, data, buf, length);
335228753Smm		if (ret == -1 || ret == 0) {
336231200Smm			archive_set_error(f->archive, EIO,
337228753Smm			    "Can't write to filter");
338228753Smm			return (ARCHIVE_FATAL);
339228753Smm		}
340228753Smm		length -= ret;
341228753Smm		buf += ret;
342228753Smm	}
343228753Smm	return (ARCHIVE_OK);
344228753Smm}
345228753Smm
346228753Smm/*
347248616Smm * Finish the filtering...
348228753Smm */
349248616Smmint
350248616Smm__archive_write_program_close(struct archive_write_filter *f,
351248616Smm    struct archive_write_program_data *data)
352228753Smm{
353231200Smm	int ret, r1, status;
354231200Smm	ssize_t bytes_read;
355228753Smm
356248616Smm	if (data->child == 0)
357248616Smm		return __archive_write_close_filter(f->next_filter);
358248616Smm
359228753Smm	ret = 0;
360231200Smm	close(data->child_stdin);
361231200Smm	data->child_stdin = -1;
362231200Smm	fcntl(data->child_stdout, F_SETFL, 0);
363228753Smm
364228753Smm	for (;;) {
365228753Smm		do {
366231200Smm			bytes_read = read(data->child_stdout,
367231200Smm			    data->child_buf + data->child_buf_avail,
368231200Smm			    data->child_buf_len - data->child_buf_avail);
369228753Smm		} while (bytes_read == -1 && errno == EINTR);
370228753Smm
371228753Smm		if (bytes_read == 0 || (bytes_read == -1 && errno == EPIPE))
372228753Smm			break;
373228753Smm
374228753Smm		if (bytes_read == -1) {
375231200Smm			archive_set_error(f->archive, errno,
376228753Smm			    "Read from filter failed unexpectedly.");
377228753Smm			ret = ARCHIVE_FATAL;
378228753Smm			goto cleanup;
379228753Smm		}
380231200Smm		data->child_buf_avail += bytes_read;
381228753Smm
382231200Smm		ret = __archive_write_filter(f->next_filter,
383231200Smm		    data->child_buf, data->child_buf_avail);
384231200Smm		if (ret != ARCHIVE_OK) {
385228753Smm			ret = ARCHIVE_FATAL;
386228753Smm			goto cleanup;
387228753Smm		}
388231200Smm		data->child_buf_avail = 0;
389228753Smm	}
390228753Smm
391228753Smmcleanup:
392228753Smm	/* Shut down the child. */
393231200Smm	if (data->child_stdin != -1)
394231200Smm		close(data->child_stdin);
395231200Smm	if (data->child_stdout != -1)
396231200Smm		close(data->child_stdout);
397231200Smm	while (waitpid(data->child, &status, 0) == -1 && errno == EINTR)
398228753Smm		continue;
399248616Smm#if defined(_WIN32) && !defined(__CYGWIN__)
400248616Smm	CloseHandle(data->child);
401248616Smm#endif
402248616Smm	data->child = 0;
403228753Smm
404228753Smm	if (status != 0) {
405231200Smm		archive_set_error(f->archive, EIO,
406228753Smm		    "Filter exited with failure.");
407228753Smm		ret = ARCHIVE_FATAL;
408228753Smm	}
409231200Smm	r1 = __archive_write_close_filter(f->next_filter);
410231200Smm	return (r1 < ret ? r1 : ret);
411231200Smm}
412228753Smm
413