diff options
Diffstat (limited to 'debfile.c')
-rw-r--r-- | debfile.c | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/debfile.c b/debfile.c new file mode 100644 index 0000000..61d73cd --- /dev/null +++ b/debfile.c @@ -0,0 +1,229 @@ +/* This file is part of "reprepro" + * Copyright (C) 2006 Bernhard R. Link + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1301 USA + */ +#include <config.h> + +#include <errno.h> +#include <assert.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <archive.h> +#include <archive_entry.h> +#include "error.h" +#include "uncompression.h" +#include "ar.h" +#include "chunks.h" +#include "debfile.h" + +#ifndef HAVE_LIBARCHIVE +#error Why did this file got compiled instead of extractcontrol.c? +#endif +#if ARCHIVE_VERSION_NUMBER < 3000000 +#define archive_read_free archive_read_finish +#endif + +static retvalue read_control_file(char **control, const char *debfile, struct archive *tar, struct archive_entry *entry) { + int64_t size; + char *buffer, *n; + const char *afterchanges; + size_t len, controllen; + ssize_t got; + + size = archive_entry_size(entry); + if (size <= 0) { + fprintf(stderr, "Error: Empty control file within %s!\n", + debfile); + return RET_ERROR; + } + if (size > 10*1024*1024) { + fprintf(stderr, +"Error: Ridiculously long control file within %s!\n", + debfile); + return RET_ERROR; + } + buffer = malloc(size + 2); + if (FAILEDTOALLOC(buffer)) + return RET_ERROR_OOM; + len = 0; + while ((got = archive_read_data(tar, buffer+len, ((size_t)size+1)-len)) > 0 + && !interrupted()) { + len += got; + if (len > (size_t)size) { + fprintf(stderr, +"Internal Error: libarchive miscalculated length of the control file inside '%s',\n" +" perhaps the file is corrupt, perhaps libarchive!\n", debfile); + free(buffer); + return RET_ERROR; + } + } + if (interrupted()) { + free(buffer); + return RET_ERROR_INTERRUPTED; + } + if (got < 0) { + free(buffer); + fprintf(stderr, "Error reading control file from %s\n", + debfile); + return RET_ERROR; + } + if (len < (size_t)size) + fprintf(stderr, +"Warning: libarchive miscalculated length of the control file inside '%s'.\n" +"Maybe the file is corrupt, perhaps libarchive!\n", debfile); + buffer[len] = '\0'; + + controllen = chunk_extract(buffer, buffer, len, true, &afterchanges); + + if (controllen == 0) { + fprintf(stderr, +"Could only find spaces within control file of '%s'!\n", + debfile); + free(buffer); + return RET_ERROR; + } + if ((size_t)(afterchanges - buffer) < len) { + if (*afterchanges == '\0') + fprintf(stderr, +"Unexpected \\0 character within control file of '%s'!\n", debfile); + else + fprintf(stderr, +"Unexpected data after ending empty line in control file of '%s'!\n", debfile); + free(buffer); + return RET_ERROR; + } + assert (buffer[controllen] == '\0'); + n = realloc(buffer, controllen+1); + if (FAILEDTOALLOC(n)) { + free(buffer); + return RET_ERROR_OOM; + } + *control = n; + return RET_OK; +} + +static retvalue read_control_tar(char **control, const char *debfile, struct ar_archive *ar, struct archive *tar) { + struct archive_entry *entry; + int a; + retvalue r; + + archive_read_support_format_tar(tar); + archive_read_support_format_gnutar(tar); + a = archive_read_open(tar, ar, + ar_archivemember_open, + ar_archivemember_read, + ar_archivemember_close); + if (a != ARCHIVE_OK) { + fprintf(stderr, +"open control.tar.gz within '%s' failed: %d:%d:%s\n", + debfile, + a, archive_errno(tar), + archive_error_string(tar)); + return RET_ERROR; + } + while ((a=archive_read_next_header(tar, &entry)) == ARCHIVE_OK) { + if (strcmp(archive_entry_pathname(entry), "./control") != 0 && + strcmp(archive_entry_pathname(entry), "control") != 0) { + a = archive_read_data_skip(tar); + if (a != ARCHIVE_OK) { + int e = archive_errno(tar); + printf( +"Error skipping %s within data.tar.gz from %s: %d=%s\n", + archive_entry_pathname(entry), + debfile, + e, archive_error_string(tar)); + return (e!=0)?(RET_ERRNO(e)):RET_ERROR; + } + if (interrupted()) + return RET_ERROR_INTERRUPTED; + } else { + r = read_control_file(control, debfile, tar, entry); + if (r != RET_NOTHING) + return r; + } + } + if (a != ARCHIVE_EOF) { + int e = archive_errno(tar); + printf("Error reading control.tar.gz from %s: %d=%s\n", + debfile, + e, archive_error_string(tar)); + return (e!=0)?(RET_ERRNO(e)):RET_ERROR; + } + fprintf(stderr, +"Could not find a control file within control.tar.gz within '%s'!\n", + debfile); + return RET_ERROR_MISSING; +} + +retvalue extractcontrol(char **control, const char *debfile) { + struct ar_archive *ar; + retvalue r; + bool hadcandidate = false; + + r = ar_open(&ar, debfile); + if (RET_WAS_ERROR(r)) + return r; + assert (r != RET_NOTHING); + do { + char *filename; + enum compression c; + + r = ar_nextmember(ar, &filename); + if (RET_IS_OK(r)) { + if (strncmp(filename, "control.tar", 11) != 0) { + free(filename); + continue; + } + hadcandidate = true; + for (c = 0 ; c < c_COUNT ; c++) { + if (strcmp(filename + 11, + uncompression_suffix[c]) == 0) + break; + } + if (c >= c_COUNT) { + free(filename); + continue; + } + ar_archivemember_setcompression(ar, c); + if (uncompression_supported(c)) { + struct archive *tar; + + tar = archive_read_new(); + r = read_control_tar(control, debfile, ar, tar); + // TODO run archive_read_close to get error messages? + archive_read_free(tar); + if (r != RET_NOTHING) { + ar_close(ar); + free(filename); + return r; + } + + } + free(filename); + } + } while (RET_IS_OK(r)); + ar_close(ar); + if (hadcandidate) + fprintf(stderr, +"Could not find a suitable control.tar file within '%s'!\n", debfile); + else + fprintf(stderr, +"Could not find a control.tar file within '%s'!\n", debfile); + return RET_ERROR_MISSING; +} |