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