1228753Smm/*- 2228753Smm * Copyright (c) 2007 Joerg Sonnenberger 3228753Smm * All rights reserved. 4228753Smm * 5228753Smm * Redistribution and use in source and binary forms, with or without 6228753Smm * modification, are permitted provided that the following conditions 7228753Smm * are met: 8228753Smm * 1. Redistributions of source code must retain the above copyright 9228753Smm * notice, this list of conditions and the following disclaimer. 10228753Smm * 2. Redistributions in binary form must reproduce the above copyright 11228753Smm * notice, this list of conditions and the following disclaimer in the 12228753Smm * documentation and/or other materials provided with the distribution. 13228753Smm * 14228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24228753Smm */ 25228753Smm 26228753Smm#include "archive_platform.h" 27228753Smm 28229592Smm__FBSDID("$FreeBSD$"); 29228753Smm 30228753Smm/* This capability is only available on POSIX systems. */ 31228753Smm#if (!defined(HAVE_PIPE) || !defined(HAVE_FCNTL) || \ 32228753Smm !(defined(HAVE_FORK) || defined(HAVE_VFORK))) && (!defined(_WIN32) || defined(__CYGWIN__)) 33228753Smm#include "archive.h" 34228753Smm 35228753Smm/* 36228753Smm * On non-Posix systems, allow the program to build, but choke if 37228753Smm * this function is actually invoked. 38228753Smm */ 39228753Smmint 40228753Smmarchive_write_set_compression_program(struct archive *_a, const char *cmd) 41228753Smm{ 42228753Smm archive_set_error(_a, -1, 43228753Smm "External compression programs not supported on this platform"); 44228753Smm return (ARCHIVE_FATAL); 45228753Smm} 46228753Smm 47228753Smm#else 48228753Smm 49228753Smm#ifdef HAVE_SYS_WAIT_H 50228753Smm# include <sys/wait.h> 51228753Smm#endif 52228753Smm#ifdef HAVE_ERRNO_H 53228753Smm# include <errno.h> 54228753Smm#endif 55228753Smm#ifdef HAVE_FCNTL_H 56228753Smm# include <fcntl.h> 57228753Smm#endif 58228753Smm#ifdef HAVE_STDLIB_H 59228753Smm# include <stdlib.h> 60228753Smm#endif 61228753Smm#ifdef HAVE_STRING_H 62228753Smm# include <string.h> 63228753Smm#endif 64228753Smm 65228753Smm#include "archive.h" 66228753Smm#include "archive_private.h" 67228753Smm#include "archive_write_private.h" 68228753Smm 69228753Smm#include "filter_fork.h" 70228753Smm 71228753Smmstruct private_data { 72228753Smm char *description; 73228753Smm pid_t child; 74228753Smm int child_stdin, child_stdout; 75228753Smm 76228753Smm char *child_buf; 77228753Smm size_t child_buf_len, child_buf_avail; 78228753Smm}; 79228753Smm 80228753Smmstatic int archive_compressor_program_finish(struct archive_write *); 81228753Smmstatic int archive_compressor_program_init(struct archive_write *); 82228753Smmstatic int archive_compressor_program_write(struct archive_write *, 83228753Smm const void *, size_t); 84228753Smm 85228753Smm/* 86228753Smm * Allocate, initialize and return a archive object. 87228753Smm */ 88228753Smmint 89228753Smmarchive_write_set_compression_program(struct archive *_a, const char *cmd) 90228753Smm{ 91228753Smm struct archive_write *a = (struct archive_write *)_a; 92228753Smm __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, 93228753Smm ARCHIVE_STATE_NEW, "archive_write_set_compression_program"); 94228753Smm a->compressor.init = &archive_compressor_program_init; 95228753Smm a->compressor.config = strdup(cmd); 96228753Smm return (ARCHIVE_OK); 97228753Smm} 98228753Smm 99228753Smm/* 100228753Smm * Setup callback. 101228753Smm */ 102228753Smmstatic int 103228753Smmarchive_compressor_program_init(struct archive_write *a) 104228753Smm{ 105228753Smm int ret; 106228753Smm struct private_data *state; 107228753Smm static const char *prefix = "Program: "; 108228753Smm char *cmd = a->compressor.config; 109228753Smm 110228753Smm if (a->client_opener != NULL) { 111228753Smm ret = (a->client_opener)(&a->archive, a->client_data); 112228753Smm if (ret != ARCHIVE_OK) 113228753Smm return (ret); 114228753Smm } 115228753Smm 116228753Smm state = (struct private_data *)malloc(sizeof(*state)); 117228753Smm if (state == NULL) { 118228753Smm archive_set_error(&a->archive, ENOMEM, 119228753Smm "Can't allocate data for compression"); 120228753Smm return (ARCHIVE_FATAL); 121228753Smm } 122228753Smm memset(state, 0, sizeof(*state)); 123228753Smm 124228753Smm a->archive.compression_code = ARCHIVE_COMPRESSION_PROGRAM; 125228753Smm state->description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1); 126228753Smm strcpy(state->description, prefix); 127228753Smm strcat(state->description, cmd); 128228753Smm a->archive.compression_name = state->description; 129228753Smm 130228753Smm state->child_buf_len = a->bytes_per_block; 131228753Smm state->child_buf_avail = 0; 132228753Smm state->child_buf = malloc(state->child_buf_len); 133228753Smm 134228753Smm if (state->child_buf == NULL) { 135228753Smm archive_set_error(&a->archive, ENOMEM, 136228753Smm "Can't allocate data for compression buffer"); 137228753Smm free(state); 138228753Smm return (ARCHIVE_FATAL); 139228753Smm } 140228753Smm 141228753Smm if ((state->child = __archive_create_child(cmd, 142228753Smm &state->child_stdin, &state->child_stdout)) == -1) { 143228753Smm archive_set_error(&a->archive, EINVAL, 144228753Smm "Can't initialise filter"); 145228753Smm free(state->child_buf); 146228753Smm free(state); 147228753Smm return (ARCHIVE_FATAL); 148228753Smm } 149228753Smm 150228753Smm a->compressor.write = archive_compressor_program_write; 151228753Smm a->compressor.finish = archive_compressor_program_finish; 152228753Smm 153228753Smm a->compressor.data = state; 154228753Smm return (0); 155228753Smm} 156228753Smm 157228753Smmstatic ssize_t 158228753Smmchild_write(struct archive_write *a, const char *buf, size_t buf_len) 159228753Smm{ 160228753Smm struct private_data *state = a->compressor.data; 161228753Smm ssize_t ret; 162228753Smm 163228753Smm if (state->child_stdin == -1) 164228753Smm return (-1); 165228753Smm 166228753Smm if (buf_len == 0) 167228753Smm return (-1); 168228753Smm 169228753Smmrestart_write: 170228753Smm do { 171228753Smm ret = write(state->child_stdin, buf, buf_len); 172228753Smm } while (ret == -1 && errno == EINTR); 173228753Smm 174228753Smm if (ret > 0) 175228753Smm return (ret); 176228753Smm if (ret == 0) { 177228753Smm close(state->child_stdin); 178228753Smm state->child_stdin = -1; 179228753Smm fcntl(state->child_stdout, F_SETFL, 0); 180228753Smm return (0); 181228753Smm } 182228753Smm if (ret == -1 && errno != EAGAIN) 183228753Smm return (-1); 184228753Smm 185228753Smm if (state->child_stdout == -1) { 186228753Smm fcntl(state->child_stdin, F_SETFL, 0); 187228753Smm __archive_check_child(state->child_stdin, state->child_stdout); 188228753Smm goto restart_write; 189228753Smm } 190228753Smm 191228753Smm do { 192228753Smm ret = read(state->child_stdout, 193228753Smm state->child_buf + state->child_buf_avail, 194228753Smm state->child_buf_len - state->child_buf_avail); 195228753Smm } while (ret == -1 && errno == EINTR); 196228753Smm 197228753Smm if (ret == 0 || (ret == -1 && errno == EPIPE)) { 198228753Smm close(state->child_stdout); 199228753Smm state->child_stdout = -1; 200228753Smm fcntl(state->child_stdin, F_SETFL, 0); 201228753Smm goto restart_write; 202228753Smm } 203228753Smm if (ret == -1 && errno == EAGAIN) { 204228753Smm __archive_check_child(state->child_stdin, state->child_stdout); 205228753Smm goto restart_write; 206228753Smm } 207228753Smm if (ret == -1) 208228753Smm return (-1); 209228753Smm 210228753Smm state->child_buf_avail += ret; 211228753Smm 212228753Smm ret = (a->client_writer)(&a->archive, a->client_data, 213228753Smm state->child_buf, state->child_buf_avail); 214228753Smm if (ret <= 0) 215228753Smm return (-1); 216228753Smm 217228753Smm if ((size_t)ret < state->child_buf_avail) { 218228753Smm memmove(state->child_buf, state->child_buf + ret, 219228753Smm state->child_buf_avail - ret); 220228753Smm } 221228753Smm state->child_buf_avail -= ret; 222228753Smm a->archive.raw_position += ret; 223228753Smm goto restart_write; 224228753Smm} 225228753Smm 226228753Smm/* 227228753Smm * Write data to the compressed stream. 228228753Smm */ 229228753Smmstatic int 230228753Smmarchive_compressor_program_write(struct archive_write *a, const void *buff, 231228753Smm size_t length) 232228753Smm{ 233228753Smm ssize_t ret; 234228753Smm const char *buf; 235228753Smm 236228753Smm if (a->client_writer == NULL) { 237228753Smm archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, 238228753Smm "No write callback is registered? " 239228753Smm "This is probably an internal programming error."); 240228753Smm return (ARCHIVE_FATAL); 241228753Smm } 242228753Smm 243228753Smm buf = buff; 244228753Smm while (length > 0) { 245228753Smm ret = child_write(a, buf, length); 246228753Smm if (ret == -1 || ret == 0) { 247228753Smm archive_set_error(&a->archive, EIO, 248228753Smm "Can't write to filter"); 249228753Smm return (ARCHIVE_FATAL); 250228753Smm } 251228753Smm length -= ret; 252228753Smm buf += ret; 253228753Smm } 254228753Smm 255228753Smm a->archive.file_position += length; 256228753Smm return (ARCHIVE_OK); 257228753Smm} 258228753Smm 259228753Smm 260228753Smm/* 261228753Smm * Finish the compression... 262228753Smm */ 263228753Smmstatic int 264228753Smmarchive_compressor_program_finish(struct archive_write *a) 265228753Smm{ 266228753Smm int ret, status; 267228753Smm ssize_t bytes_read, bytes_written; 268228753Smm struct private_data *state; 269228753Smm 270228753Smm state = (struct private_data *)a->compressor.data; 271228753Smm ret = 0; 272228753Smm if (a->client_writer == NULL) { 273228753Smm archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, 274228753Smm "No write callback is registered? " 275228753Smm "This is probably an internal programming error."); 276228753Smm ret = ARCHIVE_FATAL; 277228753Smm goto cleanup; 278228753Smm } 279228753Smm 280228753Smm /* XXX pad compressed data. */ 281228753Smm 282228753Smm close(state->child_stdin); 283228753Smm state->child_stdin = -1; 284228753Smm fcntl(state->child_stdout, F_SETFL, 0); 285228753Smm 286228753Smm for (;;) { 287228753Smm do { 288228753Smm bytes_read = read(state->child_stdout, 289228753Smm state->child_buf + state->child_buf_avail, 290228753Smm state->child_buf_len - state->child_buf_avail); 291228753Smm } while (bytes_read == -1 && errno == EINTR); 292228753Smm 293228753Smm if (bytes_read == 0 || (bytes_read == -1 && errno == EPIPE)) 294228753Smm break; 295228753Smm 296228753Smm if (bytes_read == -1) { 297228753Smm archive_set_error(&a->archive, errno, 298228753Smm "Read from filter failed unexpectedly."); 299228753Smm ret = ARCHIVE_FATAL; 300228753Smm goto cleanup; 301228753Smm } 302228753Smm state->child_buf_avail += bytes_read; 303228753Smm 304228753Smm bytes_written = (a->client_writer)(&a->archive, a->client_data, 305228753Smm state->child_buf, state->child_buf_avail); 306228753Smm if (bytes_written <= 0) { 307228753Smm ret = ARCHIVE_FATAL; 308228753Smm goto cleanup; 309228753Smm } 310228753Smm if ((size_t)bytes_written < state->child_buf_avail) { 311228753Smm memmove(state->child_buf, 312228753Smm state->child_buf + bytes_written, 313228753Smm state->child_buf_avail - bytes_written); 314228753Smm } 315228753Smm state->child_buf_avail -= bytes_written; 316228753Smm a->archive.raw_position += bytes_written; 317228753Smm } 318228753Smm 319228753Smm /* XXX pad final compressed block. */ 320228753Smm 321228753Smmcleanup: 322228753Smm /* Shut down the child. */ 323228753Smm if (state->child_stdin != -1) 324228753Smm close(state->child_stdin); 325228753Smm if (state->child_stdout != -1) 326228753Smm close(state->child_stdout); 327228753Smm while (waitpid(state->child, &status, 0) == -1 && errno == EINTR) 328228753Smm continue; 329228753Smm 330228753Smm if (status != 0) { 331228753Smm archive_set_error(&a->archive, EIO, 332228753Smm "Filter exited with failure."); 333228753Smm ret = ARCHIVE_FATAL; 334228753Smm } 335228753Smm 336228753Smm /* Release our configuration data. */ 337228753Smm free(a->compressor.config); 338228753Smm a->compressor.config = NULL; 339228753Smm 340228753Smm /* Release our private state data. */ 341228753Smm free(state->child_buf); 342228753Smm free(state->description); 343228753Smm free(state); 344228753Smm return (ret); 345228753Smm} 346228753Smm 347228753Smm#endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */ 348