Commit a7047ce4 authored by Bernhard Link's avatar Bernhard Link

Adding libarchive support

parent 07be844a
2006-05-06 Bernhard R. Link <brlink@debian.org>
* add support for using libarchive to get the
control file out of a .deb instead of calling
ar and tar.
2006-05-03 Bernhard R. Link <brlink@debian.org>
* add new pull and checkpull actions
* repair checkupdate statistics of newest available
......
......@@ -4,10 +4,21 @@ EXTRA_DIST = autogen.sh
bin_PROGRAMS = reprepro
AM_CPPFLAGS = -D_GNU_SOURCE=1 -Wall
if HAVE_LIBARCHIVE
ARCHIVE_USED = ar.c debfile.c
ARCHIVE_UNUSED = extractcontrol.c
else
ARCHIVE_USED = extractcontrol.c
ARCHIVE_UNUSED = ar.c debfile.c
endif
reprepro_SOURCES = guesscomponent.c files.c md5.c md5sum.c dirs.c chunks.c reference.c packages.c binaries.c sources.c names.c dpkgversions.c release.c mprintf.c updates.c strlist.c signature.c distribution.c extractcontrol.c checkindeb.c checkindsc.c checkin.c copyfile.c upgradelist.c target.c aptmethod.c downloadcache.c main.c override.c terms.c ignore.c filterlist.c exports.c tracking.c optionsfile.c readrelease.c donefile.c pull.c
noinst_HEADERS = guesscomponent.h md5.h md5sum.h dirs.h files.h chunks.h reference.h packages.h binaries.h sources.h names.h release.h error.h mprintf.h updates.h strlist.h signature.h distribution.h extractcontrol.h checkindeb.h checkindsc.h copyfile.h upgradelist.h target.h aptmethod.h downloadcache.h override.h terms.h ignore.h filterlist.h dpkgversions.h checkin.h exports.h globals.h tracking.h trackingt.h optionsfile.h readrelease.h donefile.h pull.h
AM_CPPFLAGS = -D_GNU_SOURCE=1 -Wall $(ARCHIVECPP)
reprepro_LDADD = $(ARCHIVELIBS)
reprepro_SOURCES = guesscomponent.c files.c md5.c md5sum.c dirs.c chunks.c reference.c packages.c binaries.c sources.c names.c dpkgversions.c release.c mprintf.c updates.c strlist.c signature.c distribution.c checkindeb.c checkindsc.c checkin.c copyfile.c upgradelist.c target.c aptmethod.c downloadcache.c main.c override.c terms.c ignore.c filterlist.c exports.c tracking.c optionsfile.c readrelease.c donefile.c pull.c $(ARCHIVE_USED)
EXTRA_reprepro_SOURCE = $(ARCHIVE_UNUSED)
noinst_HEADERS = guesscomponent.h md5.h md5sum.h dirs.h files.h chunks.h reference.h packages.h binaries.h sources.h names.h release.h error.h mprintf.h updates.h strlist.h signature.h distribution.h debfile.h checkindeb.h checkindsc.h copyfile.h upgradelist.h target.h aptmethod.h downloadcache.h override.h terms.h ignore.h filterlist.h dpkgversions.h checkin.h exports.h globals.h tracking.h trackingt.h optionsfile.h readrelease.h donefile.h pull.h ar.h
MAINTAINERCLEANFILES = Makefile.in configure install-sh stamp-h.in aclocal.m4 config.h.in mkinstalldirs config.guess config.sub missing
......
/* This file is part of "reprepro"
* Copyright (C) 2005,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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <malloc.h>
#include <assert.h>
#include <archive.h>
#include "error.h"
#include "ar.h"
/* Arr, me matees, Arr */
#define BLOCKSIZE 10240
#define AR_MAGIC "!<arch>\n"
#define AR_HEADERMAGIC "`\n"
struct ar_archive {
char *filename;
int fd;
struct ar_header {
char ah_filename[16];
char ah_date[12];
char ah_uid[6];
char ah_gid[6];
char ah_mode[8];
char ah_size[10];
char ah_magictrailer[2];
} currentheader;
off_t bytes_left;
void *readbuffer;
bool_t wasodd;
};
static ssize_t readwait(int fd, void *buf, size_t count) {
ssize_t totalread;
totalread = 0;
while( count > 0 ) {
ssize_t s;
s = read(fd,buf,count);
if( s < 0 )
return s;
if( (size_t)s > count ) {
errno = EINVAL;
return -1;
}
if( s == 0 )
break;
totalread += s;
buf += s;
count -= s;
}
return totalread;
}
retvalue ar_open(/*@out@*/struct ar_archive **n, const char *filename) {
struct ar_archive *ar;
char buffer[sizeof(AR_MAGIC)];
ssize_t bytesread;
ar = calloc(1,sizeof(struct ar_archive));
if( ar == NULL )
return RET_ERROR_OOM;
ar->fd = open(filename,O_NOCTTY|O_RDONLY);
if( ar->fd < 0 ) {
int e = errno;
fprintf(stderr,"Error opening %s: %m\n",filename);
free(ar);
return RET_ERRNO(e);
}
bytesread = readwait(ar->fd,buffer,sizeof(AR_MAGIC)-1);
if( bytesread != sizeof(AR_MAGIC)-1 ) {
int e = errno;
(void)close(ar->fd);
free(ar);
if( bytesread < 0 ) {
fprintf(stderr,"Error reading from %s: %s\n",filename,strerror(e));
return RET_ERRNO(e);
} else {
fprintf(stderr,"Premature end of reading from %s\n",filename);
return RET_ERROR;
}
}
if( memcmp(buffer,AR_MAGIC,sizeof(AR_MAGIC)-1) != 0 ) {
(void)close(ar->fd);
free(ar);
fprintf(stderr,"Missing ar-header '!<arch>' at the beginning of %s\n",filename);
return RET_ERROR;
}
ar->filename = strdup(filename);
if( ar->filename == NULL ) {
close(ar->fd);
free(ar);
return RET_ERROR_OOM;
}
*n = ar;
return RET_OK;
}
void ar_close(/*@only@*/struct ar_archive *ar) {
if( ar != NULL ) {
if( ar->fd >= 0 )
(void)close(ar->fd);
free(ar->filename);
free(ar);
}
}
/* RET_OK = next is there, RET_NOTHING = eof, < 0 = error */
retvalue ar_nextmember(struct ar_archive *ar,/*@out@*/char **filename) {
ssize_t bytesread;
char *p;
assert(ar->readbuffer == NULL);
assert(ar->fd >= 0);
/* seek over what is left from the last part: */
if( ar->bytes_left >0 || ar->wasodd ) {
off_t s;
s = lseek(ar->fd,ar->bytes_left+ar->wasodd,SEEK_CUR);
if( s == (off_t)-1 ) {
int e = errno;
fprintf(stderr,"Error seeking to next member in ar-file %s: %s\n",ar->filename,strerror(e));
return RET_ERRNO(e);
}
}
/* read the next header from the file */
bytesread = readwait(ar->fd,&ar->currentheader,sizeof(ar->currentheader));
if( bytesread == 0 )
return RET_NOTHING;
if( bytesread != sizeof(ar->currentheader) ){
int e = errno;
if( bytesread < 0 ) {
fprintf(stderr,"Error reading from ar-file %s: %s\n",ar->filename,strerror(e));
return RET_ERRNO(e);
} else {
fprintf(stderr,"Premature end of ar-file %s\n",ar->filename);
return RET_ERROR;
}
}
if( memcmp(ar->currentheader.ah_magictrailer,AR_HEADERMAGIC,2) != 0 ) {
fprintf(stderr,"Corrupt ar-file %s\n",ar->filename);
return RET_ERROR;
}
/* calculate the length and mark possible fillers being needed */
ar->currentheader.ah_size[11] = '\0'; // ugly, but it works
ar->bytes_left = strtoul(ar->currentheader.ah_size,&p,10);
if( *p != '\0' && *p != ' ' ) {
fprintf(stderr,"Error calculating length field in ar-file %s\n",ar->filename);
return RET_ERROR;
}
if( (ar->bytes_left & 1) != 0 )
ar->wasodd = TRUE;
/* get the name of the file */
if( FALSE ) {
/* handle long filenames */
// TODO!
} else {
/* normal filenames */
int i = sizeof(ar->currentheader.ah_filename);
while( i > 0 && ar->currentheader.ah_filename[i-1] == ' ')
i--;
*filename = strndup(ar->currentheader.ah_filename,i);
}
return RET_OK;
}
ssize_t ar_archivemember_read(struct archive *a, void *d, const void **p) {
struct ar_archive *ar = d;
ssize_t bytesread;
assert( ar->readbuffer != NULL );
if( ar->bytes_left == 0 )
return 0;
*p = ar->readbuffer;
bytesread = read(ar->fd,ar->readbuffer,(ar->bytes_left > BLOCKSIZE)?BLOCKSIZE:ar->bytes_left);
if( bytesread < 0 ) {
archive_set_error(a,errno,"Error reading from file: %m");
return -1;
}
if( bytesread == 0 ) {
archive_set_error(a,EIO,"Unexpected end of file");
return -1;
}
ar->bytes_left -= bytesread;
return bytesread;
}
int ar_archivemember_open(struct archive *a, void *d) {
struct ar_archive *ar = d;
assert(ar->readbuffer == NULL );
ar->readbuffer = malloc(BLOCKSIZE);
if( ar->readbuffer == NULL ) {
archive_set_error(a,ENOMEM,"Out of memory");
return ARCHIVE_FATAL;
}
return ARCHIVE_OK;
}
int ar_archivemember_close(UNUSED(struct archive *a), void *d) {
struct ar_archive *ar = d;
free(ar->readbuffer);
ar->readbuffer = NULL;
return ARCHIVE_OK;
}
#ifndef DEBCOMP_AR_H
#define DEBCOMP_AR_H
struct ar_archive;
retvalue ar_open(/*@out@*/struct ar_archive **, const char *filename);
void ar_close(/*@only@*/struct ar_archive *);
/* RET_OK = next is there, RET_NOTHING = eof, < 0 = error */
retvalue ar_nextmember(struct ar_archive *,/*@out@*/char **filename);
/* the following can be used for libarchive to read an file in the ar
* after ar_nextmember returned sucessfully.
* All references get invalid after the ar_nextmember is called again. */
int ar_archivemember_close(struct archive *, void *);
int ar_archivemember_open(struct archive *, void *);
ssize_t ar_archivemember_read(struct archive *, void *, const void **);
#endif
......@@ -36,7 +36,7 @@
#include "reference.h"
#include "binaries.h"
#include "files.h"
#include "extractcontrol.h"
#include "debfile.h"
#include "guesscomponent.h"
#include "tracking.h"
......
......@@ -16,16 +16,49 @@ AC_CHECK_LIB(bz2,BZ2_bzCompressInit,,[AC_MSG_WARN(["no libbz2 found, compiling w
AC_CHECK_LIB(db3,db_create,,[AC_MSG_ERROR(["no libdb3 found"])],)
AC_CHECK_LIB(gpgme,gpgme_check_version,,[AC_MSG_ERROR(["no libgpgme found"])],)
AC_CHECK_HEADER(gpgme.h,,[AC_MSG_ERROR(["no gpgme.h found"])])
CHECK_ENUM(GPGME_SIG_STAT_GOOD_EXP,[gpgme.h],[CPPFLAGS="$CPPFLAGS -DHASGPGMEGOODEXP"],[],)
AH_TEMPLATE([HASGPGMEGOODEXP],[Defined if libgpgme contains GPGME_SIG_STAT_GOOD_EXP])
CHECK_ENUM(GPGME_SIG_STAT_GOOD_EXP,[gpgme.h],[AC_DEFINE_UNQUOTED(AS_TR_CPP(HASGPGMEGOODEXP))],[],)
AC_ARG_ENABLE(debug, [ --enable-debug Activate some switches, that make debugging easier],
[
debug=yes
CPPFLAGS="$CPPFLAGS -Wall -g"
],
[debug=no])
AM_CONDITIONAL(DEBUG, test "x$debug" = "xyes")
ARCHIVELIBS=""
ARCHIVECPP=""
AH_TEMPLATE([HAVE_LIBARCHIVE],[Defined if libarchive is available])
AC_ARG_WITH(libarchive,
[ --with-libarchive=path|yes|no Give path to prefix libarchive was installed with],[dnl
case "$withval" in
no)
;;
yes)
AC_CHECK_LIB(archive,archive_read_new,[dnl
AC_CHECK_HEADER(archive.h,[dnl
AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_LIBARCHIVE))
ARCHIVELIBS="-larchive"
],[AC_MSG_ERROR([Could not find archive.h])])
],[AC_MSG_ERROR([Could not find libarchive])])
;;
*)
AC_CHECK_LIB(archive,archive_read_new,[dnl
mysave_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="-I$withval/include $CPPFLAGS"
AC_CHECK_HEADER(archive.h,[dnl
AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_LIBARCHIVE))
ARCHIVELIBS="-L$withval/lib -larchive"
ARCHIVECPP="-I$withval/include"
],[AC_MSG_ERROR([Could not find archive.h])])
CPPFLAGS="$mysave_CPPFLAGS"
],[AC_MSG_ERROR([Could not find libarchive])],[-L$withval/lib])
;;
esac
],[dnl without --with-libarchive we look for it but not finding it is no error:
AC_CHECK_LIB(archive,archive_read_new,[dnl
AC_CHECK_HEADER(archive.h,[dnl
AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_LIBARCHIVE))
ARCHIVELIBS="-larchive"
],[AC_MSG_WARN([Compiling without libarchive])])
],[AC_MSG_WARN([Compiling without libarchive])])
])
AM_CONDITIONAL([HAVE_LIBARCHIVE],[test -n "$ARCHIVELIBS"])
AC_SUBST([ARCHIVELIBS])
AC_SUBST([ARCHIVECPP])
dnl
dnl Create makefiles
......
/* 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 <malloc.h>
#include <string.h>
#include <ctype.h>
#include <archive.h>
#include <archive_entry.h>
#include "error.h"
#include "ar.h"
#include "md5sum.h"
#include "chunks.h"
#include "debfile.h"
#ifndef HAVE_LIBARCHIVE
#error Why did this file got compiled instead of extractcontrol.c?
#endif
static retvalue read_control_file(char **control, const char *debfile, struct archive *tar, struct archive_entry *entry) {
int64_t size;
char *buffer, *startofchanges, *endofchanges, *afterchanges, *n;
size_t len;
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: Ridiculous long control file within %s!\n", debfile);
return RET_ERROR;
}
buffer = malloc(size+2);
len = 0;
while( (got = archive_read_data(tar, buffer+len, ((size_t)size+1)-len)) > 0 ) {
len += got;
if( len > size ) {
fprintf(stderr, "Internal Error: libarchive miscalculated length of the control file within '%s',\n"
" perhaps the file is corrupt, perhaps libarchive!\n", debfile);
free(buffer);
return RET_ERROR;
}
}
if( len < size )
fprintf(stderr, "Warning: libarchive overcalculated length of the control file within '%s',\n"
" perhaps the file is corrupt, perhaps libarchive!\n", debfile);
buffer[len] = '\0';
startofchanges = buffer;
while( *startofchanges != '\0' && xisspace(*startofchanges)) {
startofchanges++;
}
if( (size_t)(startofchanges-buffer) >= len) {
fprintf(stderr,"Could only find spaces within contol file of '%s'!\n",
debfile);
free(buffer);
return RET_ERROR;
}
len -= (startofchanges-buffer);
memmove(buffer, startofchanges, len+1);
endofchanges = buffer;
while( *endofchanges != '\0' &&
( *endofchanges != '\n' || *(endofchanges-1)!= '\n')) {
endofchanges++;
}
afterchanges = endofchanges;
while( *afterchanges != '\0' && xisspace(*afterchanges)) {
afterchanges++;
}
if( (size_t)(afterchanges - buffer) < len ) {
if( *afterchanges == '\0' ) {
fprintf(stderr,"Unexpected \\0 character within control file of '%s'!\n", debfile);
free(buffer);
return RET_ERROR;
}
fprintf(stderr,"Unexpected data after ending empty line in control file of '%s'!\n", debfile);
free(buffer);
return RET_ERROR;
}
*afterchanges = '\0';
n = realloc(buffer, (afterchanges-buffer)+1);
if( n == NULL ) {
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);
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;
}
} 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;
r = ar_open(&ar,debfile);
if( RET_WAS_ERROR(r) )
return r;
assert( r != RET_NOTHING);
do {
char *filename;
r = ar_nextmember(ar, &filename);
if( RET_IS_OK(r) ) {
if( strcmp(filename,"control.tar.gz") == 0 ) {
struct archive *tar;
tar = archive_read_new();
archive_read_support_compression_gzip(tar);
r = read_control_tar(control, debfile, ar, tar);
archive_read_finish(tar);
if( r != RET_NOTHING ) {
ar_close(ar);
free(filename);
return r;
}
}
free(filename);
}
} while( RET_IS_OK(r) );
ar_close(ar);
fprintf(stderr,"Could not find a control.tar.gz file within '%s'!\n",debfile);
return RET_ERROR_MISSING;
}
#ifndef REPREPRO_EXTRACTCONTROL_H
#define REPREPRO_EXTRACTCONTROL_H
#ifndef REPREPRO_DEBFILE_H
#define REPREPRO_DEBFILE_H
#ifndef REPREPRO_ERROR_H
#include "error.h"
......
......@@ -20,8 +20,10 @@ endif
config.status: configure
dh_testdir
./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --prefix=/usr --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info CFLAGS="$(CFLAGS)" LDFLAGS="-Wl,-z,syms"
./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) \
--prefix=/usr --mandir=\$${prefix}/share/man \
--without-libarchive \
CFLAGS="$(CFLAGS)" LDFLAGS="-Wl,-z,syms"
build: build-stamp
......
......@@ -29,17 +29,16 @@
#include "error.h"
#include "md5sum.h"
#include "chunks.h"
#include "extractcontrol.h"
extern int verbose;
#include "debfile.h"
#ifdef HAVE_LIBARCHIVE
#error Why did this file got compiled instead of debfile.c?
#endif
// **********************************************************************
// * This is simply a ugly prototype until I have time to get something
// * correct (perhaps borrowing code from dpkg2.0). Until then its just
// * a quick and dirty hack to make it running.
// * This is a very simple implementation calling ar and tar, which
// * is only used with --without-libarchive or when no libarchive was
// * found.
// **********************************************************************
//TODO: write this properly.
retvalue extractcontrol(char **control,const char *debfile) {
int pipe1[2];
......
......@@ -46,7 +46,7 @@
#include "pull.h"
#include "upgradelist.h"
#include "signature.h"
#include "extractcontrol.h"
#include "debfile.h"
#include "checkindeb.h"
#include "checkindsc.h"
#include "checkin.h"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment