1228753Smm/*- 2228753Smm * Copyright (c) 2003-2007 Tim Kientzle 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" 27229592Smm__FBSDID("$FreeBSD$"); 28228753Smm 29228753Smm#ifdef HAVE_SYS_TYPES_H 30228753Smm#include <sys/types.h> 31228753Smm#endif 32228753Smm#ifdef HAVE_ERRNO_H 33228753Smm#include <errno.h> 34228753Smm#endif 35228753Smm#ifdef HAVE_STDLIB_H 36228753Smm#include <stdlib.h> 37228753Smm#endif 38228753Smm#ifdef HAVE_STRING_H 39228753Smm#include <string.h> 40228753Smm#endif 41228753Smm 42228753Smm#include "archive.h" 43228753Smm#include "archive_entry.h" 44228753Smm#include "archive_private.h" 45228753Smm#include "archive_read_private.h" 46228753Smm#include "archive_write_disk_private.h" 47228753Smm 48228753Smmstruct extract { 49228753Smm struct archive *ad; /* archive_write_disk object */ 50228753Smm 51228753Smm /* Progress function invoked during extract. */ 52228753Smm void (*extract_progress)(void *); 53228753Smm void *extract_progress_user_data; 54228753Smm}; 55228753Smm 56228753Smmstatic int archive_read_extract_cleanup(struct archive_read *); 57228753Smmstatic int copy_data(struct archive *ar, struct archive *aw); 58228753Smmstatic struct extract *get_extract(struct archive_read *); 59228753Smm 60228753Smmstatic struct extract * 61228753Smmget_extract(struct archive_read *a) 62228753Smm{ 63228753Smm /* If we haven't initialized, do it now. */ 64228753Smm /* This also sets up a lot of global state. */ 65228753Smm if (a->extract == NULL) { 66228753Smm a->extract = (struct extract *)malloc(sizeof(*a->extract)); 67228753Smm if (a->extract == NULL) { 68228753Smm archive_set_error(&a->archive, ENOMEM, "Can't extract"); 69228753Smm return (NULL); 70228753Smm } 71228753Smm memset(a->extract, 0, sizeof(*a->extract)); 72228753Smm a->extract->ad = archive_write_disk_new(); 73228753Smm if (a->extract->ad == NULL) { 74228753Smm archive_set_error(&a->archive, ENOMEM, "Can't extract"); 75228753Smm return (NULL); 76228753Smm } 77228753Smm archive_write_disk_set_standard_lookup(a->extract->ad); 78228753Smm a->cleanup_archive_extract = archive_read_extract_cleanup; 79228753Smm } 80228753Smm return (a->extract); 81228753Smm} 82228753Smm 83228753Smmint 84228753Smmarchive_read_extract(struct archive *_a, struct archive_entry *entry, int flags) 85228753Smm{ 86228753Smm struct extract *extract; 87228753Smm 88228753Smm extract = get_extract((struct archive_read *)_a); 89228753Smm if (extract == NULL) 90228753Smm return (ARCHIVE_FATAL); 91228753Smm archive_write_disk_set_options(extract->ad, flags); 92228753Smm return (archive_read_extract2(_a, entry, extract->ad)); 93228753Smm} 94228753Smm 95228753Smmint 96228753Smmarchive_read_extract2(struct archive *_a, struct archive_entry *entry, 97228753Smm struct archive *ad) 98228753Smm{ 99228753Smm struct archive_read *a = (struct archive_read *)_a; 100228753Smm int r, r2; 101228753Smm 102228753Smm /* Set up for this particular entry. */ 103228753Smm archive_write_disk_set_skip_file(ad, 104228753Smm a->skip_file_dev, a->skip_file_ino); 105228753Smm r = archive_write_header(ad, entry); 106228753Smm if (r < ARCHIVE_WARN) 107228753Smm r = ARCHIVE_WARN; 108228753Smm if (r != ARCHIVE_OK) 109228753Smm /* If _write_header failed, copy the error. */ 110228753Smm archive_copy_error(&a->archive, ad); 111228753Smm else if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) > 0) 112228753Smm /* Otherwise, pour data into the entry. */ 113228753Smm r = copy_data(_a, ad); 114228753Smm r2 = archive_write_finish_entry(ad); 115228753Smm if (r2 < ARCHIVE_WARN) 116228753Smm r2 = ARCHIVE_WARN; 117228753Smm /* Use the first message. */ 118228753Smm if (r2 != ARCHIVE_OK && r == ARCHIVE_OK) 119228753Smm archive_copy_error(&a->archive, ad); 120228753Smm /* Use the worst error return. */ 121228753Smm if (r2 < r) 122228753Smm r = r2; 123228753Smm return (r); 124228753Smm} 125228753Smm 126228753Smmvoid 127228753Smmarchive_read_extract_set_progress_callback(struct archive *_a, 128228753Smm void (*progress_func)(void *), void *user_data) 129228753Smm{ 130228753Smm struct archive_read *a = (struct archive_read *)_a; 131228753Smm struct extract *extract = get_extract(a); 132228753Smm if (extract != NULL) { 133228753Smm extract->extract_progress = progress_func; 134228753Smm extract->extract_progress_user_data = user_data; 135228753Smm } 136228753Smm} 137228753Smm 138228753Smmstatic int 139228753Smmcopy_data(struct archive *ar, struct archive *aw) 140228753Smm{ 141228753Smm off_t offset; 142228753Smm const void *buff; 143228753Smm struct extract *extract; 144228753Smm size_t size; 145228753Smm int r; 146228753Smm 147228753Smm extract = get_extract((struct archive_read *)ar); 148228753Smm for (;;) { 149228753Smm r = archive_read_data_block(ar, &buff, &size, &offset); 150228753Smm if (r == ARCHIVE_EOF) 151228753Smm return (ARCHIVE_OK); 152228753Smm if (r != ARCHIVE_OK) 153228753Smm return (r); 154228753Smm r = archive_write_data_block(aw, buff, size, offset); 155228753Smm if (r < ARCHIVE_WARN) 156228753Smm r = ARCHIVE_WARN; 157228753Smm if (r != ARCHIVE_OK) { 158228753Smm archive_set_error(ar, archive_errno(aw), 159228753Smm "%s", archive_error_string(aw)); 160228753Smm return (r); 161228753Smm } 162228753Smm if (extract->extract_progress) 163228753Smm (extract->extract_progress) 164228753Smm (extract->extract_progress_user_data); 165228753Smm } 166228753Smm} 167228753Smm 168228753Smm/* 169228753Smm * Cleanup function for archive_extract. 170228753Smm */ 171228753Smmstatic int 172228753Smmarchive_read_extract_cleanup(struct archive_read *a) 173228753Smm{ 174228753Smm int ret = ARCHIVE_OK; 175228753Smm 176229592Smm ret = archive_write_free(a->extract->ad); 177228753Smm free(a->extract); 178228753Smm a->extract = NULL; 179228753Smm return (ret); 180228753Smm} 181