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 29 #ifdef HAVE_SYS_WAIT_H 30 # include <sys/wait.h> 31 #endif 32 #ifdef HAVE_ERRNO_H 33 # include <errno.h> 34 #endif 35 #ifdef HAVE_FCNTL_H 36 # include <fcntl.h> 37 #endif 38 #ifdef HAVE_STDLIB_H 39 # include <stdlib.h> 40 #endif 41 #ifdef HAVE_STRING_H 42 # include <string.h> 43 #endif 44 45 #include "archive.h" 46 #include "archive_private.h" 47 #include "archive_string.h" 48 #include "archive_write_private.h" 49 #include "filter_fork.h" 50 51 #if ARCHIVE_VERSION_NUMBER < 4000000 52 int 53 archive_write_set_compression_program(struct archive *a, const char *cmd) 54 { 55 __archive_write_filters_free(a); 56 return (archive_write_add_filter_program(a, cmd)); 57 } 58 #endif 59 60 struct archive_write_program_data { 61 #if defined(_WIN32) && !defined(__CYGWIN__) 62 HANDLE child; 63 #else 64 pid_t child; 65 #endif 66 int child_stdin, child_stdout; 67 68 char *child_buf; 69 size_t child_buf_len, child_buf_avail; 70 char *program_name; 71 }; 72 73 struct private_data { 74 struct archive_write_program_data *pdata; 75 struct archive_string description; 76 char *cmd; 77 }; 78 79 static int archive_compressor_program_open(struct archive_write_filter *); 80 static int archive_compressor_program_write(struct archive_write_filter *, 81 const void *, size_t); 82 static int archive_compressor_program_close(struct archive_write_filter *); 83 static 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 */ 89 int 90 archive_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(cmd); 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); 126 memerr: 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 133 static int 134 archive_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 141 static int 142 archive_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 150 static int 151 archive_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 158 static int 159 archive_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 */ 176 struct archive_write_program_data * 177 __archive_write_program_allocate(const char *program) 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 data->program_name = strdup(program); 187 return (data); 188 } 189 190 /* 191 * Release the resources. 192 */ 193 int 194 __archive_write_program_free(struct archive_write_program_data *data) 195 { 196 197 if (data) { 198 free(data->program_name); 199 free(data->child_buf); 200 free(data); 201 } 202 return (ARCHIVE_OK); 203 } 204 205 int 206 __archive_write_program_open(struct archive_write_filter *f, 207 struct archive_write_program_data *data, const char *cmd) 208 { 209 int ret; 210 211 if (data->child_buf == NULL) { 212 data->child_buf_len = 65536; 213 data->child_buf_avail = 0; 214 data->child_buf = malloc(data->child_buf_len); 215 216 if (data->child_buf == NULL) { 217 archive_set_error(f->archive, ENOMEM, 218 "Can't allocate compression buffer"); 219 return (ARCHIVE_FATAL); 220 } 221 } 222 223 ret = __archive_create_child(cmd, &data->child_stdin, 224 &data->child_stdout, &data->child); 225 if (ret != ARCHIVE_OK) { 226 archive_set_error(f->archive, EINVAL, 227 "Can't launch external program: %s", cmd); 228 return (ARCHIVE_FATAL); 229 } 230 return (ARCHIVE_OK); 231 } 232 233 static ssize_t 234 child_write(struct archive_write_filter *f, 235 struct archive_write_program_data *data, const char *buf, size_t buf_len) 236 { 237 ssize_t ret; 238 239 if (data->child_stdin == -1) 240 return (-1); 241 242 if (buf_len == 0) 243 return (-1); 244 245 for (;;) { 246 do { 247 ret = write(data->child_stdin, buf, buf_len); 248 } while (ret == -1 && errno == EINTR); 249 250 if (ret > 0) 251 return (ret); 252 if (ret == 0) { 253 close(data->child_stdin); 254 data->child_stdin = -1; 255 fcntl(data->child_stdout, F_SETFL, 0); 256 return (0); 257 } 258 if (ret == -1 && errno != EAGAIN) 259 return (-1); 260 261 if (data->child_stdout == -1) { 262 fcntl(data->child_stdin, F_SETFL, 0); 263 __archive_check_child(data->child_stdin, 264 data->child_stdout); 265 continue; 266 } 267 268 do { 269 ret = read(data->child_stdout, 270 data->child_buf + data->child_buf_avail, 271 data->child_buf_len - data->child_buf_avail); 272 } while (ret == -1 && errno == EINTR); 273 274 if (ret == 0 || (ret == -1 && errno == EPIPE)) { 275 close(data->child_stdout); 276 data->child_stdout = -1; 277 fcntl(data->child_stdin, F_SETFL, 0); 278 continue; 279 } 280 if (ret == -1 && errno == EAGAIN) { 281 __archive_check_child(data->child_stdin, 282 data->child_stdout); 283 continue; 284 } 285 if (ret == -1) 286 return (-1); 287 288 data->child_buf_avail += ret; 289 290 ret = __archive_write_filter(f->next_filter, 291 data->child_buf, data->child_buf_avail); 292 if (ret != ARCHIVE_OK) 293 return (-1); 294 data->child_buf_avail = 0; 295 } 296 } 297 298 /* 299 * Write data to the filter stream. 300 */ 301 int 302 __archive_write_program_write(struct archive_write_filter *f, 303 struct archive_write_program_data *data, const void *buff, size_t length) 304 { 305 ssize_t ret; 306 const char *buf; 307 308 if (data->child == 0) 309 return (ARCHIVE_OK); 310 311 buf = buff; 312 while (length > 0) { 313 ret = child_write(f, data, buf, length); 314 if (ret == -1 || ret == 0) { 315 archive_set_error(f->archive, EIO, 316 "Can't write to program: %s", data->program_name); 317 return (ARCHIVE_FATAL); 318 } 319 length -= ret; 320 buf += ret; 321 } 322 return (ARCHIVE_OK); 323 } 324 325 /* 326 * Finish the filtering... 327 */ 328 int 329 __archive_write_program_close(struct archive_write_filter *f, 330 struct archive_write_program_data *data) 331 { 332 int ret, status; 333 ssize_t bytes_read; 334 335 if (data->child == 0) 336 return ARCHIVE_OK; 337 338 ret = 0; 339 close(data->child_stdin); 340 data->child_stdin = -1; 341 fcntl(data->child_stdout, F_SETFL, 0); 342 343 for (;;) { 344 do { 345 bytes_read = read(data->child_stdout, 346 data->child_buf + data->child_buf_avail, 347 data->child_buf_len - data->child_buf_avail); 348 } while (bytes_read == -1 && errno == EINTR); 349 350 if (bytes_read == 0 || (bytes_read == -1 && errno == EPIPE)) 351 break; 352 353 if (bytes_read == -1) { 354 archive_set_error(f->archive, errno, 355 "Error reading from program: %s", data->program_name); 356 ret = ARCHIVE_FATAL; 357 goto cleanup; 358 } 359 data->child_buf_avail += bytes_read; 360 361 ret = __archive_write_filter(f->next_filter, 362 data->child_buf, data->child_buf_avail); 363 if (ret != ARCHIVE_OK) { 364 ret = ARCHIVE_FATAL; 365 goto cleanup; 366 } 367 data->child_buf_avail = 0; 368 } 369 370 cleanup: 371 /* Shut down the child. */ 372 if (data->child_stdin != -1) 373 close(data->child_stdin); 374 if (data->child_stdout != -1) 375 close(data->child_stdout); 376 while (waitpid(data->child, &status, 0) == -1 && errno == EINTR) 377 continue; 378 #if defined(_WIN32) && !defined(__CYGWIN__) 379 CloseHandle(data->child); 380 #endif 381 data->child = 0; 382 383 if (status != 0) { 384 archive_set_error(f->archive, EIO, 385 "Error closing program: %s", data->program_name); 386 ret = ARCHIVE_FATAL; 387 } 388 return ret; 389 } 390 391