archive_write_set_compression_program.c revision 229592
1/*- 2 * Copyright (c) 2007 Joerg Sonnenberger 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__FBSDID("$FreeBSD: stable/9/contrib/libarchive/libarchive/archive_write_set_compression_program.c 229592 2012-01-05 12:06:54Z mm $"); 29 30/* This capability is only available on POSIX systems. */ 31#if (!defined(HAVE_PIPE) || !defined(HAVE_FCNTL) || \ 32 !(defined(HAVE_FORK) || defined(HAVE_VFORK))) && (!defined(_WIN32) || defined(__CYGWIN__)) 33#include "archive.h" 34 35/* 36 * On non-Posix systems, allow the program to build, but choke if 37 * this function is actually invoked. 38 */ 39int 40archive_write_set_compression_program(struct archive *_a, const char *cmd) 41{ 42 archive_set_error(_a, -1, 43 "External compression programs not supported on this platform"); 44 return (ARCHIVE_FATAL); 45} 46 47#else 48 49#ifdef HAVE_SYS_WAIT_H 50# include <sys/wait.h> 51#endif 52#ifdef HAVE_ERRNO_H 53# include <errno.h> 54#endif 55#ifdef HAVE_FCNTL_H 56# include <fcntl.h> 57#endif 58#ifdef HAVE_STDLIB_H 59# include <stdlib.h> 60#endif 61#ifdef HAVE_STRING_H 62# include <string.h> 63#endif 64 65#include "archive.h" 66#include "archive_private.h" 67#include "archive_write_private.h" 68 69#include "filter_fork.h" 70 71struct private_data { 72 char *description; 73 pid_t child; 74 int child_stdin, child_stdout; 75 76 char *child_buf; 77 size_t child_buf_len, child_buf_avail; 78}; 79 80static int archive_compressor_program_finish(struct archive_write *); 81static int archive_compressor_program_init(struct archive_write *); 82static int archive_compressor_program_write(struct archive_write *, 83 const void *, size_t); 84 85/* 86 * Allocate, initialize and return a archive object. 87 */ 88int 89archive_write_set_compression_program(struct archive *_a, const char *cmd) 90{ 91 struct archive_write *a = (struct archive_write *)_a; 92 __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, 93 ARCHIVE_STATE_NEW, "archive_write_set_compression_program"); 94 a->compressor.init = &archive_compressor_program_init; 95 a->compressor.config = strdup(cmd); 96 return (ARCHIVE_OK); 97} 98 99/* 100 * Setup callback. 101 */ 102static int 103archive_compressor_program_init(struct archive_write *a) 104{ 105 int ret; 106 struct private_data *state; 107 static const char *prefix = "Program: "; 108 char *cmd = a->compressor.config; 109 110 if (a->client_opener != NULL) { 111 ret = (a->client_opener)(&a->archive, a->client_data); 112 if (ret != ARCHIVE_OK) 113 return (ret); 114 } 115 116 state = (struct private_data *)malloc(sizeof(*state)); 117 if (state == NULL) { 118 archive_set_error(&a->archive, ENOMEM, 119 "Can't allocate data for compression"); 120 return (ARCHIVE_FATAL); 121 } 122 memset(state, 0, sizeof(*state)); 123 124 a->archive.compression_code = ARCHIVE_COMPRESSION_PROGRAM; 125 state->description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1); 126 strcpy(state->description, prefix); 127 strcat(state->description, cmd); 128 a->archive.compression_name = state->description; 129 130 state->child_buf_len = a->bytes_per_block; 131 state->child_buf_avail = 0; 132 state->child_buf = malloc(state->child_buf_len); 133 134 if (state->child_buf == NULL) { 135 archive_set_error(&a->archive, ENOMEM, 136 "Can't allocate data for compression buffer"); 137 free(state); 138 return (ARCHIVE_FATAL); 139 } 140 141 if ((state->child = __archive_create_child(cmd, 142 &state->child_stdin, &state->child_stdout)) == -1) { 143 archive_set_error(&a->archive, EINVAL, 144 "Can't initialise filter"); 145 free(state->child_buf); 146 free(state); 147 return (ARCHIVE_FATAL); 148 } 149 150 a->compressor.write = archive_compressor_program_write; 151 a->compressor.finish = archive_compressor_program_finish; 152 153 a->compressor.data = state; 154 return (0); 155} 156 157static ssize_t 158child_write(struct archive_write *a, const char *buf, size_t buf_len) 159{ 160 struct private_data *state = a->compressor.data; 161 ssize_t ret; 162 163 if (state->child_stdin == -1) 164 return (-1); 165 166 if (buf_len == 0) 167 return (-1); 168 169restart_write: 170 do { 171 ret = write(state->child_stdin, buf, buf_len); 172 } while (ret == -1 && errno == EINTR); 173 174 if (ret > 0) 175 return (ret); 176 if (ret == 0) { 177 close(state->child_stdin); 178 state->child_stdin = -1; 179 fcntl(state->child_stdout, F_SETFL, 0); 180 return (0); 181 } 182 if (ret == -1 && errno != EAGAIN) 183 return (-1); 184 185 if (state->child_stdout == -1) { 186 fcntl(state->child_stdin, F_SETFL, 0); 187 __archive_check_child(state->child_stdin, state->child_stdout); 188 goto restart_write; 189 } 190 191 do { 192 ret = read(state->child_stdout, 193 state->child_buf + state->child_buf_avail, 194 state->child_buf_len - state->child_buf_avail); 195 } while (ret == -1 && errno == EINTR); 196 197 if (ret == 0 || (ret == -1 && errno == EPIPE)) { 198 close(state->child_stdout); 199 state->child_stdout = -1; 200 fcntl(state->child_stdin, F_SETFL, 0); 201 goto restart_write; 202 } 203 if (ret == -1 && errno == EAGAIN) { 204 __archive_check_child(state->child_stdin, state->child_stdout); 205 goto restart_write; 206 } 207 if (ret == -1) 208 return (-1); 209 210 state->child_buf_avail += ret; 211 212 ret = (a->client_writer)(&a->archive, a->client_data, 213 state->child_buf, state->child_buf_avail); 214 if (ret <= 0) 215 return (-1); 216 217 if ((size_t)ret < state->child_buf_avail) { 218 memmove(state->child_buf, state->child_buf + ret, 219 state->child_buf_avail - ret); 220 } 221 state->child_buf_avail -= ret; 222 a->archive.raw_position += ret; 223 goto restart_write; 224} 225 226/* 227 * Write data to the compressed stream. 228 */ 229static int 230archive_compressor_program_write(struct archive_write *a, const void *buff, 231 size_t length) 232{ 233 ssize_t ret; 234 const char *buf; 235 236 if (a->client_writer == NULL) { 237 archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, 238 "No write callback is registered? " 239 "This is probably an internal programming error."); 240 return (ARCHIVE_FATAL); 241 } 242 243 buf = buff; 244 while (length > 0) { 245 ret = child_write(a, buf, length); 246 if (ret == -1 || ret == 0) { 247 archive_set_error(&a->archive, EIO, 248 "Can't write to filter"); 249 return (ARCHIVE_FATAL); 250 } 251 length -= ret; 252 buf += ret; 253 } 254 255 a->archive.file_position += length; 256 return (ARCHIVE_OK); 257} 258 259 260/* 261 * Finish the compression... 262 */ 263static int 264archive_compressor_program_finish(struct archive_write *a) 265{ 266 int ret, status; 267 ssize_t bytes_read, bytes_written; 268 struct private_data *state; 269 270 state = (struct private_data *)a->compressor.data; 271 ret = 0; 272 if (a->client_writer == NULL) { 273 archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, 274 "No write callback is registered? " 275 "This is probably an internal programming error."); 276 ret = ARCHIVE_FATAL; 277 goto cleanup; 278 } 279 280 /* XXX pad compressed data. */ 281 282 close(state->child_stdin); 283 state->child_stdin = -1; 284 fcntl(state->child_stdout, F_SETFL, 0); 285 286 for (;;) { 287 do { 288 bytes_read = read(state->child_stdout, 289 state->child_buf + state->child_buf_avail, 290 state->child_buf_len - state->child_buf_avail); 291 } while (bytes_read == -1 && errno == EINTR); 292 293 if (bytes_read == 0 || (bytes_read == -1 && errno == EPIPE)) 294 break; 295 296 if (bytes_read == -1) { 297 archive_set_error(&a->archive, errno, 298 "Read from filter failed unexpectedly."); 299 ret = ARCHIVE_FATAL; 300 goto cleanup; 301 } 302 state->child_buf_avail += bytes_read; 303 304 bytes_written = (a->client_writer)(&a->archive, a->client_data, 305 state->child_buf, state->child_buf_avail); 306 if (bytes_written <= 0) { 307 ret = ARCHIVE_FATAL; 308 goto cleanup; 309 } 310 if ((size_t)bytes_written < state->child_buf_avail) { 311 memmove(state->child_buf, 312 state->child_buf + bytes_written, 313 state->child_buf_avail - bytes_written); 314 } 315 state->child_buf_avail -= bytes_written; 316 a->archive.raw_position += bytes_written; 317 } 318 319 /* XXX pad final compressed block. */ 320 321cleanup: 322 /* Shut down the child. */ 323 if (state->child_stdin != -1) 324 close(state->child_stdin); 325 if (state->child_stdout != -1) 326 close(state->child_stdout); 327 while (waitpid(state->child, &status, 0) == -1 && errno == EINTR) 328 continue; 329 330 if (status != 0) { 331 archive_set_error(&a->archive, EIO, 332 "Filter exited with failure."); 333 ret = ARCHIVE_FATAL; 334 } 335 336 /* Release our configuration data. */ 337 free(a->compressor.config); 338 a->compressor.config = NULL; 339 340 /* Release our private state data. */ 341 free(state->child_buf); 342 free(state->description); 343 free(state); 344 return (ret); 345} 346 347#endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */ 348