index : pacman | |
Archlinux32 fork of pacman | gitolite user |
summaryrefslogtreecommitdiff |
diff --git a/lib/libalpm/Makefile.am b/lib/libalpm/Makefile.am index 3473a73a..da663cb5 100644 --- a/lib/libalpm/Makefile.am +++ b/lib/libalpm/Makefile.am @@ -25,20 +25,20 @@ libalpm_la_SOURCES = \ alpm.h alpm.c \ alpm_list.h alpm_list.c \ backup.h backup.c \ - be_files.c \ + be_local.c \ be_package.c \ - cache.h cache.c \ + be_sync.c \ conflict.h conflict.c \ db.h db.c \ delta.h delta.c \ deps.h deps.c \ + diskspace.h diskspace.c \ dload.h dload.c \ error.c \ graph.h \ group.h group.c \ handle.h handle.c \ log.h log.c \ - md5.h md5.c \ package.h package.c \ remove.h remove.c \ sync.h sync.c \ @@ -46,6 +46,11 @@ libalpm_la_SOURCES = \ util.h util.c \ version.c +if !HAVE_LIBSSL +libalpm_la_SOURCES += \ + md5.h md5.c +endif + libalpm_la_LDFLAGS = -no-undefined -version-info $(LIB_VERSION_INFO) libalpm_la_LIBADD = $(LTLIBINTL) diff --git a/lib/libalpm/add.c b/lib/libalpm/add.c index f39a0ecf..ed57a079 100644 --- a/lib/libalpm/add.c +++ b/lib/libalpm/add.c @@ -40,7 +40,6 @@ #include "alpm_list.h" #include "trans.h" #include "util.h" -#include "cache.h" #include "log.h" #include "backup.h" #include "package.h" @@ -54,7 +53,7 @@ * @param target the name of the file target to add * @return 0 on success, -1 on error (pm_errno is set accordingly) */ -int SYMEXPORT alpm_add_target(char *target) +int SYMEXPORT alpm_add_target(const char *target) { pmpkg_t *pkg = NULL; const char *pkgname, *pkgver; @@ -110,6 +109,31 @@ error: return(-1); } +static int perform_extraction(struct archive *archive, + struct archive_entry *entry, const char *filename, const char *origname) +{ + int ret; + const int archive_flags = ARCHIVE_EXTRACT_OWNER | + ARCHIVE_EXTRACT_PERM | + ARCHIVE_EXTRACT_TIME; + + archive_entry_set_pathname(entry, filename); + + ret = archive_read_extract(archive, entry, archive_flags); + if(ret == ARCHIVE_WARN && archive_errno(archive) != ENOSPC) { + /* operation succeeded but a "non-critical" error was encountered */ + _alpm_log(PM_LOG_WARNING, _("warning given when extracting %s (%s)\n"), + origname, archive_error_string(archive)); + } else if(ret != ARCHIVE_OK) { + _alpm_log(PM_LOG_ERROR, _("could not extract %s (%s)\n"), + origname, archive_error_string(archive)); + alpm_logaction("error: could not extract %s (%s)\n", + origname, archive_error_string(archive)); + return(1); + } + return(0); +} + static int extract_single_file(struct archive *archive, struct archive_entry *entry, pmpkg_t *newpkg, pmpkg_t *oldpkg, pmtrans_t *trans, pmdb_t *db) @@ -120,9 +144,6 @@ static int extract_single_file(struct archive *archive, int needbackup = 0, notouch = 0; char *hash_orig = NULL; char *entryname_orig = NULL; - const int archive_flags = ARCHIVE_EXTRACT_OWNER | - ARCHIVE_EXTRACT_PERM | - ARCHIVE_EXTRACT_TIME; int errors = 0; entryname = archive_entry_pathname(entry); @@ -277,18 +298,10 @@ static int extract_single_file(struct archive *archive, int ret; snprintf(checkfile, PATH_MAX, "%s.paccheck", filename); - archive_entry_set_pathname(entry, checkfile); - - ret = archive_read_extract(archive, entry, archive_flags); - if(ret == ARCHIVE_WARN) { - /* operation succeeded but a non-critical error was encountered */ - _alpm_log(PM_LOG_DEBUG, "warning extracting %s (%s)\n", - entryname_orig, archive_error_string(archive)); - } else if(ret != ARCHIVE_OK) { - _alpm_log(PM_LOG_ERROR, _("could not extract %s (%s)\n"), - entryname_orig, archive_error_string(archive)); - alpm_logaction("error: could not extract %s (%s)\n", - entryname_orig, archive_error_string(archive)); + + ret = perform_extraction(archive, entry, checkfile, entryname_orig); + if(ret == 1) { + /* error */ FREE(hash_orig); FREE(entryname_orig); return(1); @@ -430,18 +443,9 @@ static int extract_single_file(struct archive *archive, unlink(filename); } - archive_entry_set_pathname(entry, filename); - - ret = archive_read_extract(archive, entry, archive_flags); - if(ret == ARCHIVE_WARN) { - /* operation succeeded but a non-critical error was encountered */ - _alpm_log(PM_LOG_DEBUG, "warning extracting %s (%s)\n", - entryname_orig, archive_error_string(archive)); - } else if(ret != ARCHIVE_OK) { - _alpm_log(PM_LOG_ERROR, _("could not extract %s (%s)\n"), - entryname_orig, archive_error_string(archive)); - alpm_logaction("error: could not extract %s (%s)\n", - entryname_orig, archive_error_string(archive)); + ret = perform_extraction(archive, entry, filename, entryname_orig); + if(ret == 1) { + /* error */ FREE(entryname_orig); return(1); } @@ -498,7 +502,7 @@ static int commit_single_pkg(pmpkg_t *newpkg, int pkg_current, int pkg_count, oldpkg = _alpm_pkg_dup(local); /* make sure all infos are loaded because the database entry * will be removed soon */ - _alpm_db_read(oldpkg->origin_data.db, oldpkg, INFRQ_ALL); + _alpm_local_db_read(oldpkg->origin_data.db, oldpkg, INFRQ_ALL); EVENT(trans, PM_TRANS_EVT_UPGRADE_START, newpkg, oldpkg); _alpm_log(PM_LOG_DEBUG, "upgrading package %s-%s\n", @@ -544,7 +548,7 @@ static int commit_single_pkg(pmpkg_t *newpkg, int pkg_current, int pkg_count, /* prepare directory for database entries so permission are correct after changelog/install script installation (FS#12263) */ - if(_alpm_db_prepare(db, newpkg)) { + if(_alpm_local_db_prepare(db, newpkg)) { alpm_logaction("error: could not create database entry %s-%s\n", alpm_pkg_get_name(newpkg), alpm_pkg_get_version(newpkg)); pm_errno = PM_ERR_DB_WRITE; @@ -556,6 +560,7 @@ static int commit_single_pkg(pmpkg_t *newpkg, int pkg_current, int pkg_count, struct archive *archive; struct archive_entry *entry; char cwd[PATH_MAX] = ""; + int restore_cwd = 0; _alpm_log(PM_LOG_DEBUG, "extracting files\n"); @@ -579,11 +584,16 @@ static int commit_single_pkg(pmpkg_t *newpkg, int pkg_current, int pkg_count, /* save the cwd so we can restore it later */ if(getcwd(cwd, PATH_MAX) == NULL) { _alpm_log(PM_LOG_ERROR, _("could not get current working directory\n")); - cwd[0] = 0; + } else { + restore_cwd = 1; } /* libarchive requires this for extracting hard links */ - chdir(handle->root); + if(chdir(handle->root) != 0) { + _alpm_log(PM_LOG_ERROR, _("could not change directory to %s (%s)\n"), handle->root, strerror(errno)); + ret = -1; + goto cleanup; + } /* call PROGRESS once with 0 percent, as we sort-of skip that here */ if(is_upgrade) { @@ -629,9 +639,9 @@ static int commit_single_pkg(pmpkg_t *newpkg, int pkg_current, int pkg_count, } archive_read_finish(archive); - /* restore the old cwd is we have it */ - if(strlen(cwd)) { - chdir(cwd); + /* restore the old cwd if we have it */ + if(restore_cwd && chdir(cwd) != 0) { + _alpm_log(PM_LOG_ERROR, _("could not change directory to %s (%s)\n"), cwd, strerror(errno)); } if(errors) { @@ -656,7 +666,7 @@ static int commit_single_pkg(pmpkg_t *newpkg, int pkg_current, int pkg_count, _alpm_log(PM_LOG_DEBUG, "updating database\n"); _alpm_log(PM_LOG_DEBUG, "adding database entry '%s'\n", newpkg->name); - if(_alpm_db_write(db, newpkg, INFRQ_ALL)) { + if(_alpm_local_db_write(db, newpkg, INFRQ_ALL)) { _alpm_log(PM_LOG_ERROR, _("could not update database entry %s-%s\n"), alpm_pkg_get_name(newpkg), alpm_pkg_get_version(newpkg)); alpm_logaction("error: could not update database entry %s-%s\n", @@ -706,6 +716,7 @@ cleanup: int _alpm_upgrade_packages(pmtrans_t *trans, pmdb_t *db) { int pkg_count, pkg_current; + int skip_ldconfig = 0, ret = 0; alpm_list_t *targ; ALPM_LOG_FUNC; @@ -723,18 +734,28 @@ int _alpm_upgrade_packages(pmtrans_t *trans, pmdb_t *db) /* loop through our package list adding/upgrading one at a time */ for(targ = trans->add; targ; targ = targ->next) { if(handle->trans->state == STATE_INTERRUPTED) { - return(0); + return(ret); } pmpkg_t *newpkg = (pmpkg_t *)targ->data; - commit_single_pkg(newpkg, pkg_current, pkg_count, trans, db); + if(commit_single_pkg(newpkg, pkg_current, pkg_count, trans, db)) { + /* something screwed up on the commit, abort the trans */ + trans->state = STATE_INTERRUPTED; + pm_errno = PM_ERR_TRANS_ABORT; + /* running ldconfig at this point could possibly screw system */ + skip_ldconfig = 1; + ret = -1; + } + pkg_current++; } - /* run ldconfig if it exists */ - _alpm_ldconfig(handle->root); + if(!skip_ldconfig) { + /* run ldconfig if it exists */ + _alpm_ldconfig(handle->root); + } - return(0); + return(ret); } /* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/alpm.c b/lib/libalpm/alpm.c index 51b9e25b..3f9cfff3 100644 --- a/lib/libalpm/alpm.c +++ b/lib/libalpm/alpm.c @@ -23,6 +23,11 @@ #include "config.h" +/* connection caching setup */ +#ifdef HAVE_LIBFETCH +#include <fetch.h> +#endif + /* libalpm */ #include "alpm.h" #include "alpm_list.h" @@ -54,6 +59,10 @@ int SYMEXPORT alpm_initialize(void) bindtextdomain("libalpm", LOCALEDIR); #endif +#ifdef HAVE_LIBFETCH + fetchConnectionCacheInit(5, 1); +#endif + return(0); } @@ -73,6 +82,10 @@ int SYMEXPORT alpm_release(void) _alpm_handle_free(handle); handle = NULL; +#ifdef HAVE_LIBFETCH + fetchConnectionCacheClose(); +#endif + return(0); } diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h index 33291325..ea4aa14a 100644 --- a/lib/libalpm/alpm.h +++ b/lib/libalpm/alpm.h @@ -97,66 +97,69 @@ typedef int (*alpm_cb_fetch)(const char *url, const char *localpath, * Options */ -alpm_cb_log alpm_option_get_logcb(); +alpm_cb_log alpm_option_get_logcb(void); void alpm_option_set_logcb(alpm_cb_log cb); -alpm_cb_download alpm_option_get_dlcb(); +alpm_cb_download alpm_option_get_dlcb(void); void alpm_option_set_dlcb(alpm_cb_download cb); -alpm_cb_fetch alpm_option_get_fetchcb(); +alpm_cb_fetch alpm_option_get_fetchcb(void); void alpm_option_set_fetchcb(alpm_cb_fetch cb); -alpm_cb_totaldl alpm_option_get_totaldlcb(); +alpm_cb_totaldl alpm_option_get_totaldlcb(void); void alpm_option_set_totaldlcb(alpm_cb_totaldl cb); -const char *alpm_option_get_root(); +const char *alpm_option_get_root(void); int alpm_option_set_root(const char *root); -const char *alpm_option_get_dbpath(); +const char *alpm_option_get_dbpath(void); int alpm_option_set_dbpath(const char *dbpath); -alpm_list_t *alpm_option_get_cachedirs(); +alpm_list_t *alpm_option_get_cachedirs(void); int alpm_option_add_cachedir(const char *cachedir); void alpm_option_set_cachedirs(alpm_list_t *cachedirs); int alpm_option_remove_cachedir(const char *cachedir); -const char *alpm_option_get_logfile(); +const char *alpm_option_get_logfile(void); int alpm_option_set_logfile(const char *logfile); -const char *alpm_option_get_lockfile(); +const char *alpm_option_get_lockfile(void); /* no set_lockfile, path is determined from dbpath */ -int alpm_option_get_usesyslog(); +int alpm_option_get_usesyslog(void); void alpm_option_set_usesyslog(int usesyslog); -alpm_list_t *alpm_option_get_noupgrades(); +alpm_list_t *alpm_option_get_noupgrades(void); void alpm_option_add_noupgrade(const char *pkg); void alpm_option_set_noupgrades(alpm_list_t *noupgrade); int alpm_option_remove_noupgrade(const char *pkg); -alpm_list_t *alpm_option_get_noextracts(); +alpm_list_t *alpm_option_get_noextracts(void); void alpm_option_add_noextract(const char *pkg); void alpm_option_set_noextracts(alpm_list_t *noextract); int alpm_option_remove_noextract(const char *pkg); -alpm_list_t *alpm_option_get_ignorepkgs(); +alpm_list_t *alpm_option_get_ignorepkgs(void); void alpm_option_add_ignorepkg(const char *pkg); void alpm_option_set_ignorepkgs(alpm_list_t *ignorepkgs); int alpm_option_remove_ignorepkg(const char *pkg); -alpm_list_t *alpm_option_get_ignoregrps(); +alpm_list_t *alpm_option_get_ignoregrps(void); void alpm_option_add_ignoregrp(const char *grp); void alpm_option_set_ignoregrps(alpm_list_t *ignoregrps); int alpm_option_remove_ignoregrp(const char *grp); -const char *alpm_option_get_arch(); +const char *alpm_option_get_arch(void); void alpm_option_set_arch(const char *arch); -int alpm_option_get_usedelta(); +int alpm_option_get_usedelta(void); void alpm_option_set_usedelta(int usedelta); -pmdb_t *alpm_option_get_localdb(); -alpm_list_t *alpm_option_get_syncdbs(); +int alpm_option_get_checkspace(void); +void alpm_option_set_checkspace(int checkspace); + +pmdb_t *alpm_option_get_localdb(void); +alpm_list_t *alpm_option_get_syncdbs(void); /* * Install reasons -- ie, why the package was installed @@ -235,7 +238,7 @@ size_t alpm_pkg_changelog_read(void *ptr, size_t size, /*int alpm_pkg_changelog_feof(const pmpkg_t *pkg, void *fp);*/ int alpm_pkg_changelog_close(const pmpkg_t *pkg, void *fp); int alpm_pkg_has_scriptlet(pmpkg_t *pkg); -int alpm_pkg_has_force(pmpkg_t *pkg); +int alpm_pkg_get_epoch(pmpkg_t *pkg); off_t alpm_pkg_download_size(pmpkg_t *newpkg); alpm_list_t *alpm_pkg_unused_deltas(pmpkg_t *pkg); @@ -368,6 +371,10 @@ typedef enum _pmtransevt_t { * The repository's tree name is passed to the callback. */ PM_TRANS_EVT_RETRIEVE_START, + /** Disk space usage will be computed for a package */ + PM_TRANS_EVT_DISKSPACE_START, + /** Disk space usage was computed for a package */ + PM_TRANS_EVT_DISKSPACE_DONE, } pmtransevt_t; /*@}*/ @@ -386,7 +393,8 @@ typedef enum _pmtransprog_t { PM_TRANS_PROGRESS_ADD_START, PM_TRANS_PROGRESS_UPGRADE_START, PM_TRANS_PROGRESS_REMOVE_START, - PM_TRANS_PROGRESS_CONFLICTS_START + PM_TRANS_PROGRESS_CONFLICTS_START, + PM_TRANS_PROGRESS_DISKSPACE_START, } pmtransprog_t; /* Transaction Event callback */ @@ -399,9 +407,9 @@ typedef void (*alpm_trans_cb_conv)(pmtransconv_t, void *, void *, /* Transaction Progress callback */ typedef void (*alpm_trans_cb_progress)(pmtransprog_t, const char *, int, int, int); -int alpm_trans_get_flags(); -alpm_list_t * alpm_trans_get_add(); -alpm_list_t * alpm_trans_get_remove(); +int alpm_trans_get_flags(void); +alpm_list_t * alpm_trans_get_add(void); +alpm_list_t * alpm_trans_get_remove(void); int alpm_trans_init(pmtransflag_t flags, alpm_trans_cb_event cb_event, alpm_trans_cb_conv conv, alpm_trans_cb_progress cb_progress); @@ -411,10 +419,10 @@ int alpm_trans_interrupt(void); int alpm_trans_release(void); int alpm_sync_sysupgrade(int enable_downgrade); -int alpm_sync_target(char *target); -int alpm_sync_dbtarget(char *db, char *target); -int alpm_add_target(char *target); -int alpm_remove_target(char *target); +int alpm_sync_target(const char *target); +int alpm_sync_dbtarget(const char *db, const char *target); +int alpm_add_target(const char *target); +int alpm_remove_target(const char *target); /* * Dependencies and conflicts @@ -429,10 +437,9 @@ typedef enum _pmdepmod_t { PM_DEP_MOD_LT } pmdepmod_t; -int alpm_depcmp(pmpkg_t *pkg, pmdepend_t *dep); alpm_list_t *alpm_checkdeps(alpm_list_t *pkglist, int reversedeps, alpm_list_t *remove, alpm_list_t *upgrade); -alpm_list_t *alpm_deptest(pmdb_t *db, alpm_list_t *targets); +pmpkg_t *alpm_find_satisfier(alpm_list_t *pkgs, const char *depstring); const char *alpm_miss_get_target(const pmdepmissing_t *miss); pmdepend_t *alpm_miss_get_dep(pmdepmissing_t *miss); @@ -480,6 +487,7 @@ enum _pmerrno_t { PM_ERR_NOT_A_FILE, PM_ERR_NOT_A_DIR, PM_ERR_WRONG_ARGS, + PM_ERR_DISK_SPACE, /* Interface */ PM_ERR_HANDLE_NULL, PM_ERR_HANDLE_NOT_NULL, @@ -522,6 +530,7 @@ enum _pmerrno_t { PM_ERR_FILE_CONFLICTS, /* Misc */ PM_ERR_RETRIEVE, + PM_ERR_WRITE, PM_ERR_INVALID_REGEX, /* External library errors */ PM_ERR_LIBARCHIVE, diff --git a/lib/libalpm/be_files.c b/lib/libalpm/be_local.c index c0b8b439..b2a50cc8 100644 --- a/lib/libalpm/be_files.c +++ b/lib/libalpm/be_local.c @@ -1,8 +1,8 @@ /* - * be_files.c + * be_local.c * - * Copyright (c) 2006 by Christian Hamar <krics@linuxforum.hu> - * Copyright (c) 2006 by Miklos Vajna <vmiklos@frugalware.org> + * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,317 +40,344 @@ /* libalpm */ #include "db.h" #include "alpm_list.h" -#include "cache.h" #include "log.h" #include "util.h" #include "alpm.h" #include "handle.h" #include "package.h" -#include "delta.h" +#include "group.h" #include "deps.h" #include "dload.h" -static int checkdbdir(pmdb_t *db) -{ - struct stat buf; - const char *path = _alpm_db_path(db); +#define LAZY_LOAD(info, errret) \ + do { \ + ALPM_LOG_FUNC; \ + ASSERT(handle != NULL, return(errret)); \ + ASSERT(pkg != NULL, return(errret)); \ + if(pkg->origin != PKG_FROM_FILE && !(pkg->infolevel & info)) { \ + _alpm_local_db_read(pkg->origin_data.db, pkg, info); \ + } \ + } while(0) - if(stat(path, &buf) != 0) { - _alpm_log(PM_LOG_DEBUG, "database dir '%s' does not exist, creating it\n", - path); - if(_alpm_makepath(path) != 0) { - RET_ERR(PM_ERR_SYSTEM, -1); - } - } else if(!S_ISDIR(buf.st_mode)) { - _alpm_log(PM_LOG_WARNING, _("removing invalid database: %s\n"), path); - if(unlink(path) != 0 || _alpm_makepath(path) != 0) { - RET_ERR(PM_ERR_SYSTEM, -1); - } - } - return(0); -} -/* create list of directories in db */ -static int dirlist_from_tar(const char *archive, alpm_list_t **dirlist) +/* Cache-specific accessor functions. These implementations allow for lazy + * loading by the files backend when a data member is actually needed + * rather than loading all pieces of information when the package is first + * initialized. + */ + +static const char *_cache_get_filename(pmpkg_t *pkg) { - struct archive *_archive; - struct archive_entry *entry; + LAZY_LOAD(INFRQ_DESC, NULL); + return pkg->filename; +} - if((_archive = archive_read_new()) == NULL) - RET_ERR(PM_ERR_LIBARCHIVE, -1); +static const char *_cache_get_name(pmpkg_t *pkg) +{ + ASSERT(pkg != NULL, return(NULL)); + return pkg->name; +} - archive_read_support_compression_all(_archive); - archive_read_support_format_all(_archive); +static const char *_cache_get_version(pmpkg_t *pkg) +{ + ASSERT(pkg != NULL, return(NULL)); + return pkg->version; +} - if(archive_read_open_filename(_archive, archive, - ARCHIVE_DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK) { - _alpm_log(PM_LOG_ERROR, _("could not open %s: %s\n"), archive, - archive_error_string(_archive)); - RET_ERR(PM_ERR_PKG_OPEN, -1); - } +static const char *_cache_get_desc(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, NULL); + return pkg->desc; +} - while(archive_read_next_header(_archive, &entry) == ARCHIVE_OK) { - const struct stat *st; - const char *entryname; /* the name of the file in the archive */ +static const char *_cache_get_url(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, NULL); + return pkg->url; +} - st = archive_entry_stat(entry); - entryname = archive_entry_pathname(entry); +static time_t _cache_get_builddate(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, 0); + return pkg->builddate; +} - if(S_ISDIR(st->st_mode)) { - char *name = strdup(entryname); - *dirlist = alpm_list_add(*dirlist, name); - } - } - archive_read_finish(_archive); +static time_t _cache_get_installdate(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, 0); + return pkg->installdate; +} - *dirlist = alpm_list_msort(*dirlist, alpm_list_count(*dirlist), _alpm_str_cmp); - return(0); +static const char *_cache_get_packager(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, NULL); + return pkg->packager; } -/* create list of directories in db */ -static int dirlist_from_fs(const char *syncdbpath, alpm_list_t **dirlist) +static const char *_cache_get_md5sum(pmpkg_t *pkg) { - DIR *dbdir; - struct dirent *ent = NULL; - struct stat sbuf; - char path[PATH_MAX]; + LAZY_LOAD(INFRQ_DESC, NULL); + return pkg->md5sum; +} - dbdir = opendir(syncdbpath); - if (dbdir != NULL) { - while((ent = readdir(dbdir)) != NULL) { - char *name = ent->d_name; - size_t len; - char *entry; +static const char *_cache_get_arch(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, NULL); + return pkg->arch; +} - if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { - continue; - } +static off_t _cache_get_size(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, -1); + return pkg->size; +} - /* stat the entry, make sure it's a directory */ - snprintf(path, PATH_MAX, "%s%s", syncdbpath, name); - if(stat(path, &sbuf) != 0 || !S_ISDIR(sbuf.st_mode)) { - continue; - } +static off_t _cache_get_isize(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, -1); + return pkg->isize; +} - len = strlen(name); - MALLOC(entry, len + 2, RET_ERR(PM_ERR_MEMORY, -1)); - strcpy(entry, name); - entry[len] = '/'; - entry[len+1] = '\0'; - *dirlist = alpm_list_add(*dirlist, entry); - } - closedir(dbdir); - } +static pmpkgreason_t _cache_get_reason(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, -1); + return pkg->reason; +} - *dirlist = alpm_list_msort(*dirlist, alpm_list_count(*dirlist), _alpm_str_cmp); - return(0); +static alpm_list_t *_cache_get_licenses(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, NULL); + return pkg->licenses; } -/* remove old directories from dbdir */ -static int remove_olddir(const char *syncdbpath, alpm_list_t *dirlist) +static alpm_list_t *_cache_get_groups(pmpkg_t *pkg) { - alpm_list_t *i; - for (i = dirlist; i; i = i->next) { - const char *name = i->data; - char *dbdir; - size_t len = strlen(syncdbpath) + strlen(name) + 2; - MALLOC(dbdir, len, RET_ERR(PM_ERR_MEMORY, -1)); - snprintf(dbdir, len, "%s%s", syncdbpath, name); - _alpm_log(PM_LOG_DEBUG, "removing: %s\n", dbdir); - if(_alpm_rmrf(dbdir) != 0) { - _alpm_log(PM_LOG_ERROR, _("could not remove database directory %s\n"), dbdir); - free(dbdir); - RET_ERR(PM_ERR_DB_REMOVE, -1); - } - free(dbdir); - } - return(0); + LAZY_LOAD(INFRQ_DESC, NULL); + return pkg->groups; } -/** Update a package database - * - * An update of the package database \a db will be attempted. Unless - * \a force is true, the update will only be performed if the remote - * database was modified since the last update. - * - * A transaction is necessary for this operation, in order to obtain a - * database lock. During this transaction the front-end will be informed - * of the download progress of the database via the download callback. - * - * Example: - * @code - * pmdb_t *db; - * int result; - * db = alpm_list_getdata(alpm_option_get_syncdbs()); - * if(alpm_trans_init(0, NULL, NULL, NULL) == 0) { - * result = alpm_db_update(0, db); - * alpm_trans_release(); - * - * if(result > 0) { - * printf("Unable to update database: %s\n", alpm_strerrorlast()); - * } else if(result < 0) { - * printf("Database already up to date\n"); - * } else { - * printf("Database updated\n"); - * } - * } - * @endcode - * - * @ingroup alpm_databases - * @note After a successful update, the \link alpm_db_get_pkgcache() - * package cache \endlink will be invalidated - * @param force if true, then forces the update, otherwise update only in case - * the database isn't up to date - * @param db pointer to the package database to update - * @return 0 on success, > 0 on error (pm_errno is set accordingly), < 0 if up - * to date - */ -int SYMEXPORT alpm_db_update(int force, pmdb_t *db) +static int _cache_get_epoch(pmpkg_t *pkg) { - char *dbfile, *dbfilepath; - const char *dbpath, *syncdbpath; - alpm_list_t *newdirlist = NULL, *olddirlist = NULL; - alpm_list_t *onlynew = NULL, *onlyold = NULL; - size_t len; - int ret; + LAZY_LOAD(INFRQ_DESC, -1); + return pkg->epoch; +} +static int _cache_has_scriptlet(pmpkg_t *pkg) +{ ALPM_LOG_FUNC; /* Sanity checks */ - ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1)); - ASSERT(db != NULL && db != handle->db_local, RET_ERR(PM_ERR_WRONG_ARGS, -1)); - /* Verify we are in a transaction. This is done _mainly_ because we need a DB - * lock - if we update without a db lock, we may kludge some other pacman - * process that _has_ a lock. - */ - ASSERT(handle->trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1)); - ASSERT(handle->trans->state == STATE_INITIALIZED, RET_ERR(PM_ERR_TRANS_NOT_INITIALIZED, -1)); - - if(!alpm_list_find_ptr(handle->dbs_sync, db)) { - RET_ERR(PM_ERR_DB_NOT_FOUND, -1); + ASSERT(handle != NULL, return(-1)); + ASSERT(pkg != NULL, return(-1)); + + if(!(pkg->infolevel & INFRQ_SCRIPTLET)) { + _alpm_local_db_read(pkg->origin_data.db, pkg, INFRQ_SCRIPTLET); } + return pkg->scriptlet; +} + +static alpm_list_t *_cache_get_depends(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, NULL); + return pkg->depends; +} + +static alpm_list_t *_cache_get_optdepends(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, NULL); + return pkg->optdepends; +} - len = strlen(db->treename) + strlen(DBEXT) + 1; - MALLOC(dbfile, len, RET_ERR(PM_ERR_MEMORY, -1)); - sprintf(dbfile, "%s" DBEXT, db->treename); +static alpm_list_t *_cache_get_conflicts(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, NULL); + return pkg->conflicts; +} - dbpath = alpm_option_get_dbpath(); +static alpm_list_t *_cache_get_provides(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, NULL); + return pkg->provides; +} - ret = _alpm_download_single_file(dbfile, db->servers, dbpath, force); - free(dbfile); +static alpm_list_t *_cache_get_replaces(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, NULL); + return pkg->replaces; +} - if(ret == 1) { - /* files match, do nothing */ - pm_errno = 0; - return(1); - } else if(ret == -1) { - /* pm_errno was set by the download code */ - _alpm_log(PM_LOG_DEBUG, "failed to sync db: %s\n", alpm_strerrorlast()); - return(-1); - } +/* local packages can not have deltas */ +static alpm_list_t *_cache_get_deltas(pmpkg_t *pkg) +{ + return NULL; +} - syncdbpath = _alpm_db_path(db); - - /* form the path to the db location */ - len = strlen(dbpath) + strlen(db->treename) + strlen(DBEXT) + 1; - MALLOC(dbfilepath, len, RET_ERR(PM_ERR_MEMORY, -1)); - sprintf(dbfilepath, "%s%s" DBEXT, dbpath, db->treename); - - if(force) { - /* if forcing update, remove the old dir and extract the db */ - if(_alpm_rmrf(syncdbpath) != 0) { - _alpm_log(PM_LOG_ERROR, _("could not remove database %s\n"), db->treename); - RET_ERR(PM_ERR_DB_REMOVE, -1); - } else { - _alpm_log(PM_LOG_DEBUG, "database dir %s removed\n", _alpm_db_path(db)); - } - } else { - /* if not forcing, only remove and extract what is necessary */ - ret = dirlist_from_tar(dbfilepath, &newdirlist); - if(ret) { - goto cleanup; - } - ret = dirlist_from_fs(syncdbpath, &olddirlist); - if(ret) { - goto cleanup; - } +static alpm_list_t *_cache_get_files(pmpkg_t *pkg) +{ + ALPM_LOG_FUNC; - alpm_list_diff_sorted(olddirlist, newdirlist, _alpm_str_cmp, &onlyold, &onlynew); + /* Sanity checks */ + ASSERT(handle != NULL, return(NULL)); + ASSERT(pkg != NULL, return(NULL)); - ret = remove_olddir(syncdbpath, onlyold); - if(ret) { - goto cleanup; - } + if(pkg->origin == PKG_FROM_LOCALDB + && !(pkg->infolevel & INFRQ_FILES)) { + _alpm_local_db_read(pkg->origin_data.db, pkg, INFRQ_FILES); } + return pkg->files; +} - /* Cache needs to be rebuilt */ - _alpm_db_free_pkgcache(db); +static alpm_list_t *_cache_get_backup(pmpkg_t *pkg) +{ + ALPM_LOG_FUNC; - checkdbdir(db); - ret = _alpm_unpack(dbfilepath, syncdbpath, onlynew, 0); + /* Sanity checks */ + ASSERT(handle != NULL, return(NULL)); + ASSERT(pkg != NULL, return(NULL)); -cleanup: - FREELIST(newdirlist); - FREELIST(olddirlist); - alpm_list_free(onlynew); - alpm_list_free(onlyold); + if(pkg->origin == PKG_FROM_LOCALDB + && !(pkg->infolevel & INFRQ_FILES)) { + _alpm_local_db_read(pkg->origin_data.db, pkg, INFRQ_FILES); + } + return pkg->backup; +} - free(dbfilepath); +/** + * Open a package changelog for reading. Similar to fopen in functionality, + * except that the returned 'file stream' is from the database. + * @param pkg the package (from db) to read the changelog + * @return a 'file stream' to the package changelog + */ +static void *_cache_changelog_open(pmpkg_t *pkg) +{ + ALPM_LOG_FUNC; - if(ret) { - RET_ERR(PM_ERR_SYSTEM, -1); - } + /* Sanity checks */ + ASSERT(handle != NULL, return(NULL)); + ASSERT(pkg != NULL, return(NULL)); + + char clfile[PATH_MAX]; + snprintf(clfile, PATH_MAX, "%s/%s/%s-%s/changelog", + alpm_option_get_dbpath(), + alpm_db_get_name(alpm_pkg_get_db(pkg)), + alpm_pkg_get_name(pkg), + alpm_pkg_get_version(pkg)); + return fopen(clfile, "r"); +} - return(0); +/** + * Read data from an open changelog 'file stream'. Similar to fread in + * functionality, this function takes a buffer and amount of data to read. + * @param ptr a buffer to fill with raw changelog data + * @param size the size of the buffer + * @param pkg the package that the changelog is being read from + * @param fp a 'file stream' to the package changelog + * @return the number of characters read, or 0 if there is no more data + */ +static size_t _cache_changelog_read(void *ptr, size_t size, + const pmpkg_t *pkg, const void *fp) +{ + return ( fread(ptr, 1, size, (FILE*)fp) ); +} + +/* +static int _cache_changelog_feof(const pmpkg_t *pkg, void *fp) +{ + return( feof((FILE*)fp) ); +} +*/ + +/** + * Close a package changelog for reading. Similar to fclose in functionality, + * except that the 'file stream' is from the database. + * @param pkg the package that the changelog was read from + * @param fp a 'file stream' to the package changelog + * @return whether closing the package changelog stream was successful + */ +static int _cache_changelog_close(const pmpkg_t *pkg, void *fp) +{ + return( fclose((FILE*)fp) ); } -static int splitname(const char *target, pmpkg_t *pkg) +/** The local database operations struct. Get package fields through + * lazy accessor methods that handle any backend loading and caching + * logic. + */ +static struct pkg_operations local_pkg_ops = { + .get_filename = _cache_get_filename, + .get_name = _cache_get_name, + .get_version = _cache_get_version, + .get_desc = _cache_get_desc, + .get_url = _cache_get_url, + .get_builddate = _cache_get_builddate, + .get_installdate = _cache_get_installdate, + .get_packager = _cache_get_packager, + .get_md5sum = _cache_get_md5sum, + .get_arch = _cache_get_arch, + .get_size = _cache_get_size, + .get_isize = _cache_get_isize, + .get_reason = _cache_get_reason, + .get_epoch = _cache_get_epoch, + .has_scriptlet = _cache_has_scriptlet, + .get_licenses = _cache_get_licenses, + .get_groups = _cache_get_groups, + .get_depends = _cache_get_depends, + .get_optdepends = _cache_get_optdepends, + .get_conflicts = _cache_get_conflicts, + .get_provides = _cache_get_provides, + .get_replaces = _cache_get_replaces, + .get_deltas = _cache_get_deltas, + .get_files = _cache_get_files, + .get_backup = _cache_get_backup, + + .changelog_open = _cache_changelog_open, + .changelog_read = _cache_changelog_read, + .changelog_close = _cache_changelog_close, +}; + +static int checkdbdir(pmdb_t *db) { - /* the format of a db entry is as follows: - * package-version-rel/ - * package name can contain hyphens, so parse from the back- go back - * two hyphens and we have split the version from the name. - */ - char *tmp, *p, *q; + struct stat buf; + const char *path = _alpm_db_path(db); - if(target == NULL || pkg == NULL) { - return(-1); - } - STRDUP(tmp, target, RET_ERR(PM_ERR_MEMORY, -1)); - p = tmp + strlen(tmp); - - /* do the magic parsing- find the beginning of the version string - * by doing two iterations of same loop to lop off two hyphens */ - for(q = --p; *q && *q != '-'; q--); - for(p = --q; *p && *p != '-'; p--); - if(*p != '-' || p == tmp) { - return(-1); + if(stat(path, &buf) != 0) { + _alpm_log(PM_LOG_DEBUG, "database dir '%s' does not exist, creating it\n", + path); + if(_alpm_makepath(path) != 0) { + RET_ERR(PM_ERR_SYSTEM, -1); + } + } else if(!S_ISDIR(buf.st_mode)) { + _alpm_log(PM_LOG_WARNING, _("removing invalid database: %s\n"), path); + if(unlink(path) != 0 || _alpm_makepath(path) != 0) { + RET_ERR(PM_ERR_SYSTEM, -1); + } } + return(0); +} - /* copy into fields and return */ - if(pkg->version) { - FREE(pkg->version); - } - STRDUP(pkg->version, p+1, RET_ERR(PM_ERR_MEMORY, -1)); - /* insert a terminator at the end of the name (on hyphen)- then copy it */ - *p = '\0'; - if(pkg->name) { - FREE(pkg->name); +static int is_dir(const char *path, struct dirent *entry) +{ +#ifdef HAVE_STRUCT_DIRENT_D_TYPE + return(entry->d_type == DT_DIR); +#else + char buffer[PATH_MAX]; + snprintf(buffer, PATH_MAX, "%s/%s", path, entry->d_name); + + struct stat sbuf; + if (!stat(buffer, &sbuf)) { + return(S_ISDIR(sbuf.st_mode)); } - STRDUP(pkg->name, tmp, RET_ERR(PM_ERR_MEMORY, -1)); - free(tmp); return(0); +#endif } -int _alpm_db_populate(pmdb_t *db) +static int local_db_populate(pmdb_t *db) { int count = 0; struct dirent *ent = NULL; - struct stat sbuf; - char path[PATH_MAX]; const char *dbpath; DIR *dbdir; @@ -359,20 +386,22 @@ int _alpm_db_populate(pmdb_t *db) ASSERT(db != NULL, RET_ERR(PM_ERR_DB_NULL, -1)); dbpath = _alpm_db_path(db); + if(dbpath == NULL) { + return(-1); + } dbdir = opendir(dbpath); if(dbdir == NULL) { return(0); } while((ent = readdir(dbdir)) != NULL) { const char *name = ent->d_name; + pmpkg_t *pkg; if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { continue; } - /* stat the entry, make sure it's a directory */ - snprintf(path, PATH_MAX, "%s%s", dbpath, name); - if(stat(path, &sbuf) != 0 || !S_ISDIR(sbuf.st_mode)) { + if(!is_dir(dbpath, ent)) { continue; } @@ -382,7 +411,7 @@ int _alpm_db_populate(pmdb_t *db) return(-1); } /* split the db entry name */ - if(splitname(name, pkg) != 0) { + if(_alpm_splitname(name, pkg) != 0) { _alpm_log(PM_LOG_ERROR, _("invalid name for database entry '%s'\n"), name); _alpm_pkg_free(pkg); @@ -397,12 +426,15 @@ int _alpm_db_populate(pmdb_t *db) } /* explicitly read with only 'BASE' data, accessors will handle the rest */ - if(_alpm_db_read(db, pkg, INFRQ_BASE) == -1) { + if(_alpm_local_db_read(db, pkg, INFRQ_BASE) == -1) { _alpm_log(PM_LOG_ERROR, _("corrupted database entry '%s'\n"), name); _alpm_pkg_free(pkg); continue; } - pkg->origin = PKG_FROM_CACHE; + + pkg->origin = PKG_FROM_LOCALDB; + pkg->ops = &local_pkg_ops; + pkg->origin_data.db = db; /* add to the collection */ _alpm_log(PM_LOG_FUNCTION, "adding '%s' to package cache for db '%s'\n", @@ -430,12 +462,12 @@ static char *get_pkgpath(pmdb_t *db, pmpkg_t *info) return(pkgpath); } -int _alpm_db_read(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq) + +int _alpm_local_db_read(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq) { FILE *fp = NULL; char path[PATH_MAX]; - char line[513]; - int sline = sizeof(line)-1; + char line[1024]; char *pkgpath = NULL; ALPM_LOG_FUNC; @@ -445,7 +477,7 @@ int _alpm_db_read(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq) } if(info == NULL || info->name == NULL || info->version == NULL) { - _alpm_log(PM_LOG_DEBUG, "invalid package entry provided to _alpm_db_read, skipping\n"); + _alpm_log(PM_LOG_DEBUG, "invalid package entry provided to _alpm_local_db_read, skipping\n"); return(-1); } @@ -467,7 +499,7 @@ int _alpm_db_read(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq) info->name, inforeq); /* clear out 'line', to be certain - and to make valgrind happy */ - memset(line, 0, sline+1); + memset(line, 0, sizeof(line)); pkgpath = get_pkgpath(db, info); @@ -486,12 +518,12 @@ int _alpm_db_read(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq) goto error; } while(!feof(fp)) { - if(fgets(line, 256, fp) == NULL) { + if(fgets(line, sizeof(line), fp) == NULL) { break; } _alpm_strtrim(line); if(strcmp(line, "%NAME%") == 0) { - if(fgets(line, sline, fp) == NULL) { + if(fgets(line, sizeof(line), fp) == NULL) { goto error; } if(strcmp(_alpm_strtrim(line), info->name) != 0) { @@ -499,47 +531,42 @@ int _alpm_db_read(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq) "mismatch on package %s\n"), db->treename, info->name); } } else if(strcmp(line, "%VERSION%") == 0) { - if(fgets(line, sline, fp) == NULL) { + if(fgets(line, sizeof(line), fp) == NULL) { goto error; } if(strcmp(_alpm_strtrim(line), info->version) != 0) { _alpm_log(PM_LOG_ERROR, _("%s database is inconsistent: version " "mismatch on package %s\n"), db->treename, info->name); } - } else if(strcmp(line, "%FILENAME%") == 0) { - if(fgets(line, sline, fp) == NULL) { - goto error; - } - STRDUP(info->filename, _alpm_strtrim(line), goto error); } else if(strcmp(line, "%DESC%") == 0) { - if(fgets(line, sline, fp) == NULL) { + if(fgets(line, sizeof(line), fp) == NULL) { goto error; } STRDUP(info->desc, _alpm_strtrim(line), goto error); } else if(strcmp(line, "%GROUPS%") == 0) { - while(fgets(line, sline, fp) && strlen(_alpm_strtrim(line))) { + while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) { char *linedup; STRDUP(linedup, _alpm_strtrim(line), goto error); info->groups = alpm_list_add(info->groups, linedup); } } else if(strcmp(line, "%URL%") == 0) { - if(fgets(line, sline, fp) == NULL) { + if(fgets(line, sizeof(line), fp) == NULL) { goto error; } STRDUP(info->url, _alpm_strtrim(line), goto error); } else if(strcmp(line, "%LICENSE%") == 0) { - while(fgets(line, sline, fp) && strlen(_alpm_strtrim(line))) { + while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) { char *linedup; STRDUP(linedup, _alpm_strtrim(line), goto error); info->licenses = alpm_list_add(info->licenses, linedup); } } else if(strcmp(line, "%ARCH%") == 0) { - if(fgets(line, sline, fp) == NULL) { + if(fgets(line, sizeof(line), fp) == NULL) { goto error; } STRDUP(info->arch, _alpm_strtrim(line), goto error); } else if(strcmp(line, "%BUILDDATE%") == 0) { - if(fgets(line, sline, fp) == NULL) { + if(fgets(line, sizeof(line), fp) == NULL) { goto error; } _alpm_strtrim(line); @@ -555,7 +582,7 @@ int _alpm_db_read(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq) info->builddate = atol(line); } } else if(strcmp(line, "%INSTALLDATE%") == 0) { - if(fgets(line, sline, fp) == NULL) { + if(fgets(line, sizeof(line), fp) == NULL) { goto error; } _alpm_strtrim(line); @@ -571,51 +598,67 @@ int _alpm_db_read(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq) info->installdate = atol(line); } } else if(strcmp(line, "%PACKAGER%") == 0) { - if(fgets(line, sline, fp) == NULL) { + if(fgets(line, sizeof(line), fp) == NULL) { goto error; } STRDUP(info->packager, _alpm_strtrim(line), goto error); } else if(strcmp(line, "%REASON%") == 0) { - if(fgets(line, sline, fp) == NULL) { + if(fgets(line, sizeof(line), fp) == NULL) { goto error; } info->reason = (pmpkgreason_t)atol(_alpm_strtrim(line)); - } else if(strcmp(line, "%SIZE%") == 0 || strcmp(line, "%CSIZE%") == 0) { + } else if(strcmp(line, "%SIZE%") == 0) { /* NOTE: the CSIZE and SIZE fields both share the "size" field * in the pkginfo_t struct. This can be done b/c CSIZE * is currently only used in sync databases, and SIZE is * only used in local databases. */ - if(fgets(line, sline, fp) == NULL) { + if(fgets(line, sizeof(line), fp) == NULL) { goto error; } info->size = atol(_alpm_strtrim(line)); - /* also store this value to isize if isize is unset */ - if(info->isize == 0) { - info->isize = info->size; + /* also store this value to isize */ + info->isize = info->size; + } else if(strcmp(line, "%REPLACES%") == 0) { + while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) { + char *linedup; + STRDUP(linedup, _alpm_strtrim(line), goto error); + info->replaces = alpm_list_add(info->replaces, linedup); } - } else if(strcmp(line, "%ISIZE%") == 0) { - /* ISIZE (installed size) tag only appears in sync repositories, - * not the local one. */ - if(fgets(line, sline, fp) == NULL) { + } else if(strcmp(line, "%EPOCH%") == 0) { + if(fgets(line, sizeof(line), fp) == NULL) { goto error; } - info->isize = atol(_alpm_strtrim(line)); - } else if(strcmp(line, "%MD5SUM%") == 0) { - /* MD5SUM tag only appears in sync repositories, - * not the local one. */ - if(fgets(line, sline, fp) == NULL) { - goto error; + info->epoch = atoi(_alpm_strtrim(line)); + } else if(strcmp(line, "%FORCE%") == 0) { + /* For backward compatibility, treat force as a non-zero epoch + * but only if we didn't already have a known epoch value. */ + if(!info->epoch) { + info->epoch = 1; } - STRDUP(info->md5sum, _alpm_strtrim(line), goto error); - } else if(strcmp(line, "%REPLACES%") == 0) { - while(fgets(line, sline, fp) && strlen(_alpm_strtrim(line))) { + } else if(strcmp(line, "%DEPENDS%") == 0) { + while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) { + pmdepend_t *dep = _alpm_splitdep(_alpm_strtrim(line)); + info->depends = alpm_list_add(info->depends, dep); + } + } else if(strcmp(line, "%OPTDEPENDS%") == 0) { + while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) { char *linedup; STRDUP(linedup, _alpm_strtrim(line), goto error); - info->replaces = alpm_list_add(info->replaces, linedup); + info->optdepends = alpm_list_add(info->optdepends, linedup); + } + } else if(strcmp(line, "%CONFLICTS%") == 0) { + while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) { + char *linedup; + STRDUP(linedup, _alpm_strtrim(line), goto error); + info->conflicts = alpm_list_add(info->conflicts, linedup); + } + } else if(strcmp(line, "%PROVIDES%") == 0) { + while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) { + char *linedup; + STRDUP(linedup, _alpm_strtrim(line), goto error); + info->provides = alpm_list_add(info->provides, linedup); } - } else if(strcmp(line, "%FORCE%") == 0) { - info->force = 1; } } fclose(fp); @@ -629,16 +672,16 @@ int _alpm_db_read(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq) _alpm_log(PM_LOG_ERROR, _("could not open file %s: %s\n"), path, strerror(errno)); goto error; } - while(fgets(line, 256, fp)) { + while(fgets(line, sizeof(line), fp)) { _alpm_strtrim(line); if(strcmp(line, "%FILES%") == 0) { - while(fgets(line, sline, fp) && strlen(_alpm_strtrim(line))) { + while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) { char *linedup; STRDUP(linedup, _alpm_strtrim(line), goto error); info->files = alpm_list_add(info->files, linedup); } } else if(strcmp(line, "%BACKUP%") == 0) { - while(fgets(line, sline, fp) && strlen(_alpm_strtrim(line))) { + while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) { char *linedup; STRDUP(linedup, _alpm_strtrim(line), goto error); info->backup = alpm_list_add(info->backup, linedup); @@ -649,66 +692,6 @@ int _alpm_db_read(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq) fp = NULL; } - /* DEPENDS */ - if(inforeq & INFRQ_DEPENDS) { - snprintf(path, PATH_MAX, "%sdepends", pkgpath); - if((fp = fopen(path, "r")) == NULL) { - _alpm_log(PM_LOG_ERROR, _("could not open file %s: %s\n"), path, strerror(errno)); - goto error; - } - while(!feof(fp)) { - fgets(line, 255, fp); - _alpm_strtrim(line); - if(strcmp(line, "%DEPENDS%") == 0) { - while(fgets(line, sline, fp) && strlen(_alpm_strtrim(line))) { - pmdepend_t *dep = _alpm_splitdep(_alpm_strtrim(line)); - info->depends = alpm_list_add(info->depends, dep); - } - } else if(strcmp(line, "%OPTDEPENDS%") == 0) { - while(fgets(line, sline, fp) && strlen(_alpm_strtrim(line))) { - char *linedup; - STRDUP(linedup, _alpm_strtrim(line), goto error); - info->optdepends = alpm_list_add(info->optdepends, linedup); - } - } else if(strcmp(line, "%CONFLICTS%") == 0) { - while(fgets(line, sline, fp) && strlen(_alpm_strtrim(line))) { - char *linedup; - STRDUP(linedup, _alpm_strtrim(line), goto error); - info->conflicts = alpm_list_add(info->conflicts, linedup); - } - } else if(strcmp(line, "%PROVIDES%") == 0) { - while(fgets(line, sline, fp) && strlen(_alpm_strtrim(line))) { - char *linedup; - STRDUP(linedup, _alpm_strtrim(line), goto error); - info->provides = alpm_list_add(info->provides, linedup); - } - } - } - fclose(fp); - fp = NULL; - } - - /* DELTAS */ - if(inforeq & INFRQ_DELTAS) { - snprintf(path, PATH_MAX, "%sdeltas", pkgpath); - if((fp = fopen(path, "r"))) { - while(!feof(fp)) { - fgets(line, 255, fp); - _alpm_strtrim(line); - if(strcmp(line, "%DELTAS%") == 0) { - while(fgets(line, sline, fp) && strlen(_alpm_strtrim(line))) { - pmdelta_t *delta = _alpm_delta_parse(line); - if(delta) { - info->deltas = alpm_list_add(info->deltas, delta); - } - } - } - } - fclose(fp); - fp = NULL; - } - } - /* INSTALL */ if(inforeq & INFRQ_SCRIPTLET) { snprintf(path, PATH_MAX, "%sinstall", pkgpath); @@ -731,7 +714,7 @@ error: return(-1); } -int _alpm_db_prepare(pmdb_t *db, pmpkg_t *info) +int _alpm_local_db_prepare(pmdb_t *db, pmpkg_t *info) { mode_t oldmask; int retval = 0; @@ -755,14 +738,13 @@ int _alpm_db_prepare(pmdb_t *db, pmpkg_t *info) return(retval); } -int _alpm_db_write(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq) +int _alpm_local_db_write(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq) { FILE *fp = NULL; char path[PATH_MAX]; mode_t oldmask; alpm_list_t *lp = NULL; int retval = 0; - int local = 0; char *pkgpath = NULL; ALPM_LOG_FUNC; @@ -776,8 +758,8 @@ int _alpm_db_write(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq) /* make sure we have a sane umask */ oldmask = umask(0022); - if(strcmp(db->treename, "local") == 0) { - local = 1; + if(strcmp(db->treename, "local") != 0) { + return(-1); } /* DESC */ @@ -810,102 +792,45 @@ int _alpm_db_write(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq) } fprintf(fp, "\n"); } - if(local) { - if(info->force) { - fprintf(fp, "%%EPOCH%%\n" - "1\n\n"); - } - if(info->url) { - fprintf(fp, "%%URL%%\n" - "%s\n\n", info->url); - } - if(info->licenses) { - fputs("%LICENSE%\n", fp); - for(lp = info->licenses; lp; lp = lp->next) { - fprintf(fp, "%s\n", (char *)lp->data); - } - fprintf(fp, "\n"); - } - if(info->arch) { - fprintf(fp, "%%ARCH%%\n" - "%s\n\n", info->arch); - } - if(info->builddate) { - fprintf(fp, "%%BUILDDATE%%\n" - "%ld\n\n", info->builddate); - } - if(info->installdate) { - fprintf(fp, "%%INSTALLDATE%%\n" - "%ld\n\n", info->installdate); - } - if(info->packager) { - fprintf(fp, "%%PACKAGER%%\n" - "%s\n\n", info->packager); - } - if(info->isize) { - /* only write installed size, csize is irrelevant once installed */ - fprintf(fp, "%%SIZE%%\n" - "%jd\n\n", (intmax_t)info->isize); - } - if(info->reason) { - fprintf(fp, "%%REASON%%\n" - "%u\n\n", info->reason); - } - } else { - if(info->size) { - fprintf(fp, "%%CSIZE%%\n" - "%jd\n\n", (intmax_t)info->size); - } - if(info->isize) { - fprintf(fp, "%%ISIZE%%\n" - "%jd\n\n", (intmax_t)info->isize); - } - if(info->md5sum) { - fprintf(fp, "%%MD5SUM%%\n" - "%s\n\n", info->md5sum); - } + if(info->epoch) { + fprintf(fp, "%%EPOCH%%\n" + "%d\n\n", info->epoch); } - fclose(fp); - fp = NULL; - } - - /* FILES */ - if(local && (inforeq & INFRQ_FILES)) { - _alpm_log(PM_LOG_DEBUG, "writing %s-%s FILES information back to db\n", - info->name, info->version); - snprintf(path, PATH_MAX, "%sfiles", pkgpath); - if((fp = fopen(path, "w")) == NULL) { - _alpm_log(PM_LOG_ERROR, _("could not open file %s: %s\n"), path, strerror(errno)); - retval = -1; - goto cleanup; + if(info->url) { + fprintf(fp, "%%URL%%\n" + "%s\n\n", info->url); } - if(info->files) { - fprintf(fp, "%%FILES%%\n"); - for(lp = info->files; lp; lp = lp->next) { + if(info->licenses) { + fputs("%LICENSE%\n", fp); + for(lp = info->licenses; lp; lp = lp->next) { fprintf(fp, "%s\n", (char *)lp->data); } fprintf(fp, "\n"); } - if(info->backup) { - fprintf(fp, "%%BACKUP%%\n"); - for(lp = info->backup; lp; lp = lp->next) { - fprintf(fp, "%s\n", (char *)lp->data); - } - fprintf(fp, "\n"); + if(info->arch) { + fprintf(fp, "%%ARCH%%\n" + "%s\n\n", info->arch); } - fclose(fp); - fp = NULL; - } - - /* DEPENDS */ - if(inforeq & INFRQ_DEPENDS) { - _alpm_log(PM_LOG_DEBUG, "writing %s-%s DEPENDS information back to db\n", - info->name, info->version); - snprintf(path, PATH_MAX, "%sdepends", pkgpath); - if((fp = fopen(path, "w")) == NULL) { - _alpm_log(PM_LOG_ERROR, _("could not open file %s: %s\n"), path, strerror(errno)); - retval = -1; - goto cleanup; + if(info->builddate) { + fprintf(fp, "%%BUILDDATE%%\n" + "%ld\n\n", info->builddate); + } + if(info->installdate) { + fprintf(fp, "%%INSTALLDATE%%\n" + "%ld\n\n", info->installdate); + } + if(info->packager) { + fprintf(fp, "%%PACKAGER%%\n" + "%s\n\n", info->packager); + } + if(info->isize) { + /* only write installed size, csize is irrelevant once installed */ + fprintf(fp, "%%SIZE%%\n" + "%jd\n\n", (intmax_t)info->isize); + } + if(info->reason) { + fprintf(fp, "%%REASON%%\n" + "%u\n\n", info->reason); } if(info->depends) { fputs("%DEPENDS%\n", fp); @@ -937,6 +862,35 @@ int _alpm_db_write(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq) } fprintf(fp, "\n"); } + + fclose(fp); + fp = NULL; + } + + /* FILES */ + if(inforeq & INFRQ_FILES) { + _alpm_log(PM_LOG_DEBUG, "writing %s-%s FILES information back to db\n", + info->name, info->version); + snprintf(path, PATH_MAX, "%sfiles", pkgpath); + if((fp = fopen(path, "w")) == NULL) { + _alpm_log(PM_LOG_ERROR, _("could not open file %s: %s\n"), path, strerror(errno)); + retval = -1; + goto cleanup; + } + if(info->files) { + fprintf(fp, "%%FILES%%\n"); + for(lp = info->files; lp; lp = lp->next) { + fprintf(fp, "%s\n", (char *)lp->data); + } + fprintf(fp, "\n"); + } + if(info->backup) { + fprintf(fp, "%%BACKUP%%\n"); + for(lp = info->backup; lp; lp = lp->next) { + fprintf(fp, "%s\n", (char *)lp->data); + } + fprintf(fp, "\n"); + } fclose(fp); fp = NULL; } @@ -955,7 +909,7 @@ cleanup: return(retval); } -int _alpm_db_remove(pmdb_t *db, pmpkg_t *info) +int _alpm_local_db_remove(pmdb_t *db, pmpkg_t *info) { int ret = 0; char *pkgpath = NULL; @@ -976,4 +930,33 @@ int _alpm_db_remove(pmdb_t *db, pmpkg_t *info) return(ret); } +struct db_operations local_db_ops = { + .populate = local_db_populate, + .unregister = _alpm_db_unregister, +}; + +pmdb_t *_alpm_db_register_local(void) +{ + pmdb_t *db; + + ALPM_LOG_FUNC; + + if(handle->db_local != NULL) { + _alpm_log(PM_LOG_WARNING, _("attempt to re-register the 'local' DB\n")); + RET_ERR(PM_ERR_DB_NOT_NULL, NULL); + } + + _alpm_log(PM_LOG_DEBUG, "registering local database\n"); + + db = _alpm_db_new("local", 1); + db->ops = &local_db_ops; + if(db == NULL) { + RET_ERR(PM_ERR_DB_CREATE, NULL); + } + + handle->db_local = db; + return(db); +} + + /* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/be_package.c b/lib/libalpm/be_package.c index ff266ae8..90fd4124 100644 --- a/lib/libalpm/be_package.c +++ b/lib/libalpm/be_package.c @@ -26,6 +26,7 @@ #include <limits.h> #include <ctype.h> #include <locale.h> /* setlocale */ +#include <errno.h> /* libarchive */ #include <archive.h> @@ -39,6 +40,113 @@ #include "deps.h" /* _alpm_splitdep */ /** + * Open a package changelog for reading. Similar to fopen in functionality, + * except that the returned 'file stream' is from an archive. + * @param pkg the package (file) to read the changelog + * @return a 'file stream' to the package changelog + */ +static void *_package_changelog_open(pmpkg_t *pkg) +{ + ALPM_LOG_FUNC; + + ASSERT(pkg != NULL, return(NULL)); + + struct archive *archive = NULL; + struct archive_entry *entry; + const char *pkgfile = pkg->origin_data.file; + + if((archive = archive_read_new()) == NULL) { + RET_ERR(PM_ERR_LIBARCHIVE, NULL); + } + + archive_read_support_compression_all(archive); + archive_read_support_format_all(archive); + + if (archive_read_open_filename(archive, pkgfile, + ARCHIVE_DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK) { + RET_ERR(PM_ERR_PKG_OPEN, NULL); + } + + while(archive_read_next_header(archive, &entry) == ARCHIVE_OK) { + const char *entry_name = archive_entry_pathname(entry); + + if(strcmp(entry_name, ".CHANGELOG") == 0) { + return(archive); + } + } + /* we didn't find a changelog */ + archive_read_finish(archive); + errno = ENOENT; + + return(NULL); +} + +/** + * Read data from an open changelog 'file stream'. Similar to fread in + * functionality, this function takes a buffer and amount of data to read. + * @param ptr a buffer to fill with raw changelog data + * @param size the size of the buffer + * @param pkg the package that the changelog is being read from + * @param fp a 'file stream' to the package changelog + * @return the number of characters read, or 0 if there is no more data + */ +static size_t _package_changelog_read(void *ptr, size_t size, + const pmpkg_t *pkg, const void *fp) +{ + ssize_t sret = archive_read_data((struct archive*)fp, ptr, size); + /* Report error (negative values) */ + if(sret < 0) { + pm_errno = PM_ERR_LIBARCHIVE; + return(0); + } else { + return((size_t)sret); + } +} + +/* +static int _package_changelog_feof(const pmpkg_t *pkg, void *fp) +{ + // note: this doesn't quite work, no feof in libarchive + return( archive_read_data((struct archive*)fp, NULL, 0) ); +} +*/ + +/** + * Close a package changelog for reading. Similar to fclose in functionality, + * except that the 'file stream' is from an archive. + * @param pkg the package (file) that the changelog was read from + * @param fp a 'file stream' to the package changelog + * @return whether closing the package changelog stream was successful + */ +static int _package_changelog_close(const pmpkg_t *pkg, void *fp) +{ + return( archive_read_finish((struct archive *)fp) ); +} + + +/** Package file operations struct accessor. We implement this as a method + * rather than a static struct as in be_files because we want to reuse the + * majority of the default_pkg_ops struct and add only a few operations of + * our own on top. The static file_pkg_ops variable inside this function + * lets us only initialize an operations struct once which can always be + * accessed by this method. + */ +static struct pkg_operations *get_file_pkg_ops(void) +{ + static struct pkg_operations *file_pkg_ops = NULL; + /* determine whether our static file_pkg_ops struct has been initialized */ + if(!file_pkg_ops) { + MALLOC(file_pkg_ops, sizeof(struct pkg_operations), + RET_ERR(PM_ERR_MEMORY, NULL)); + memcpy(file_pkg_ops, &default_pkg_ops, sizeof(struct pkg_operations)); + file_pkg_ops->changelog_open = _package_changelog_open; + file_pkg_ops->changelog_read = _package_changelog_read; + file_pkg_ops->changelog_close = _package_changelog_close; + } + return(file_pkg_ops); +} + +/** * Parses the package description file for a package into a pmpkg_t struct. * @param archive the archive to read from, pointed at the .PKGINFO entry * @param newpkg an empty pmpkg_t struct to fill with package info @@ -47,17 +155,22 @@ */ static int parse_descfile(struct archive *a, pmpkg_t *newpkg) { - char line[PATH_MAX]; char *ptr = NULL; char *key = NULL; int linenum = 0; + struct archive_read_buffer buf; ALPM_LOG_FUNC; - /* loop until we reach EOF (where archive_fgets will return NULL) */ - while(_alpm_archive_fgets(line, PATH_MAX, a) != NULL) { + memset(&buf, 0, sizeof(buf)); + /* 512K for a line length seems reasonable */ + buf.max_line_size = 512 * 1024; + + /* loop until we reach EOF or other error */ + while(_alpm_archive_fgets(a, &buf) == ARCHIVE_OK) { + char *line = _alpm_strtrim(buf.line); + linenum++; - _alpm_strtrim(line); if(strlen(line) == 0 || line[0] == '#') { continue; } @@ -69,21 +182,27 @@ static int parse_descfile(struct archive *a, pmpkg_t *newpkg) } else { key = _alpm_strtrim(key); ptr = _alpm_strtrim(ptr); - if(!strcmp(key, "pkgname")) { + if(strcmp(key, "pkgname") == 0) { STRDUP(newpkg->name, ptr, RET_ERR(PM_ERR_MEMORY, -1)); - } else if(!strcmp(key, "pkgver")) { + newpkg->name_hash = _alpm_hash_sdbm(newpkg->name); + } else if(strcmp(key, "pkgver") == 0) { STRDUP(newpkg->version, ptr, RET_ERR(PM_ERR_MEMORY, -1)); - } else if(!strcmp(key, "pkgdesc")) { + } else if(strcmp(key, "pkgdesc") == 0) { STRDUP(newpkg->desc, ptr, RET_ERR(PM_ERR_MEMORY, -1)); - } else if(!strcmp(key, "force")) { - newpkg->force = 1; - } else if(!strcmp(key, "group")) { + } else if(strcmp(key, "force") == 0) { + /* For backward compatibility, like in sync_db_read */ + if(!newpkg->epoch) { + newpkg->epoch = 1; + } + } else if(strcmp(key, "epoch") == 0) { + newpkg->epoch = atoi(ptr); + } else if(strcmp(key, "group") == 0) { newpkg->groups = alpm_list_add(newpkg->groups, strdup(ptr)); - } else if(!strcmp(key, "url")) { + } else if(strcmp(key, "url") == 0) { STRDUP(newpkg->url, ptr, RET_ERR(PM_ERR_MEMORY, -1)); - } else if(!strcmp(key, "license")) { + } else if(strcmp(key, "license") == 0) { newpkg->licenses = alpm_list_add(newpkg->licenses, strdup(ptr)); - } else if(!strcmp(key, "builddate")) { + } else if(strcmp(key, "builddate") == 0) { char first = tolower((unsigned char)ptr[0]); if(first > 'a' && first < 'z') { struct tm tmp_tm = {0}; /* initialize to null in case of failure */ @@ -94,27 +213,27 @@ static int parse_descfile(struct archive *a, pmpkg_t *newpkg) } else { newpkg->builddate = atol(ptr); } - } else if(!strcmp(key, "packager")) { + } else if(strcmp(key, "packager") == 0) { STRDUP(newpkg->packager, ptr, RET_ERR(PM_ERR_MEMORY, -1)); - } else if(!strcmp(key, "arch")) { + } else if(strcmp(key, "arch") == 0) { STRDUP(newpkg->arch, ptr, RET_ERR(PM_ERR_MEMORY, -1)); - } else if(!strcmp(key, "size")) { + } else if(strcmp(key, "size") == 0) { /* size in the raw package is uncompressed (installed) size */ newpkg->isize = atol(ptr); - } else if(!strcmp(key, "depend")) { + } else if(strcmp(key, "depend") == 0) { pmdepend_t *dep = _alpm_splitdep(ptr); newpkg->depends = alpm_list_add(newpkg->depends, dep); - } else if(!strcmp(key, "optdepend")) { + } else if(strcmp(key, "optdepend") == 0) { newpkg->optdepends = alpm_list_add(newpkg->optdepends, strdup(ptr)); - } else if(!strcmp(key, "conflict")) { + } else if(strcmp(key, "conflict") == 0) { newpkg->conflicts = alpm_list_add(newpkg->conflicts, strdup(ptr)); - } else if(!strcmp(key, "replaces")) { + } else if(strcmp(key, "replaces") == 0) { newpkg->replaces = alpm_list_add(newpkg->replaces, strdup(ptr)); - } else if(!strcmp(key, "provides")) { + } else if(strcmp(key, "provides") == 0) { newpkg->provides = alpm_list_add(newpkg->provides, strdup(ptr)); - } else if(!strcmp(key, "backup")) { + } else if(strcmp(key, "backup") == 0) { newpkg->backup = alpm_list_add(newpkg->backup, strdup(ptr)); - } else if(!strcmp(key, "makepkgopt")) { + } else if(strcmp(key, "makepkgopt") == 0) { /* not used atm */ } else { _alpm_log(PM_LOG_DEBUG, "%s: syntax error in description file line %d\n", @@ -232,11 +351,13 @@ static pmpkg_t *pkg_load(const char *pkgfile, int full) goto pkg_invalid; } - archive_read_finish(archive); + archive_read_finish(archive); /* internal fields for package struct */ newpkg->origin = PKG_FROM_FILE; + /* TODO eventually kill/move this? */ newpkg->origin_data.file = strdup(pkgfile); + newpkg->ops = get_file_pkg_ops(); if(full) { /* "checking for conflicts" requires a sorted list, ensure that here */ @@ -247,7 +368,7 @@ static pmpkg_t *pkg_load(const char *pkgfile, int full) } else { /* get rid of any partial filelist we may have collected, it is invalid */ FREELIST(newpkg->files); - newpkg->infolevel = INFRQ_BASE | INFRQ_DESC | INFRQ_DEPENDS; + newpkg->infolevel = INFRQ_BASE | INFRQ_DESC; } return(newpkg); diff --git a/lib/libalpm/be_sync.c b/lib/libalpm/be_sync.c new file mode 100644 index 00000000..4786864a --- /dev/null +++ b/lib/libalpm/be_sync.c @@ -0,0 +1,418 @@ +/* + * be_sync.c + * + * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <errno.h> +#include <ctype.h> +#include <locale.h> +#include <limits.h> + +/* libarchive */ +#include <archive.h> +#include <archive_entry.h> + +/* libalpm */ +#include "util.h" +#include "log.h" +#include "alpm.h" +#include "alpm_list.h" +#include "package.h" +#include "handle.h" +#include "delta.h" +#include "deps.h" +#include "dload.h" + +/** Update a package database + * + * An update of the package database \a db will be attempted. Unless + * \a force is true, the update will only be performed if the remote + * database was modified since the last update. + * + * A transaction is necessary for this operation, in order to obtain a + * database lock. During this transaction the front-end will be informed + * of the download progress of the database via the download callback. + * + * Example: + * @code + * pmdb_t *db; + * int result; + * db = alpm_list_getdata(alpm_option_get_syncdbs()); + * if(alpm_trans_init(0, NULL, NULL, NULL) == 0) { + * result = alpm_db_update(0, db); + * alpm_trans_release(); + * + * if(result > 0) { + * printf("Unable to update database: %s\n", alpm_strerrorlast()); + * } else if(result < 0) { + * printf("Database already up to date\n"); + * } else { + * printf("Database updated\n"); + * } + * } + * @endcode + * + * @ingroup alpm_databases + * @note After a successful update, the \link alpm_db_get_pkgcache() + * package cache \endlink will be invalidated + * @param force if true, then forces the update, otherwise update only in case + * the database isn't up to date + * @param db pointer to the package database to update + * @return 0 on success, > 0 on error (pm_errno is set accordingly), < 0 if up + * to date + */ +int SYMEXPORT alpm_db_update(int force, pmdb_t *db) +{ + char *dbfile, *syncpath; + const char *dbpath; + struct stat buf; + size_t len; + int ret; + + ALPM_LOG_FUNC; + + /* Sanity checks */ + ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1)); + ASSERT(db != NULL && db != handle->db_local, RET_ERR(PM_ERR_WRONG_ARGS, -1)); + + if(!alpm_list_find_ptr(handle->dbs_sync, db)) { + RET_ERR(PM_ERR_DB_NOT_FOUND, -1); + } + + len = strlen(db->treename) + 4; + MALLOC(dbfile, len, RET_ERR(PM_ERR_MEMORY, -1)); + sprintf(dbfile, "%s.db", db->treename); + + dbpath = alpm_option_get_dbpath(); + len = strlen(dbpath) + 6; + MALLOC(syncpath, len, RET_ERR(PM_ERR_MEMORY, -1)); + sprintf(syncpath, "%s%s", dbpath, "sync/"); + + if(stat(syncpath, &buf) != 0) { + _alpm_log(PM_LOG_DEBUG, "database dir '%s' does not exist, creating it\n", + syncpath); + if(_alpm_makepath(syncpath) != 0) { + free(dbfile); + free(syncpath); + RET_ERR(PM_ERR_SYSTEM, -1); + } + } else if(!S_ISDIR(buf.st_mode)) { + _alpm_log(PM_LOG_WARNING, _("removing invalid file: %s\n"), syncpath); + if(unlink(syncpath) != 0 || _alpm_makepath(syncpath) != 0) { + free(dbfile); + free(syncpath); + RET_ERR(PM_ERR_SYSTEM, -1); + } + } + + ret = _alpm_download_single_file(dbfile, db->servers, syncpath, force); + free(dbfile); + free(syncpath); + + if(ret == 1) { + /* files match, do nothing */ + pm_errno = 0; + return(1); + } else if(ret == -1) { + /* pm_errno was set by the download code */ + _alpm_log(PM_LOG_DEBUG, "failed to sync db: %s\n", alpm_strerrorlast()); + return(-1); + } + + /* Cache needs to be rebuilt */ + _alpm_db_free_pkgcache(db); + + return(0); +} + +/* Forward decl so I don't reorganize the whole file right now */ +static int sync_db_read(pmdb_t *db, struct archive *archive, struct archive_entry *entry); + +static int sync_db_populate(pmdb_t *db) +{ + int count = 0; + struct archive *archive; + struct archive_entry *entry; + + ALPM_LOG_FUNC; + + ASSERT(db != NULL, RET_ERR(PM_ERR_DB_NULL, -1)); + + if((archive = archive_read_new()) == NULL) + RET_ERR(PM_ERR_LIBARCHIVE, 1); + + archive_read_support_compression_all(archive); + archive_read_support_format_all(archive); + + if(archive_read_open_filename(archive, _alpm_db_path(db), + ARCHIVE_DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK) { + _alpm_log(PM_LOG_ERROR, _("could not open %s: %s\n"), _alpm_db_path(db), + archive_error_string(archive)); + RET_ERR(PM_ERR_PKG_OPEN, 1); + } + + while(archive_read_next_header(archive, &entry) == ARCHIVE_OK) { + const struct stat *st; + const char *name; + pmpkg_t *pkg; + + st = archive_entry_stat(entry); + + if(S_ISDIR(st->st_mode)) { + pkg = _alpm_pkg_new(); + if(pkg == NULL) { + archive_read_finish(archive); + return(-1); + } + + name = archive_entry_pathname(entry); + + if(_alpm_splitname(name, pkg) != 0) { + _alpm_log(PM_LOG_ERROR, _("invalid name for database entry '%s'\n"), + name); + _alpm_pkg_free(pkg); + continue; + } + + /* duplicated database entries are not allowed */ + if(_alpm_pkg_find(db->pkgcache, pkg->name)) { + _alpm_log(PM_LOG_ERROR, _("duplicated database entry '%s'\n"), pkg->name); + _alpm_pkg_free(pkg); + continue; + } + + pkg->origin = PKG_FROM_SYNCDB; + pkg->ops = &default_pkg_ops; + pkg->origin_data.db = db; + + /* add to the collection */ + _alpm_log(PM_LOG_FUNCTION, "adding '%s' to package cache for db '%s'\n", + pkg->name, db->treename); + db->pkgcache = alpm_list_add(db->pkgcache, pkg); + count++; + } else { + /* we have desc, depends or deltas - parse it */ + sync_db_read(db, archive, entry); + } + } + + db->pkgcache = alpm_list_msort(db->pkgcache, count, _alpm_pkg_cmp); + archive_read_finish(archive); + + return(count); +} + +#define READ_NEXT(s) do { \ + if(_alpm_archive_fgets(archive, &buf) != ARCHIVE_OK) goto error; \ + s = _alpm_strtrim(buf.line); \ +} while(0) + +#define READ_AND_STORE(f) do { \ + READ_NEXT(line); \ + STRDUP(f, line, goto error); \ +} while(0) + +#define READ_AND_STORE_ALL(f) do { \ + char *linedup; \ + READ_NEXT(line); \ + if(strlen(line) == 0) break; \ + STRDUP(linedup, line, goto error); \ + f = alpm_list_add(f, linedup); \ +} while(1) /* note the while(1) and not (0) */ + +static int sync_db_read(pmdb_t *db, struct archive *archive, struct archive_entry *entry) +{ + const char *entryname = NULL; + char *filename, *pkgname, *p, *q; + pmpkg_t *pkg; + struct archive_read_buffer buf; + + ALPM_LOG_FUNC; + + if(db == NULL) { + RET_ERR(PM_ERR_DB_NULL, -1); + } + + if(entry != NULL) { + entryname = archive_entry_pathname(entry); + } + if(entryname == NULL) { + _alpm_log(PM_LOG_DEBUG, "invalid archive entry provided to _alpm_sync_db_read, skipping\n"); + return(-1); + } + + _alpm_log(PM_LOG_FUNCTION, "loading package data from archive entry %s\n", + entryname); + + memset(&buf, 0, sizeof(buf)); + /* 512K for a line length seems reasonable */ + buf.max_line_size = 512 * 1024; + + /* get package and db file names */ + STRDUP(pkgname, entryname, RET_ERR(PM_ERR_MEMORY, -1)); + p = pkgname + strlen(pkgname); + for(q = --p; *q && *q != '/'; q--); + STRDUP(filename, q+1, RET_ERR(PM_ERR_MEMORY, -1)); + for(p = --q; *p && *p != '-'; p--); + for(q = --p; *q && *q != '-'; q--); + *q = '\0'; + + /* package is already in db due to parsing of directory name */ + pkg = _alpm_pkg_find(db->pkgcache, pkgname); + if(pkg == NULL) { + _alpm_log(PM_LOG_DEBUG, "package %s not found in %s sync database", + pkgname, db->treename); + return(-1); + } + + if(strcmp(filename, "desc") == 0 || strcmp(filename, "depends") == 0 + || strcmp(filename, "deltas") == 0) { + while(_alpm_archive_fgets(archive, &buf) == ARCHIVE_OK) { + char *line = _alpm_strtrim(buf.line); + + if(strcmp(line, "%NAME%") == 0) { + READ_NEXT(line); + if(strcmp(line, pkg->name) != 0) { + _alpm_log(PM_LOG_ERROR, _("%s database is inconsistent: name " + "mismatch on package %s\n"), db->treename, pkg->name); + } + } else if(strcmp(line, "%VERSION%") == 0) { + READ_NEXT(line); + if(strcmp(line, pkg->version) != 0) { + _alpm_log(PM_LOG_ERROR, _("%s database is inconsistent: version " + "mismatch on package %s\n"), db->treename, pkg->name); + } + } else if(strcmp(line, "%FILENAME%") == 0) { + READ_AND_STORE(pkg->filename); + } else if(strcmp(line, "%DESC%") == 0) { + READ_AND_STORE(pkg->desc); + } else if(strcmp(line, "%GROUPS%") == 0) { + READ_AND_STORE_ALL(pkg->groups); + } else if(strcmp(line, "%URL%") == 0) { + READ_AND_STORE(pkg->url); + } else if(strcmp(line, "%LICENSE%") == 0) { + READ_AND_STORE_ALL(pkg->licenses); + } else if(strcmp(line, "%ARCH%") == 0) { + READ_AND_STORE(pkg->arch); + } else if(strcmp(line, "%BUILDDATE%") == 0) { + READ_NEXT(line); + char first = tolower((unsigned char)line[0]); + if(first > 'a' && first < 'z') { + /* initialize to null in case of failure */ + struct tm tmp_tm = {0}; + setlocale(LC_TIME, "C"); + strptime(line, "%a %b %e %H:%M:%S %Y", &tmp_tm); + pkg->builddate = mktime(&tmp_tm); + setlocale(LC_TIME, ""); + } else { + pkg->builddate = atol(line); + } + } else if(strcmp(line, "%PACKAGER%") == 0) { + READ_AND_STORE(pkg->packager); + } else if(strcmp(line, "%CSIZE%") == 0) { + /* Note: the CSIZE and SIZE fields both share the "size" field in the + * pkginfo_t struct. This can be done b/c CSIZE is currently only used + * in sync databases, and SIZE is only used in local databases. + */ + READ_NEXT(line); + pkg->size = atol(line); + /* also store this value to isize if isize is unset */ + if(pkg->isize == 0) { + pkg->isize = pkg->size; + } + } else if(strcmp(line, "%ISIZE%") == 0) { + READ_NEXT(line); + pkg->isize = atol(line); + } else if(strcmp(line, "%MD5SUM%") == 0) { + READ_AND_STORE(pkg->md5sum); + } else if(strcmp(line, "%REPLACES%") == 0) { + READ_AND_STORE_ALL(pkg->replaces); + } else if(strcmp(line, "%EPOCH%") == 0) { + READ_NEXT(line); + pkg->epoch = atoi(line); + } else if(strcmp(line, "%FORCE%") == 0) { + /* For backward compatibility, treat force as a non-zero epoch + * but only if we didn't already have a known epoch value. */ + if(!pkg->epoch) { + pkg->epoch = 1; + } + } else if(strcmp(line, "%DEPENDS%") == 0) { + /* Different than the rest because of the _alpm_splitdep call. */ + while(1) { + READ_NEXT(line); + if(strlen(line) == 0) break; + pkg->depends = alpm_list_add(pkg->depends, _alpm_splitdep(line)); + } + } else if(strcmp(line, "%OPTDEPENDS%") == 0) { + READ_AND_STORE_ALL(pkg->optdepends); + } else if(strcmp(line, "%CONFLICTS%") == 0) { + READ_AND_STORE_ALL(pkg->conflicts); + } else if(strcmp(line, "%PROVIDES%") == 0) { + READ_AND_STORE_ALL(pkg->provides); + } else if(strcmp(line, "%DELTAS%") == 0) { + READ_AND_STORE_ALL(pkg->deltas); + } + } + } else { + /* unknown database file */ + _alpm_log(PM_LOG_DEBUG, "unknown database file: %s", filename); + } + +error: + FREE(pkgname); + FREE(filename); + /* TODO: return 0 always? */ + return(0); +} + +struct db_operations sync_db_ops = { + .populate = sync_db_populate, + .unregister = _alpm_db_unregister, +}; + +pmdb_t *_alpm_db_register_sync(const char *treename) +{ + pmdb_t *db; + alpm_list_t *i; + + ALPM_LOG_FUNC; + + for(i = handle->dbs_sync; i; i = i->next) { + pmdb_t *sdb = i->data; + if(strcmp(treename, sdb->treename) == 0) { + _alpm_log(PM_LOG_DEBUG, "attempt to re-register the '%s' database, using existing\n", sdb->treename); + return sdb; + } + } + + _alpm_log(PM_LOG_DEBUG, "registering sync database '%s'\n", treename); + + db = _alpm_db_new(treename, 0); + db->ops = &sync_db_ops; + if(db == NULL) { + RET_ERR(PM_ERR_DB_CREATE, NULL); + } + + handle->dbs_sync = alpm_list_add(handle->dbs_sync, db); + return(db); +} + + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/cache.c b/lib/libalpm/cache.c deleted file mode 100644 index a9a7edd9..00000000 --- a/lib/libalpm/cache.c +++ /dev/null @@ -1,291 +0,0 @@ -/* - * cache.c - * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> - * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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, see <http://www.gnu.org/licenses/>. - */ - -#include "config.h" - -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <string.h> - -/* libalpm */ -#include "cache.h" -#include "alpm_list.h" -#include "log.h" -#include "alpm.h" -#include "util.h" -#include "package.h" -#include "group.h" -#include "db.h" - -/* Returns a new package cache from db. - * It frees the cache if it already exists. - */ -int _alpm_db_load_pkgcache(pmdb_t *db) -{ - ALPM_LOG_FUNC; - - if(db == NULL) { - return(-1); - } - _alpm_db_free_pkgcache(db); - - _alpm_log(PM_LOG_DEBUG, "loading package cache for repository '%s'\n", - db->treename); - if(_alpm_db_populate(db) == -1) { - _alpm_log(PM_LOG_DEBUG, - "failed to load package cache for repository '%s'\n", db->treename); - return(-1); - } - - db->pkgcache_loaded = 1; - return(0); -} - -void _alpm_db_free_pkgcache(pmdb_t *db) -{ - ALPM_LOG_FUNC; - - if(db == NULL || !db->pkgcache_loaded) { - return; - } - - _alpm_log(PM_LOG_DEBUG, "freeing package cache for repository '%s'\n", - db->treename); - - alpm_list_free_inner(db->pkgcache, (alpm_list_fn_free)_alpm_pkg_free); - alpm_list_free(db->pkgcache); - db->pkgcache = NULL; - db->pkgcache_loaded = 0; - - _alpm_db_free_grpcache(db); -} - -alpm_list_t *_alpm_db_get_pkgcache(pmdb_t *db) -{ - ALPM_LOG_FUNC; - - if(db == NULL) { - return(NULL); - } - - if(!db->pkgcache_loaded) { - _alpm_db_load_pkgcache(db); - } - - /* hmmm, still NULL ?*/ - if(!db->pkgcache) { - _alpm_log(PM_LOG_DEBUG, "warning: pkgcache is NULL for db '%s'\n", db->treename); - } - - return(db->pkgcache); -} - -/* "duplicate" pkg with BASE info (to spare some memory) then add it to pkgcache */ -int _alpm_db_add_pkgincache(pmdb_t *db, pmpkg_t *pkg) -{ - pmpkg_t *newpkg; - - ALPM_LOG_FUNC; - - if(db == NULL || !db->pkgcache_loaded || pkg == NULL) { - return(-1); - } - - newpkg = _alpm_pkg_new(); - if(newpkg == NULL) { - return(-1); - } - newpkg->name = strdup(pkg->name); - newpkg->version = strdup(pkg->version); - if(newpkg->name == NULL || newpkg->version == NULL) { - pm_errno = PM_ERR_MEMORY; - _alpm_pkg_free(newpkg); - return(-1); - } - newpkg->origin = PKG_FROM_CACHE; - newpkg->origin_data.db = db; - newpkg->infolevel = INFRQ_BASE; - - _alpm_log(PM_LOG_DEBUG, "adding entry '%s' in '%s' cache\n", - alpm_pkg_get_name(newpkg), db->treename); - db->pkgcache = alpm_list_add_sorted(db->pkgcache, newpkg, _alpm_pkg_cmp); - - _alpm_db_free_grpcache(db); - - return(0); -} - -int _alpm_db_remove_pkgfromcache(pmdb_t *db, pmpkg_t *pkg) -{ - void *vdata; - pmpkg_t *data; - - ALPM_LOG_FUNC; - - if(db == NULL || !db->pkgcache_loaded || pkg == NULL) { - return(-1); - } - - _alpm_log(PM_LOG_DEBUG, "removing entry '%s' from '%s' cache\n", - alpm_pkg_get_name(pkg), db->treename); - - db->pkgcache = alpm_list_remove(db->pkgcache, pkg, _alpm_pkg_cmp, &vdata); - data = vdata; - if(data == NULL) { - /* package not found */ - _alpm_log(PM_LOG_DEBUG, "cannot remove entry '%s' from '%s' cache: not found\n", - alpm_pkg_get_name(pkg), db->treename); - return(-1); - } - - _alpm_pkg_free(data); - - _alpm_db_free_grpcache(db); - - return(0); -} - -pmpkg_t *_alpm_db_get_pkgfromcache(pmdb_t *db, const char *target) -{ - ALPM_LOG_FUNC; - - if(db == NULL) { - return(NULL); - } - - alpm_list_t *pkgcache = _alpm_db_get_pkgcache(db); - if(!pkgcache) { - _alpm_log(PM_LOG_DEBUG, "warning: failed to get '%s' from NULL pkgcache\n", - target); - return(NULL); - } - - return(_alpm_pkg_find(pkgcache, target)); -} - -/* Returns a new group cache from db. - */ -int _alpm_db_load_grpcache(pmdb_t *db) -{ - alpm_list_t *lp; - - ALPM_LOG_FUNC; - - if(db == NULL) { - return(-1); - } - - _alpm_log(PM_LOG_DEBUG, "loading group cache for repository '%s'\n", - db->treename); - - for(lp = _alpm_db_get_pkgcache(db); lp; lp = lp->next) { - const alpm_list_t *i; - pmpkg_t *pkg = lp->data; - - for(i = alpm_pkg_get_groups(pkg); i; i = i->next) { - const char *grpname = i->data; - alpm_list_t *j; - pmgrp_t *grp = NULL; - int found = 0; - - /* first look through the group cache for a group with this name */ - for(j = db->grpcache; j; j = j->next) { - grp = j->data; - - if(strcmp(grp->name, grpname) == 0 - && !alpm_list_find_ptr(grp->packages, pkg)) { - grp->packages = alpm_list_add(grp->packages, pkg); - found = 1; - break; - } - } - if(found) { - continue; - } - /* we didn't find the group, so create a new one with this name */ - grp = _alpm_grp_new(grpname); - grp->packages = alpm_list_add(grp->packages, pkg); - db->grpcache = alpm_list_add(db->grpcache, grp); - } - } - - db->grpcache_loaded = 1; - return(0); -} - -void _alpm_db_free_grpcache(pmdb_t *db) -{ - alpm_list_t *lg; - - ALPM_LOG_FUNC; - - if(db == NULL || !db->grpcache_loaded) { - return; - } - - _alpm_log(PM_LOG_DEBUG, "freeing group cache for repository '%s'\n", - db->treename); - - for(lg = db->grpcache; lg; lg = lg->next) { - _alpm_grp_free(lg->data); - lg->data = NULL; - } - FREELIST(db->grpcache); - db->grpcache_loaded = 0; -} - -alpm_list_t *_alpm_db_get_grpcache(pmdb_t *db) -{ - ALPM_LOG_FUNC; - - if(db == NULL) { - return(NULL); - } - - if(!db->grpcache_loaded) { - _alpm_db_load_grpcache(db); - } - - return(db->grpcache); -} - -pmgrp_t *_alpm_db_get_grpfromcache(pmdb_t *db, const char *target) -{ - alpm_list_t *i; - - ALPM_LOG_FUNC; - - if(db == NULL || target == NULL || strlen(target) == 0) { - return(NULL); - } - - for(i = _alpm_db_get_grpcache(db); i; i = i->next) { - pmgrp_t *info = i->data; - - if(strcmp(info->name, target) == 0) { - return(info); - } - } - - return(NULL); -} - -/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/cache.h b/lib/libalpm/cache.h deleted file mode 100644 index 6ddcd186..00000000 --- a/lib/libalpm/cache.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * cache.h - * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> - * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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, see <http://www.gnu.org/licenses/>. - */ -#ifndef _ALPM_CACHE_H -#define _ALPM_CACHE_H - -#include "db.h" -#include "alpm_list.h" -#include "group.h" -#include "package.h" - -/* packages */ -int _alpm_db_load_pkgcache(pmdb_t *db); -void _alpm_db_free_pkgcache(pmdb_t *db); -int _alpm_db_add_pkgincache(pmdb_t *db, pmpkg_t *pkg); -int _alpm_db_remove_pkgfromcache(pmdb_t *db, pmpkg_t *pkg); -alpm_list_t *_alpm_db_get_pkgcache(pmdb_t *db); -int _alpm_db_ensure_pkgcache(pmdb_t *db, pmdbinfrq_t infolevel); -pmpkg_t *_alpm_db_get_pkgfromcache(pmdb_t *db, const char *target); -/* groups */ -int _alpm_db_load_grpcache(pmdb_t *db); -void _alpm_db_free_grpcache(pmdb_t *db); -alpm_list_t *_alpm_db_get_grpcache(pmdb_t *db); -pmgrp_t *_alpm_db_get_grpfromcache(pmdb_t *db, const char *target); - -#endif /* _ALPM_CACHE_H */ - -/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/conflict.c b/lib/libalpm/conflict.c index e36844a8..2b0efa5a 100644 --- a/lib/libalpm/conflict.c +++ b/lib/libalpm/conflict.c @@ -38,7 +38,6 @@ #include "trans.h" #include "util.h" #include "log.h" -#include "cache.h" #include "deps.h" pmconflict_t *_alpm_conflict_new(const char *package1, const char *package2, const char *reason) @@ -88,8 +87,8 @@ int _alpm_conflict_isin(pmconflict_t *needle, alpm_list_t *haystack) char *cpkg2 = conflict->package2; char *npkg1 = needle->package1; char *npkg2 = needle->package2; - if((!strcmp(cpkg1, npkg1) && !strcmp(cpkg2, npkg2)) - || (!strcmp(cpkg1, npkg2) && !strcmp(cpkg2, npkg1))) { + if((strcmp(cpkg1, npkg1) == 0 && strcmp(cpkg2, npkg2) == 0) + || (strcmp(cpkg1, npkg2) == 0 && strcmp(cpkg2, npkg1) == 0)) { return(1); } } @@ -110,7 +109,7 @@ static int does_conflict(pmpkg_t *pkg1, const char *conflict, pmpkg_t *pkg2) pmdepend_t *conf = _alpm_splitdep(conflict); int match = 0; - match = alpm_depcmp(pkg2, conf); + match = _alpm_depcmp(pkg2, conf); if(match) { _alpm_log(PM_LOG_DEBUG, "package %s conflicts with %s (by %s)\n", pkg1name, pkg2name, conflict); @@ -321,7 +320,7 @@ static alpm_list_t *chk_filedifference(alpm_list_t *filesA, alpm_list_t *filesB) */ static alpm_list_t *add_fileconflict(alpm_list_t *conflicts, pmfileconflicttype_t type, const char *filestr, - const char* name1, const char* name2) + const char* name1, const char* name2) { pmfileconflict_t *conflict; MALLOC(conflict, sizeof(pmfileconflict_t), RET_ERR(PM_ERR_MEMORY, NULL)); diff --git a/lib/libalpm/db.c b/lib/libalpm/db.c index c8a91a2b..137f8d04 100644 --- a/lib/libalpm/db.c +++ b/lib/libalpm/db.c @@ -29,7 +29,6 @@ #include <errno.h> #include <string.h> #include <sys/stat.h> -#include <dirent.h> #include <regex.h> #include <time.h> @@ -39,8 +38,9 @@ #include "log.h" #include "util.h" #include "handle.h" -#include "cache.h" #include "alpm.h" +#include "package.h" +#include "group.h" /** \addtogroup alpm_databases Database Functions * @brief Functions to query and manipulate the database of libalpm @@ -80,7 +80,7 @@ pmdb_t SYMEXPORT *alpm_db_register_local(void) } /* Helper function for alpm_db_unregister{_all} */ -static void _alpm_db_unregister(pmdb_t *db) +void _alpm_db_unregister(pmdb_t *db) { if(db == NULL) { return; @@ -96,6 +96,7 @@ static void _alpm_db_unregister(pmdb_t *db) int SYMEXPORT alpm_db_unregister_all(void) { alpm_list_t *i; + pmdb_t *db; ALPM_LOG_FUNC; @@ -105,13 +106,16 @@ int SYMEXPORT alpm_db_unregister_all(void) ASSERT(handle->trans == NULL, RET_ERR(PM_ERR_TRANS_NOT_NULL, -1)); /* close local database */ - _alpm_db_unregister(handle->db_local); - handle->db_local = NULL; + db = handle->db_local; + if(db) { + db->ops->unregister(db); + handle->db_local = NULL; + } /* and also sync ones */ for(i = handle->dbs_sync; i; i = i->next) { - pmdb_t *db = i->data; - _alpm_db_unregister(db); + db = i->data; + db->ops->unregister(db); i->data = NULL; } FREELIST(handle->dbs_sync); @@ -154,7 +158,7 @@ int SYMEXPORT alpm_db_unregister(pmdb_t *db) RET_ERR(PM_ERR_DB_NOT_FOUND, -1); } - _alpm_db_unregister(db); + db->ops->unregister(db); return(0); } @@ -321,7 +325,7 @@ alpm_list_t SYMEXPORT *alpm_db_search(pmdb_t *db, const alpm_list_t* needles) return(_alpm_db_search(db, needles)); } -/* Set install reason for a package in db +/** Set install reason for a package in db * @param db pointer to the package database * @param name the name of the package * @param reason the new install reason @@ -341,18 +345,14 @@ int SYMEXPORT alpm_db_set_pkgreason(pmdb_t *db, const char *name, pmpkgreason_t } _alpm_log(PM_LOG_DEBUG, "setting install reason %u for %s/%s\n", reason, db->treename, name); - /* read DESC */ - if(_alpm_db_read(db, pkg, INFRQ_DESC)) { - return(-1); - } - if(pkg->reason == reason) { + if(alpm_pkg_get_reason(pkg) == reason) { /* we are done */ return(0); } /* set reason (in pkgcache) */ pkg->reason = reason; /* write DESC */ - if(_alpm_db_write(db, pkg, INFRQ_DESC)) { + if(_alpm_local_db_write(db, pkg, INFRQ_DESC)) { return(-1); } @@ -361,7 +361,7 @@ int SYMEXPORT alpm_db_set_pkgreason(pmdb_t *db, const char *name, pmpkgreason_t /** @} */ -static pmdb_t *_alpm_db_new(const char *treename, int is_local) +pmdb_t *_alpm_db_new(const char *treename, int is_local) { pmdb_t *db; @@ -409,10 +409,10 @@ const char *_alpm_db_path(pmdb_t *db) CALLOC(db->_path, 1, pathsize, RET_ERR(PM_ERR_MEMORY, NULL)); sprintf(db->_path, "%s%s/", dbpath, db->treename); } else { - pathsize = strlen(dbpath) + 5 + strlen(db->treename) + 2; + pathsize = strlen(dbpath) + 5 + strlen(db->treename) + 4; CALLOC(db->_path, 1, pathsize, RET_ERR(PM_ERR_MEMORY, NULL)); /* all sync DBs now reside in the sync/ subdir of the dbpath */ - sprintf(db->_path, "%ssync/%s/", dbpath, db->treename); + sprintf(db->_path, "%ssync/%s.db", dbpath, db->treename); } _alpm_log(PM_LOG_DEBUG, "database path for tree %s set to %s\n", db->treename, db->_path); @@ -503,52 +503,246 @@ alpm_list_t *_alpm_db_search(pmdb_t *db, const alpm_list_t *needles) return(ret); } -pmdb_t *_alpm_db_register_local(void) +/* Returns a new package cache from db. + * It frees the cache if it already exists. + */ +int _alpm_db_load_pkgcache(pmdb_t *db) { - pmdb_t *db; + ALPM_LOG_FUNC; + + if(db == NULL) { + return(-1); + } + _alpm_db_free_pkgcache(db); + + _alpm_log(PM_LOG_DEBUG, "loading package cache for repository '%s'\n", + db->treename); + if(db->ops->populate(db) == -1) { + _alpm_log(PM_LOG_DEBUG, + "failed to load package cache for repository '%s'\n", db->treename); + return(-1); + } + db->pkgcache_loaded = 1; + return(0); +} + +void _alpm_db_free_pkgcache(pmdb_t *db) +{ ALPM_LOG_FUNC; - if(handle->db_local != NULL) { - _alpm_log(PM_LOG_WARNING, _("attempt to re-register the 'local' DB\n")); - RET_ERR(PM_ERR_DB_NOT_NULL, NULL); + if(db == NULL || !db->pkgcache_loaded) { + return; } - _alpm_log(PM_LOG_DEBUG, "registering local database\n"); + _alpm_log(PM_LOG_DEBUG, "freeing package cache for repository '%s'\n", + db->treename); + + alpm_list_free_inner(db->pkgcache, (alpm_list_fn_free)_alpm_pkg_free); + alpm_list_free(db->pkgcache); + db->pkgcache_loaded = 0; + + _alpm_db_free_grpcache(db); +} + +alpm_list_t *_alpm_db_get_pkgcache(pmdb_t *db) +{ + ALPM_LOG_FUNC; - db = _alpm_db_new("local", 1); if(db == NULL) { - RET_ERR(PM_ERR_DB_CREATE, NULL); + return(NULL); } - handle->db_local = db; - return(db); + if(!db->pkgcache_loaded) { + _alpm_db_load_pkgcache(db); + } + + /* hmmm, still NULL ?*/ + if(!db->pkgcache) { + _alpm_log(PM_LOG_DEBUG, "warning: pkgcache is NULL for db '%s'\n", db->treename); + } + + return(db->pkgcache); } -pmdb_t *_alpm_db_register_sync(const char *treename) +/* "duplicate" pkg then add it to pkgcache */ +int _alpm_db_add_pkgincache(pmdb_t *db, pmpkg_t *pkg) { - pmdb_t *db; - alpm_list_t *i; + pmpkg_t *newpkg; ALPM_LOG_FUNC; - for(i = handle->dbs_sync; i; i = i->next) { - pmdb_t *sdb = i->data; - if(strcmp(treename, sdb->treename) == 0) { - _alpm_log(PM_LOG_DEBUG, "attempt to re-register the '%s' database, using existing\n", sdb->treename); - return sdb; + if(db == NULL || !db->pkgcache_loaded || pkg == NULL) { + return(-1); + } + + newpkg = _alpm_pkg_dup(pkg); + if(newpkg == NULL) { + return(-1); + } + + _alpm_log(PM_LOG_DEBUG, "adding entry '%s' in '%s' cache\n", + alpm_pkg_get_name(newpkg), db->treename); + db->pkgcache = alpm_list_add_sorted(db->pkgcache, newpkg, _alpm_pkg_cmp); + + _alpm_db_free_grpcache(db); + + return(0); +} + +int _alpm_db_remove_pkgfromcache(pmdb_t *db, pmpkg_t *pkg) +{ + void *vdata; + pmpkg_t *data; + + ALPM_LOG_FUNC; + + if(db == NULL || !db->pkgcache_loaded || pkg == NULL) { + return(-1); + } + + _alpm_log(PM_LOG_DEBUG, "removing entry '%s' from '%s' cache\n", + alpm_pkg_get_name(pkg), db->treename); + + db->pkgcache = alpm_list_remove(db->pkgcache, pkg, _alpm_pkg_cmp, &vdata); + data = vdata; + if(data == NULL) { + /* package not found */ + _alpm_log(PM_LOG_DEBUG, "cannot remove entry '%s' from '%s' cache: not found\n", + alpm_pkg_get_name(pkg), db->treename); + return(-1); + } + + _alpm_pkg_free(data); + + _alpm_db_free_grpcache(db); + + return(0); +} + +pmpkg_t *_alpm_db_get_pkgfromcache(pmdb_t *db, const char *target) +{ + ALPM_LOG_FUNC; + + if(db == NULL) { + return(NULL); + } + + alpm_list_t *pkgcache = _alpm_db_get_pkgcache(db); + if(!pkgcache) { + _alpm_log(PM_LOG_DEBUG, "warning: failed to get '%s' from NULL pkgcache\n", + target); + return(NULL); + } + + return(_alpm_pkg_find(pkgcache, target)); +} + +/* Returns a new group cache from db. + */ +int _alpm_db_load_grpcache(pmdb_t *db) +{ + alpm_list_t *lp; + + ALPM_LOG_FUNC; + + if(db == NULL) { + return(-1); + } + + _alpm_log(PM_LOG_DEBUG, "loading group cache for repository '%s'\n", + db->treename); + + for(lp = _alpm_db_get_pkgcache(db); lp; lp = lp->next) { + const alpm_list_t *i; + pmpkg_t *pkg = lp->data; + + for(i = alpm_pkg_get_groups(pkg); i; i = i->next) { + const char *grpname = i->data; + alpm_list_t *j; + pmgrp_t *grp = NULL; + int found = 0; + + /* first look through the group cache for a group with this name */ + for(j = db->grpcache; j; j = j->next) { + grp = j->data; + + if(strcmp(grp->name, grpname) == 0 + && !alpm_list_find_ptr(grp->packages, pkg)) { + grp->packages = alpm_list_add(grp->packages, pkg); + found = 1; + break; + } + } + if(found) { + continue; + } + /* we didn't find the group, so create a new one with this name */ + grp = _alpm_grp_new(grpname); + grp->packages = alpm_list_add(grp->packages, pkg); + db->grpcache = alpm_list_add(db->grpcache, grp); } } - _alpm_log(PM_LOG_DEBUG, "registering sync database '%s'\n", treename); + db->grpcache_loaded = 1; + return(0); +} + +void _alpm_db_free_grpcache(pmdb_t *db) +{ + alpm_list_t *lg; + + ALPM_LOG_FUNC; + + if(db == NULL || !db->grpcache_loaded) { + return; + } + + _alpm_log(PM_LOG_DEBUG, "freeing group cache for repository '%s'\n", + db->treename); + + for(lg = db->grpcache; lg; lg = lg->next) { + _alpm_grp_free(lg->data); + lg->data = NULL; + } + FREELIST(db->grpcache); + db->grpcache_loaded = 0; +} + +alpm_list_t *_alpm_db_get_grpcache(pmdb_t *db) +{ + ALPM_LOG_FUNC; - db = _alpm_db_new(treename, 0); if(db == NULL) { - RET_ERR(PM_ERR_DB_CREATE, NULL); + return(NULL); } - handle->dbs_sync = alpm_list_add(handle->dbs_sync, db); - return(db); + if(!db->grpcache_loaded) { + _alpm_db_load_grpcache(db); + } + + return(db->grpcache); +} + +pmgrp_t *_alpm_db_get_grpfromcache(pmdb_t *db, const char *target) +{ + alpm_list_t *i; + + ALPM_LOG_FUNC; + + if(db == NULL || target == NULL || strlen(target) == 0) { + return(NULL); + } + + for(i = _alpm_db_get_grpcache(db); i; i = i->next) { + pmgrp_t *info = i->data; + + if(strcmp(info->name, target) == 0) { + return(info); + } + } + + return(NULL); } /* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/db.h b/lib/libalpm/db.h index 1851b5c9..10aa1644 100644 --- a/lib/libalpm/db.h +++ b/lib/libalpm/db.h @@ -23,22 +23,28 @@ #define _ALPM_DB_H #include "alpm.h" -#include <limits.h> #include <time.h> +/* libarchive */ +#include <archive.h> +#include <archive_entry.h> + /* Database entries */ typedef enum _pmdbinfrq_t { INFRQ_BASE = 1, INFRQ_DESC = (1 << 1), - INFRQ_DEPENDS = (1 << 2), - INFRQ_FILES = (1 << 3), - INFRQ_SCRIPTLET = (1 << 4), - INFRQ_DELTAS = (1 << 5), - INFRQ_DSIZE = (1 << 6), + INFRQ_FILES = (1 << 2), + INFRQ_SCRIPTLET = (1 << 3), + INFRQ_DSIZE = (1 << 4), /* ALL should be info stored in the package or database */ - INFRQ_ALL = 0x3F + INFRQ_ALL = 0x1F } pmdbinfrq_t; +struct db_operations { + int (*populate) (pmdb_t *); + void (*unregister) (pmdb_t *); +}; + /* Database */ struct __pmdb_t { char *treename; @@ -46,12 +52,16 @@ struct __pmdb_t { char *_path; int pkgcache_loaded; int grpcache_loaded; + /* also indicates whether we are RO or RW */ int is_local; alpm_list_t *pkgcache; alpm_list_t *grpcache; alpm_list_t *servers; + + struct db_operations *ops; }; + /* db.c, database general calls */ void _alpm_db_free(pmdb_t *db); const char *_alpm_db_path(pmdb_t *db); @@ -59,13 +69,29 @@ int _alpm_db_cmp(const void *d1, const void *d2); alpm_list_t *_alpm_db_search(pmdb_t *db, const alpm_list_t *needles); pmdb_t *_alpm_db_register_local(void); pmdb_t *_alpm_db_register_sync(const char *treename); +void _alpm_db_unregister(pmdb_t *db); +pmdb_t *_alpm_db_new(const char *treename, int is_local); + +/* be_*.c, backend specific calls */ +int _alpm_local_db_read(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq); +int _alpm_local_db_prepare(pmdb_t *db, pmpkg_t *info); +int _alpm_local_db_write(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq); +int _alpm_local_db_remove(pmdb_t *db, pmpkg_t *info); -/* be.c, backend specific calls */ -int _alpm_db_populate(pmdb_t *db); -int _alpm_db_read(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq); -int _alpm_db_prepare(pmdb_t *db, pmpkg_t *info); -int _alpm_db_write(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq); -int _alpm_db_remove(pmdb_t *db, pmpkg_t *info); +/* cache bullshit */ +/* packages */ +int _alpm_db_load_pkgcache(pmdb_t *db); +void _alpm_db_free_pkgcache(pmdb_t *db); +int _alpm_db_add_pkgincache(pmdb_t *db, pmpkg_t *pkg); +int _alpm_db_remove_pkgfromcache(pmdb_t *db, pmpkg_t *pkg); +alpm_list_t *_alpm_db_get_pkgcache(pmdb_t *db); +int _alpm_db_ensure_pkgcache(pmdb_t *db, pmdbinfrq_t infolevel); +pmpkg_t *_alpm_db_get_pkgfromcache(pmdb_t *db, const char *target); +/* groups */ +int _alpm_db_load_grpcache(pmdb_t *db); +void _alpm_db_free_grpcache(pmdb_t *db); +alpm_list_t *_alpm_db_get_grpcache(pmdb_t *db); +pmgrp_t *_alpm_db_get_grpfromcache(pmdb_t *db, const char *target); #endif /* _ALPM_DB_H */ diff --git a/lib/libalpm/delta.h b/lib/libalpm/delta.h index 76283380..7d8d5b0f 100644 --- a/lib/libalpm/delta.h +++ b/lib/libalpm/delta.h @@ -44,6 +44,9 @@ void _alpm_delta_free(pmdelta_t *delta); off_t _alpm_shortest_delta_path(alpm_list_t *deltas, const char *to, alpm_list_t **path); +/* max percent of package size to download deltas */ +#define MAX_DELTA_RATIO 0.7 + #endif /* _ALPM_DELTA_H */ /* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/deps.c b/lib/libalpm/deps.c index 26f9b16d..2ed9d30a 100644 --- a/lib/libalpm/deps.c +++ b/lib/libalpm/deps.c @@ -34,7 +34,6 @@ #include "graph.h" #include "package.h" #include "db.h" -#include "cache.h" #include "handle.h" void _alpm_dep_free(pmdepend_t *dep) @@ -196,36 +195,25 @@ pmpkg_t *_alpm_find_dep_satisfier(alpm_list_t *pkgs, pmdepend_t *dep) for(i = pkgs; i; i = alpm_list_next(i)) { pmpkg_t *pkg = i->data; - if(alpm_depcmp(pkg, dep)) { + if(_alpm_depcmp(pkg, dep)) { return(pkg); } } return(NULL); } -/** Checks dependencies and returns missing ones in a list. - * Dependencies can include versions with depmod operators. - * @param db pointer to the local package database - * @param targets an alpm_list_t* of dependencies strings to satisfy - * @return an alpm_list_t* of missing dependencies strings +/** Find a package satisfying a specified dependency. + * The dependency can include versions with depmod operators. + * @param pkgs an alpm_list_t* of pmpkg_t where the satisfier will be searched + * @param depstring package or provision name, versioned or not + * @return a pmpkg_t* satisfying depstring */ -alpm_list_t SYMEXPORT *alpm_deptest(pmdb_t *db, alpm_list_t *targets) +pmpkg_t SYMEXPORT *alpm_find_satisfier(alpm_list_t *pkgs, const char *depstring) { - alpm_list_t *i, *ret = NULL; - - for(i = targets; i; i = alpm_list_next(i)) { - pmdepend_t *dep; - char *target; - - target = alpm_list_getdata(i); - dep = _alpm_splitdep(target); - - if(!_alpm_find_dep_satisfier(_alpm_db_get_pkgcache(db), dep)) { - ret = alpm_list_add(ret, target); - } - _alpm_dep_free(dep); - } - return(ret); + pmdepend_t *dep = _alpm_splitdep(depstring); + pmpkg_t *pkg = _alpm_find_dep_satisfier(pkgs, dep); + _alpm_dep_free(dep); + return(pkg); } /** Checks dependencies and returns missing ones in a list. @@ -248,8 +236,8 @@ alpm_list_t SYMEXPORT *alpm_checkdeps(alpm_list_t *pkglist, int reversedeps, targets = alpm_list_join(alpm_list_copy(remove), alpm_list_copy(upgrade)); for(i = pkglist; i; i = i->next) { - void *pkg = i->data; - if(alpm_list_find(targets, pkg, _alpm_pkg_cmp)) { + pmpkg_t *pkg = i->data; + if(_alpm_pkg_find(targets, pkg->name)) { modified = alpm_list_add(modified, pkg); } else { dblist = alpm_list_add(dblist, pkg); @@ -331,35 +319,44 @@ static int dep_vercmp(const char *version1, pmdepmod_t mod, return(equal); } -int SYMEXPORT alpm_depcmp(pmpkg_t *pkg, pmdepend_t *dep) +int _alpm_depcmp(pmpkg_t *pkg, pmdepend_t *dep) { alpm_list_t *i; - - ALPM_LOG_FUNC; - - const char *pkgname = alpm_pkg_get_name(pkg); - const char *pkgversion = alpm_pkg_get_version(pkg); + const char *pkgname = pkg->name; + const char *pkgversion = pkg->version; int satisfy = 0; /* check (pkg->name, pkg->version) */ - satisfy = (strcmp(pkgname, dep->name) == 0 - && dep_vercmp(pkgversion, dep->mod, dep->version)); + if(pkg->name_hash && dep->name_hash + && pkg->name_hash != dep->name_hash) { + /* skip more expensive checks */ + } else { + satisfy = (strcmp(pkgname, dep->name) == 0 + && dep_vercmp(pkgversion, dep->mod, dep->version)); + if(satisfy) { + return(satisfy); + } + } /* check provisions, format : "name=version" */ for(i = alpm_pkg_get_provides(pkg); i && !satisfy; i = i->next) { - char *provname = strdup(i->data); - char *provver = strchr(provname, '='); + const char *provision = i->data; + const char *provver = strchr(provision, '='); if(provver == NULL) { /* no provision version */ satisfy = (dep->mod == PM_DEP_MOD_ANY - && strcmp(provname, dep->name) == 0); + && strcmp(provision, dep->name) == 0); } else { - *provver = '\0'; + /* This is a bit tricker than the old code for performance reasons. To + * prevent the need to copy and duplicate strings, strncmp only the name + * portion if they are the same length, since there is a version and + * operator in play here. */ + size_t namelen = provver - provision; provver += 1; - satisfy = (strcmp(provname, dep->name) == 0 + satisfy = (strlen(dep->name) == namelen + && strncmp(provision, dep->name, namelen) == 0 && dep_vercmp(provver, dep->mod, dep->version)); } - free(provname); } return(satisfy); @@ -388,7 +385,8 @@ pmdepend_t *_alpm_splitdep(const char *depstring) depend->mod = PM_DEP_MOD_LE; *ptr = '\0'; ptr += 2; - } else if((ptr = strstr(newstr, "="))) { /* Note: we must do =,<,> checks after <=, >= checks */ + } else if((ptr = strstr(newstr, "="))) { + /* Note: we must do =,<,> checks after <=, >= checks */ depend->mod = PM_DEP_MOD_EQ; *ptr = '\0'; ptr += 1; @@ -404,6 +402,7 @@ pmdepend_t *_alpm_splitdep(const char *depstring) /* no version specified - copy the name and return it */ depend->mod = PM_DEP_MOD_ANY; STRDUP(depend->name, newstr, RET_ERR(PM_ERR_MEMORY, NULL)); + depend->name_hash = _alpm_hash_sdbm(depend->name); depend->version = NULL; free(newstr); return(depend); @@ -412,6 +411,7 @@ pmdepend_t *_alpm_splitdep(const char *depstring) /* if we get here, we have a version comparator, copy the right parts * to the right places */ STRDUP(depend->name, newstr, RET_ERR(PM_ERR_MEMORY, NULL)); + depend->name_hash = _alpm_hash_sdbm(depend->name); STRDUP(depend->version, ptr, RET_ERR(PM_ERR_MEMORY, NULL)); free(newstr); @@ -424,6 +424,7 @@ pmdepend_t *_alpm_dep_dup(const pmdepend_t *dep) CALLOC(newdep, 1, sizeof(pmdepend_t), RET_ERR(PM_ERR_MEMORY, NULL)); STRDUP(newdep->name, dep->name, RET_ERR(PM_ERR_MEMORY, NULL)); + newdep->name_hash = dep->name_hash; STRDUP(newdep->version, dep->version, RET_ERR(PM_ERR_MEMORY, NULL)); newdep->mod = dep->mod; @@ -525,7 +526,7 @@ pmpkg_t *_alpm_resolvedep(pmdepend_t *dep, alpm_list_t *dbs, /* 1. literals */ for(i = dbs; i; i = i->next) { pmpkg_t *pkg = _alpm_db_get_pkgfromcache(i->data, dep->name); - if(pkg && alpm_depcmp(pkg, dep) && !_alpm_pkg_find(excluding, pkg->name)) { + if(pkg && _alpm_depcmp(pkg, dep) && !_alpm_pkg_find(excluding, pkg->name)) { if(_alpm_pkg_should_ignore(pkg)) { int install = 0; if (prompt) { @@ -546,7 +547,7 @@ pmpkg_t *_alpm_resolvedep(pmdepend_t *dep, alpm_list_t *dbs, for(i = dbs; i; i = i->next) { for(j = _alpm_db_get_pkgcache(i->data); j; j = j->next) { pmpkg_t *pkg = j->data; - if(alpm_depcmp(pkg, dep) && strcmp(pkg->name, dep->name) && + if(_alpm_depcmp(pkg, dep) && strcmp(pkg->name, dep->name) != 0 && !_alpm_pkg_find(excluding, pkg->name)) { if(_alpm_pkg_should_ignore(pkg)) { int install = 0; @@ -672,7 +673,7 @@ int _alpm_dep_edge(pmpkg_t *pkg1, pmpkg_t *pkg2) { alpm_list_t *i; for(i = alpm_pkg_get_depends(pkg1); i; i = i->next) { - if(alpm_depcmp(pkg2, i->data)) { + if(_alpm_depcmp(pkg2, i->data)) { return(1); } } diff --git a/lib/libalpm/deps.h b/lib/libalpm/deps.h index ffc3aeeb..34e09f62 100644 --- a/lib/libalpm/deps.h +++ b/lib/libalpm/deps.h @@ -29,9 +29,10 @@ /* Dependency */ struct __pmdepend_t { - pmdepmod_t mod; char *name; char *version; + unsigned long name_hash; + pmdepmod_t mod; }; /* Missing dependency */ @@ -55,6 +56,7 @@ int _alpm_resolvedeps(alpm_list_t *localpkgs, alpm_list_t *dbs_sync, pmpkg_t *pk int _alpm_dep_edge(pmpkg_t *pkg1, pmpkg_t *pkg2); pmdepend_t *_alpm_splitdep(const char *depstring); pmpkg_t *_alpm_find_dep_satisfier(alpm_list_t *pkgs, pmdepend_t *dep); +int _alpm_depcmp(pmpkg_t *pkg, pmdepend_t *dep); #endif /* _ALPM_DEPS_H */ diff --git a/lib/libalpm/diskspace.c b/lib/libalpm/diskspace.c new file mode 100644 index 00000000..4ac0e496 --- /dev/null +++ b/lib/libalpm/diskspace.c @@ -0,0 +1,325 @@ +/* + * diskspace.c + * + * Copyright (c) 2010 Pacman Development Team <pacman-dev@archlinux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#if defined(HAVE_MNTENT_H) +#include <mntent.h> +#endif +#if defined(HAVE_SYS_STATVFS_H) +#include <sys/statvfs.h> +#endif +#if defined(HAVE_SYS_PARAM_H) +#include <sys/param.h> +#endif +#if defined(HAVE_SYS_MOUNT_H) +#include <sys/mount.h> +#endif +#if defined(HAVE_SYS_UCRED_H) +#include <sys/ucred.h> +#endif +#if defined(HAVE_SYS_TYPES_H) +#include <sys/types.h> +#endif + +#include <math.h> + +/* libarchive */ +#include <archive.h> +#include <archive_entry.h> + +/* libalpm */ +#include "diskspace.h" +#include "alpm_list.h" +#include "util.h" +#include "log.h" +#include "trans.h" +#include "handle.h" + +static int mount_point_cmp(const void *p1, const void *p2) +{ + const alpm_mountpoint_t *mp1 = p1; + const alpm_mountpoint_t *mp2 = p2; + return(strcmp(mp1->mount_dir, mp2->mount_dir)); +} + +static alpm_list_t *mount_point_list(void) +{ + alpm_list_t *mount_points = NULL; + alpm_mountpoint_t *mp; + +#if defined HAVE_GETMNTENT + struct mntent *mnt; + FILE *fp; + FSSTATSTYPE fsp; + + fp = setmntent(MOUNTED, "r"); + + if (fp == NULL) { + return(NULL); + } + + while((mnt = getmntent(fp))) { + if(statvfs(mnt->mnt_dir, &fsp) != 0) { + _alpm_log(PM_LOG_WARNING, + _("could not get filesystem information for %s\n"), mnt->mnt_dir); + continue; + } + + MALLOC(mp, sizeof(alpm_mountpoint_t), RET_ERR(PM_ERR_MEMORY, NULL)); + mp->mount_dir = strdup(mnt->mnt_dir); + memcpy(&(mp->fsp), &fsp, sizeof(FSSTATSTYPE)); + + mp->blocks_needed = 0; + mp->max_blocks_needed = 0; + mp->used = 0; + + mount_points = alpm_list_add(mount_points, mp); + } + + endmntent(fp); +#elif defined HAVE_GETMNTINFO + int entries; + FSSTATSTYPE *fsp; + + entries = getmntinfo(&fsp, MNT_NOWAIT); + + if (entries < 0) { + return NULL; + } + + for(; entries-- > 0; fsp++) { + MALLOC(mp, sizeof(alpm_mountpoint_t), RET_ERR(PM_ERR_MEMORY, NULL)); + mp->mount_dir = strdup(fsp->f_mntonname); + memcpy(&(mp->fsp), fsp, sizeof(FSSTATSTYPE)); + + mp->blocks_needed = 0; + mp->max_blocks_needed = 0; + + mount_points = alpm_list_add(mount_points, mp); + } +#endif + + mount_points = alpm_list_msort(mount_points, alpm_list_count(mount_points), + mount_point_cmp); + return(mount_points); +} + +static alpm_list_t *match_mount_point(const alpm_list_t *mount_points, + const char *file) +{ + char real_path[PATH_MAX]; + snprintf(real_path, PATH_MAX, "%s%s", handle->root, file); + + alpm_list_t *mp = alpm_list_last(mount_points); + do { + alpm_mountpoint_t *data = mp->data; + + if(strncmp(data->mount_dir, real_path, strlen(data->mount_dir)) == 0) { + return(mp); + } + + mp = mp->prev; + } while (mp != alpm_list_last(mount_points)); + + /* should not get here... */ + return(NULL); +} + +static int calculate_removed_size(const alpm_list_t *mount_points, + pmpkg_t *pkg) +{ + alpm_list_t *file; + + alpm_list_t *files = alpm_pkg_get_files(pkg); + for(file = files; file; file = file->next) { + alpm_list_t *mp; + alpm_mountpoint_t *data; + struct stat st; + char path[PATH_MAX]; + const char *filename = file->data; + + /* skip directories to be consistent with libarchive that reports them + * to be zero size and to prevent multiple counting across packages */ + if(*(filename + strlen(filename) - 1) == '/') { + continue; + } + + mp = match_mount_point(mount_points, filename); + if(mp == NULL) { + _alpm_log(PM_LOG_WARNING, + _("could not determine mount point for file %s"), filename); + continue; + } + + snprintf(path, PATH_MAX, "%s%s", handle->root, filename); + _alpm_lstat(path, &st); + + /* skip symlinks to be consistent with libarchive that reports them to + * be zero size */ + if(S_ISLNK(st.st_mode)) { + continue; + } + + data = mp->data; + data->blocks_needed -= ceil((double)(st.st_size) / + (double)(data->fsp.f_bsize)); + data->used = 1; + } + + return(0); +} + +static int calculate_installed_size(const alpm_list_t *mount_points, + pmpkg_t *pkg) +{ + int ret=0; + struct archive *archive; + struct archive_entry *entry; + const char *file; + + if ((archive = archive_read_new()) == NULL) { + pm_errno = PM_ERR_LIBARCHIVE; + ret = -1; + goto cleanup; + } + + archive_read_support_compression_all(archive); + archive_read_support_format_all(archive); + + if(archive_read_open_filename(archive, pkg->origin_data.file, + ARCHIVE_DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK) { + pm_errno = PM_ERR_PKG_OPEN; + ret = -1; + goto cleanup; + } + + while(archive_read_next_header(archive, &entry) == ARCHIVE_OK) { + alpm_list_t *mp; + alpm_mountpoint_t *data; + + file = archive_entry_pathname(entry); + + /* approximate space requirements for db entries */ + if(file[0] == '.') { + file = alpm_option_get_dbpath(); + } + + mp = match_mount_point(mount_points, file); + if(mp == NULL) { + _alpm_log(PM_LOG_WARNING, + _("could not determine mount point for file %s"), file); + continue; + } + + data = mp->data; + data->blocks_needed += ceil((double)(archive_entry_size(entry)) / + (double)(data->fsp.f_bsize)); + data->used = 1; + } + + archive_read_finish(archive); + +cleanup: + return(ret); +} + +int _alpm_check_diskspace(pmtrans_t *trans, pmdb_t *db_local) +{ + alpm_list_t *mount_points, *i; + int replaces = 0, abort = 0; + alpm_list_t *targ; + pmpkg_t *pkg; + int numtargs = alpm_list_count(trans->add); + int current = 0; + + mount_points = mount_point_list(); + if(mount_points == NULL) { + _alpm_log(PM_LOG_ERROR, _("could not determine filesystem mount points")); + return(-1); + } + + replaces = alpm_list_count(trans->remove); + if(replaces) { + numtargs += replaces; + for(targ = trans->remove; targ; targ = targ->next, current++) { + double percent = (double)current / numtargs; + PROGRESS(trans, PM_TRANS_PROGRESS_DISKSPACE_START, "", (percent * 100), + numtargs, current); + + pkg = targ->data; + calculate_removed_size(mount_points, pkg); + } + } + + for(targ = trans->add; targ; targ = targ->next, current++) { + double percent = (double)current / numtargs; + PROGRESS(trans, PM_TRANS_PROGRESS_DISKSPACE_START, "", (percent * 100), + numtargs, current); + + pkg = targ->data; + /* is this package already installed? */ + if(_alpm_db_get_pkgfromcache(db_local, pkg->name)) { + calculate_removed_size(mount_points, pkg); + } + calculate_installed_size(mount_points, pkg); + + for(i = mount_points; i; i = alpm_list_next(i)) { + alpm_mountpoint_t *data = i->data; + if(data->blocks_needed > data->max_blocks_needed) { + data->max_blocks_needed = data->blocks_needed; + } + } + } + + PROGRESS(trans, PM_TRANS_PROGRESS_DISKSPACE_START, "", 100, + numtargs, current); + + for(i = mount_points; i; i = alpm_list_next(i)) { + alpm_mountpoint_t *data = i->data; + if(data->used == 1) { + /* cushion is roughly min(5% capacity, 20MiB) */ + long fivepc = (data->fsp.f_blocks / 20) + 1; + long twentymb = (20 * 1024 * 1024 / data->fsp.f_bsize) + 1; + long cushion = fivepc < twentymb ? fivepc : twentymb; + + _alpm_log(PM_LOG_DEBUG, "partition %s, needed %ld, cushion %ld, free %ld\n", + data->mount_dir, data->max_blocks_needed, cushion, + (unsigned long)data->fsp.f_bfree); + if(data->max_blocks_needed + cushion >= 0 && + (unsigned long)(data->max_blocks_needed + cushion) > data->fsp.f_bfree) { + abort = 1; + } + } + } + + for(i = mount_points; i; i = alpm_list_next(i)) { + alpm_mountpoint_t *data = i->data; + FREE(data->mount_dir); + } + FREELIST(mount_points); + + if(abort) { + RET_ERR(PM_ERR_DISK_SPACE, -1); + } + + return(0); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/diskspace.h b/lib/libalpm/diskspace.h new file mode 100644 index 00000000..e73497ef --- /dev/null +++ b/lib/libalpm/diskspace.h @@ -0,0 +1,46 @@ +/* + * diskspace.h + * + * Copyright (c) 2010 Pacman Development Team <pacman-dev@archlinux.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _ALPM_DISKSPACE_H +#define _ALPM_DISKSPACE_H + +#if defined(HAVE_SYS_MOUNT_H) +#include <sys/mount.h> +#endif +#if defined(HAVE_SYS_STATVFS_H) +#include <sys/statvfs.h> +#endif + +#include "alpm.h" + +typedef struct __alpm_mountpoint_t { + /* mount point information */ + char *mount_dir; + /* storage for additional disk usage calculations */ + long blocks_needed; + long max_blocks_needed; + int used; + FSSTATSTYPE fsp; +} alpm_mountpoint_t; + +int _alpm_check_diskspace(pmtrans_t *trans, pmdb_t *db_local); + +#endif /* _ALPM_DISKSPACE_H */ + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/dload.c b/lib/libalpm/dload.c index c11148d1..09f716f5 100644 --- a/lib/libalpm/dload.c +++ b/lib/libalpm/dload.c @@ -29,16 +29,13 @@ #include <sys/types.h> #include <sys/stat.h> #include <signal.h> -#include <limits.h> -/* the following two are needed on BSD for libfetch */ -#if defined(HAVE_SYS_SYSLIMITS_H) -#include <sys/syslimits.h> /* PATH_MAX */ -#endif +/* the following two are needed for FreeBSD's libfetch */ +#include <limits.h> /* PATH_MAX */ #if defined(HAVE_SYS_PARAM_H) #include <sys/param.h> /* MAXHOSTNAMELEN */ #endif -#if defined(INTERNAL_DOWNLOAD) +#ifdef HAVE_LIBFETCH #include <fetch.h> #endif @@ -58,7 +55,7 @@ static char *get_filename(const char *url) { return(filename); } -#if defined(INTERNAL_DOWNLOAD) +#ifdef HAVE_LIBFETCH static char *get_destfile(const char *path, const char *filename) { char *destfile; /* len = localpath len + filename len + null */ @@ -89,7 +86,7 @@ static const char *gethost(struct url *fileurl) } int dload_interrupted; -static RETSIGTYPE inthandler(int signum) +static void inthandler(int signum) { dload_interrupted = 1; } @@ -251,7 +248,7 @@ static int download_internal(const char *url, const char *localpath, check_stop(); size_t nwritten = 0; nwritten = fwrite(buffer, 1, nread, localf); - if((nwritten != nread) || ferror(localf)) { + if((nwritten != (size_t)nread) || ferror(localf)) { pm_errno = PM_ERR_RETRIEVE; _alpm_log(PM_LOG_ERROR, _("error writing to file '%s': %s\n"), tempfile, strerror(errno)); @@ -338,7 +335,7 @@ cleanup: static int download(const char *url, const char *localpath, int force) { if(handle->fetchcb == NULL) { -#if defined(INTERNAL_DOWNLOAD) +#ifdef HAVE_LIBFETCH return(download_internal(url, localpath, force)); #else RET_ERR(PM_ERR_EXTERNAL_DOWNLOAD, -1); diff --git a/lib/libalpm/error.c b/lib/libalpm/error.c index 8d8d0458..3d056265 100644 --- a/lib/libalpm/error.c +++ b/lib/libalpm/error.c @@ -22,16 +22,13 @@ /* TODO: needed for the libfetch stuff, unfortunately- we should kill it */ #include <stdio.h> -#include <limits.h> -/* the following two are needed on BSD for libfetch */ -#if defined(HAVE_SYS_SYSLIMITS_H) -#include <sys/syslimits.h> /* PATH_MAX */ -#endif +/* the following two are needed for FreeBSD's libfetch */ +#include <limits.h> /* PATH_MAX */ #if defined(HAVE_SYS_PARAM_H) #include <sys/param.h> /* MAXHOSTNAMELEN */ #endif -#if defined(INTERNAL_DOWNLOAD) +#ifdef HAVE_LIBFETCH #include <fetch.h> /* fetchLastErrString */ #endif @@ -60,6 +57,8 @@ const char SYMEXPORT *alpm_strerror(int err) return _("could not find or read directory"); case PM_ERR_WRONG_ARGS: return _("wrong or NULL argument passed"); + case PM_ERR_DISK_SPACE: + return _("not enough disk space"); /* Interface */ case PM_ERR_HANDLE_NULL: return _("library not initialized"); @@ -145,7 +144,7 @@ const char SYMEXPORT *alpm_strerror(int err) * error string instead. */ return _("libarchive error"); case PM_ERR_LIBFETCH: -#if defined(INTERNAL_DOWNLOAD) +#ifdef HAVE_LIBFETCH return fetchLastErrString; #else /* obviously shouldn't get here... */ diff --git a/lib/libalpm/handle.c b/lib/libalpm/handle.c index aa34cf45..ffa5dd67 100644 --- a/lib/libalpm/handle.c +++ b/lib/libalpm/handle.c @@ -232,6 +232,15 @@ int SYMEXPORT alpm_option_get_usedelta() return handle->usedelta; } +int SYMEXPORT alpm_option_get_checkspace() +{ + if (handle == NULL) { + pm_errno = PM_ERR_HANDLE_NULL; + return -1; + } + return handle->checkspace; +} + pmdb_t SYMEXPORT *alpm_option_get_localdb() { if (handle == NULL) { @@ -550,4 +559,9 @@ void SYMEXPORT alpm_option_set_usedelta(int usedelta) handle->usedelta = usedelta; } +void SYMEXPORT alpm_option_set_checkspace(int checkspace) +{ + handle->checkspace = checkspace; +} + /* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/handle.h b/lib/libalpm/handle.h index 1834994e..b471ea6c 100644 --- a/lib/libalpm/handle.h +++ b/lib/libalpm/handle.h @@ -60,12 +60,13 @@ typedef struct _pmhandle_t { int usesyslog; /* Use syslog instead of logfile? */ /* TODO move to frontend */ char *arch; /* Architecture of packages we should allow */ int usedelta; /* Download deltas if possible */ + int checkspace; /* Check disk space before installing */ } pmhandle_t; /* global handle variable */ extern pmhandle_t *handle; -pmhandle_t *_alpm_handle_new(); +pmhandle_t *_alpm_handle_new(void); void _alpm_handle_free(pmhandle_t *handle); #endif /* _ALPM_HANDLE_H */ diff --git a/lib/libalpm/md5.c b/lib/libalpm/md5.c index 7d2716f2..90635046 100644 --- a/lib/libalpm/md5.c +++ b/lib/libalpm/md5.c @@ -36,6 +36,8 @@ * int md5_file( char *path, unsigned char *output ) * to * int md5_file( const char *path, unsigned char *output ) + * * use a dynamically-allocated buffer in md5_file, and increase the size + * for performance reasons * * various static/inline changes * * NOTE: XySSL has been renamed to PolarSSL, which is available at @@ -44,6 +46,7 @@ #include <string.h> #include <stdio.h> +#include <stdlib.h> #include "md5.h" @@ -309,11 +312,16 @@ int md5_file( const char *path, unsigned char output[16] ) FILE *f; size_t n; md5_context ctx; - unsigned char buf[1024]; + unsigned char *buf; - if( ( f = fopen( path, "rb" ) ) == NULL ) + if( ( buf = calloc(8192, sizeof(unsigned char)) ) == NULL ) return( 1 ); + if( ( f = fopen( path, "rb" ) ) == NULL ) { + free( buf ); + return( 1 ); + } + md5_starts( &ctx ); while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) @@ -322,6 +330,7 @@ int md5_file( const char *path, unsigned char output[16] ) md5_finish( &ctx, output ); memset( &ctx, 0, sizeof( md5_context ) ); + free( buf ); if( ferror( f ) != 0 ) { diff --git a/lib/libalpm/package.c b/lib/libalpm/package.c index becbc60f..332a082f 100644 --- a/lib/libalpm/package.c +++ b/lib/libalpm/package.c @@ -25,24 +25,18 @@ #include <stdio.h> #include <stdlib.h> -#include <limits.h> #include <string.h> #include <ctype.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> -/* libarchive */ -#include <archive.h> -#include <archive_entry.h> - /* libalpm */ #include "package.h" #include "alpm_list.h" #include "log.h" #include "util.h" #include "db.h" -#include "cache.h" #include "delta.h" #include "handle.h" #include "deps.h" @@ -63,7 +57,7 @@ int SYMEXPORT alpm_pkg_free(pmpkg_t *pkg) ASSERT(pkg != NULL, RET_ERR(PM_ERR_WRONG_ARGS, -1)); /* Only free packages loaded in user space */ - if(pkg->origin != PKG_FROM_CACHE) { + if(pkg->origin == PKG_FROM_FILE) { _alpm_pkg_free(pkg); } @@ -83,8 +77,7 @@ int SYMEXPORT alpm_pkg_checkmd5sum(pmpkg_t *pkg) ASSERT(pkg != NULL, RET_ERR(PM_ERR_WRONG_ARGS, -1)); /* We only inspect packages from sync repositories */ - ASSERT(pkg->origin == PKG_FROM_CACHE, RET_ERR(PM_ERR_PKG_INVALID, -1)); - ASSERT(pkg->origin_data.db != handle->db_local, RET_ERR(PM_ERR_PKG_INVALID, -1)); + ASSERT(pkg->origin == PKG_FROM_SYNCDB, RET_ERR(PM_ERR_PKG_INVALID, -1)); fpath = _alpm_filecache_find(alpm_pkg_get_filename(pkg)); @@ -100,334 +93,196 @@ int SYMEXPORT alpm_pkg_checkmd5sum(pmpkg_t *pkg) return(retval); } +/* Default package accessor functions. These will get overridden by any + * backend logic that needs lazy access, such as the local database through + * a lazy-load cache. However, the defaults will work just fine for fully- + * populated package structures. */ +static const char *_pkg_get_filename(pmpkg_t *pkg) { return pkg->filename; } +static const char *_pkg_get_name(pmpkg_t *pkg) { return pkg->name; } +static const char *_pkg_get_version(pmpkg_t *pkg) { return pkg->version; } +static const char *_pkg_get_desc(pmpkg_t *pkg) { return pkg->desc; } +static const char *_pkg_get_url(pmpkg_t *pkg) { return pkg->url; } +static time_t _pkg_get_builddate(pmpkg_t *pkg) { return pkg->builddate; } +static time_t _pkg_get_installdate(pmpkg_t *pkg) { return pkg->installdate; } +static const char *_pkg_get_packager(pmpkg_t *pkg) { return pkg->packager; } +static const char *_pkg_get_md5sum(pmpkg_t *pkg) { return pkg->md5sum; } +static const char *_pkg_get_arch(pmpkg_t *pkg) { return pkg->arch; } +static off_t _pkg_get_size(pmpkg_t *pkg) { return pkg->size; } +static off_t _pkg_get_isize(pmpkg_t *pkg) { return pkg->isize; } +static pmpkgreason_t _pkg_get_reason(pmpkg_t *pkg) { return pkg->reason; } +static int _pkg_get_epoch(pmpkg_t *pkg) { return pkg->epoch; } +static int _pkg_has_scriptlet(pmpkg_t *pkg) { return pkg->scriptlet; } + +static alpm_list_t *_pkg_get_licenses(pmpkg_t *pkg) { return pkg->licenses; } +static alpm_list_t *_pkg_get_groups(pmpkg_t *pkg) { return pkg->groups; } +static alpm_list_t *_pkg_get_depends(pmpkg_t *pkg) { return pkg->depends; } +static alpm_list_t *_pkg_get_optdepends(pmpkg_t *pkg) { return pkg->optdepends; } +static alpm_list_t *_pkg_get_conflicts(pmpkg_t *pkg) { return pkg->conflicts; } +static alpm_list_t *_pkg_get_provides(pmpkg_t *pkg) { return pkg->provides; } +static alpm_list_t *_pkg_get_replaces(pmpkg_t *pkg) { return pkg->replaces; } +static alpm_list_t *_pkg_get_deltas(pmpkg_t *pkg) { return pkg->deltas; } +static alpm_list_t *_pkg_get_files(pmpkg_t *pkg) { return pkg->files; } +static alpm_list_t *_pkg_get_backup(pmpkg_t *pkg) { return pkg->backup; } + +/** The standard package operations struct. Get fields directly from the + * struct itself with no abstraction layer or any type of lazy loading. + */ +struct pkg_operations default_pkg_ops = { + .get_filename = _pkg_get_filename, + .get_name = _pkg_get_name, + .get_version = _pkg_get_version, + .get_desc = _pkg_get_desc, + .get_url = _pkg_get_url, + .get_builddate = _pkg_get_builddate, + .get_installdate = _pkg_get_installdate, + .get_packager = _pkg_get_packager, + .get_md5sum = _pkg_get_md5sum, + .get_arch = _pkg_get_arch, + .get_size = _pkg_get_size, + .get_isize = _pkg_get_isize, + .get_reason = _pkg_get_reason, + .get_epoch = _pkg_get_epoch, + .has_scriptlet = _pkg_has_scriptlet, + .get_licenses = _pkg_get_licenses, + .get_groups = _pkg_get_groups, + .get_depends = _pkg_get_depends, + .get_optdepends = _pkg_get_optdepends, + .get_conflicts = _pkg_get_conflicts, + .get_provides = _pkg_get_provides, + .get_replaces = _pkg_get_replaces, + .get_deltas = _pkg_get_deltas, + .get_files = _pkg_get_files, + .get_backup = _pkg_get_backup, +}; + +/* Public functions for getting package information. These functions + * delegate the hard work to the function callbacks attached to each + * package, which depend on where the package was loaded from. */ const char SYMEXPORT *alpm_pkg_get_filename(pmpkg_t *pkg) { - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, return(NULL)); - ASSERT(pkg != NULL, return(NULL)); - - if(pkg->origin == PKG_FROM_CACHE && !(pkg->infolevel & INFRQ_DESC)) { - _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_DESC); - } - - return pkg->filename; + return pkg->ops->get_filename(pkg); } const char SYMEXPORT *alpm_pkg_get_name(pmpkg_t *pkg) { - ASSERT(pkg != NULL, return(NULL)); - return pkg->name; + return pkg->ops->get_name(pkg); } const char SYMEXPORT *alpm_pkg_get_version(pmpkg_t *pkg) { - ASSERT(pkg != NULL, return(NULL)); - return pkg->version; + return pkg->ops->get_version(pkg); } const char SYMEXPORT *alpm_pkg_get_desc(pmpkg_t *pkg) { - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, return(NULL)); - ASSERT(pkg != NULL, return(NULL)); - - if(pkg->origin == PKG_FROM_CACHE && !(pkg->infolevel & INFRQ_DESC)) { - _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_DESC); - } - return pkg->desc; + return pkg->ops->get_desc(pkg); } const char SYMEXPORT *alpm_pkg_get_url(pmpkg_t *pkg) { - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, return(NULL)); - ASSERT(pkg != NULL, return(NULL)); - - if(pkg->origin == PKG_FROM_CACHE && !(pkg->infolevel & INFRQ_DESC)) { - _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_DESC); - } - return pkg->url; + return pkg->ops->get_url(pkg); } time_t SYMEXPORT alpm_pkg_get_builddate(pmpkg_t *pkg) { - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, return(0)); - ASSERT(pkg != NULL, return(0)); - - if(pkg->origin == PKG_FROM_CACHE && !(pkg->infolevel & INFRQ_DESC)) { - _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_DESC); - } - return pkg->builddate; + return pkg->ops->get_builddate(pkg); } time_t SYMEXPORT alpm_pkg_get_installdate(pmpkg_t *pkg) { - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, return(0)); - ASSERT(pkg != NULL, return(0)); - - if(pkg->origin == PKG_FROM_CACHE && !(pkg->infolevel & INFRQ_DESC)) { - _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_DESC); - } - return pkg->installdate; + return pkg->ops->get_installdate(pkg); } const char SYMEXPORT *alpm_pkg_get_packager(pmpkg_t *pkg) { - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, return(NULL)); - ASSERT(pkg != NULL, return(NULL)); - - if(pkg->origin == PKG_FROM_CACHE && !(pkg->infolevel & INFRQ_DESC)) { - _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_DESC); - } - return pkg->packager; + return pkg->ops->get_packager(pkg); } const char SYMEXPORT *alpm_pkg_get_md5sum(pmpkg_t *pkg) { - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, return(NULL)); - ASSERT(pkg != NULL, return(NULL)); - - if(pkg->origin == PKG_FROM_CACHE && !(pkg->infolevel & INFRQ_DESC)) { - _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_DESC); - } - return pkg->md5sum; + return pkg->ops->get_md5sum(pkg); } const char SYMEXPORT *alpm_pkg_get_arch(pmpkg_t *pkg) { - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, return(NULL)); - ASSERT(pkg != NULL, return(NULL)); - - if(pkg->origin == PKG_FROM_CACHE && !(pkg->infolevel & INFRQ_DESC)) { - _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_DESC); - } - return pkg->arch; + return pkg->ops->get_arch(pkg); } off_t SYMEXPORT alpm_pkg_get_size(pmpkg_t *pkg) { - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, return(-1)); - ASSERT(pkg != NULL, return(-1)); - - if(pkg->origin == PKG_FROM_CACHE && !(pkg->infolevel & INFRQ_DESC)) { - _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_DESC); - } - return pkg->size; + return pkg->ops->get_size(pkg); } off_t SYMEXPORT alpm_pkg_get_isize(pmpkg_t *pkg) { - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, return(-1)); - ASSERT(pkg != NULL, return(-1)); - - if(pkg->origin == PKG_FROM_CACHE && !(pkg->infolevel & INFRQ_DESC)) { - _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_DESC); - } - return pkg->isize; + return pkg->ops->get_isize(pkg); } pmpkgreason_t SYMEXPORT alpm_pkg_get_reason(pmpkg_t *pkg) { - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, return(-1)); - ASSERT(pkg != NULL, return(-1)); + return pkg->ops->get_reason(pkg); +} - if(pkg->origin == PKG_FROM_CACHE && !(pkg->infolevel & INFRQ_DESC)) { - _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_DESC); - } - return pkg->reason; +int SYMEXPORT alpm_pkg_get_epoch(pmpkg_t *pkg) +{ + return pkg->ops->get_epoch(pkg); } alpm_list_t SYMEXPORT *alpm_pkg_get_licenses(pmpkg_t *pkg) { - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, return(NULL)); - ASSERT(pkg != NULL, return(NULL)); - - if(pkg->origin == PKG_FROM_CACHE && !(pkg->infolevel & INFRQ_DESC)) { - _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_DESC); - } - return pkg->licenses; + return pkg->ops->get_licenses(pkg); } alpm_list_t SYMEXPORT *alpm_pkg_get_groups(pmpkg_t *pkg) { - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, return(NULL)); - ASSERT(pkg != NULL, return(NULL)); - - if(pkg->origin == PKG_FROM_CACHE && !(pkg->infolevel & INFRQ_DESC)) { - _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_DESC); - } - return pkg->groups; -} - -int SYMEXPORT alpm_pkg_has_force(pmpkg_t *pkg) -{ - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, return(-1)); - ASSERT(pkg != NULL, return(-1)); - - if(pkg->origin == PKG_FROM_CACHE && !(pkg->infolevel & INFRQ_DESC)) { - _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_DESC); - } - return pkg->force; + return pkg->ops->get_groups(pkg); } alpm_list_t SYMEXPORT *alpm_pkg_get_depends(pmpkg_t *pkg) { - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, return(NULL)); - ASSERT(pkg != NULL, return(NULL)); - - if(pkg->origin == PKG_FROM_CACHE && !(pkg->infolevel & INFRQ_DEPENDS)) { - _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_DEPENDS); - } - return pkg->depends; + return pkg->ops->get_depends(pkg); } alpm_list_t SYMEXPORT *alpm_pkg_get_optdepends(pmpkg_t *pkg) { - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, return(NULL)); - ASSERT(pkg != NULL, return(NULL)); - - if(pkg->origin == PKG_FROM_CACHE && !(pkg->infolevel & INFRQ_DEPENDS)) { - _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_DEPENDS); - } - return pkg->optdepends; + return pkg->ops->get_optdepends(pkg); } alpm_list_t SYMEXPORT *alpm_pkg_get_conflicts(pmpkg_t *pkg) { - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, return(NULL)); - ASSERT(pkg != NULL, return(NULL)); - - if(pkg->origin == PKG_FROM_CACHE && !(pkg->infolevel & INFRQ_DEPENDS)) { - _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_DEPENDS); - } - return pkg->conflicts; + return pkg->ops->get_conflicts(pkg); } alpm_list_t SYMEXPORT *alpm_pkg_get_provides(pmpkg_t *pkg) { - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, return(NULL)); - ASSERT(pkg != NULL, return(NULL)); - - if(pkg->origin == PKG_FROM_CACHE && !(pkg->infolevel & INFRQ_DEPENDS)) { - _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_DEPENDS); - } - return pkg->provides; + return pkg->ops->get_provides(pkg); } -alpm_list_t SYMEXPORT *alpm_pkg_get_deltas(pmpkg_t *pkg) +alpm_list_t SYMEXPORT *alpm_pkg_get_replaces(pmpkg_t *pkg) { - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, return(NULL)); - ASSERT(pkg != NULL, return(NULL)); - - if(pkg->origin == PKG_FROM_CACHE && !(pkg->infolevel & INFRQ_DELTAS)) { - _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_DELTAS); - } - return pkg->deltas; + return pkg->ops->get_replaces(pkg); } -alpm_list_t SYMEXPORT *alpm_pkg_get_replaces(pmpkg_t *pkg) +alpm_list_t SYMEXPORT *alpm_pkg_get_deltas(pmpkg_t *pkg) { - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, return(NULL)); - ASSERT(pkg != NULL, return(NULL)); - - if(pkg->origin == PKG_FROM_CACHE && !(pkg->infolevel & INFRQ_DESC)) { - _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_DESC); - } - return pkg->replaces; + return pkg->ops->get_deltas(pkg); } alpm_list_t SYMEXPORT *alpm_pkg_get_files(pmpkg_t *pkg) { - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, return(NULL)); - ASSERT(pkg != NULL, return(NULL)); - - if(pkg->origin == PKG_FROM_CACHE && pkg->origin_data.db == handle->db_local - && !(pkg->infolevel & INFRQ_FILES)) { - _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_FILES); - } - return pkg->files; + return pkg->ops->get_files(pkg); } alpm_list_t SYMEXPORT *alpm_pkg_get_backup(pmpkg_t *pkg) { - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, return(NULL)); - ASSERT(pkg != NULL, return(NULL)); - - if(pkg->origin == PKG_FROM_CACHE && pkg->origin_data.db == handle->db_local - && !(pkg->infolevel & INFRQ_FILES)) { - _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_FILES); - } - return pkg->backup; + return pkg->ops->get_backup(pkg); } pmdb_t SYMEXPORT *alpm_pkg_get_db(pmpkg_t *pkg) { /* Sanity checks */ ASSERT(pkg != NULL, return(NULL)); - ASSERT(pkg->origin == PKG_FROM_CACHE, return(NULL)); + ASSERT(pkg->origin != PKG_FROM_FILE, return(NULL)); return(pkg->origin_data.db); } @@ -441,83 +296,31 @@ pmdb_t SYMEXPORT *alpm_pkg_get_db(pmpkg_t *pkg) */ void SYMEXPORT *alpm_pkg_changelog_open(pmpkg_t *pkg) { - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, return(NULL)); - ASSERT(pkg != NULL, return(NULL)); - - if(pkg->origin == PKG_FROM_CACHE) { - char clfile[PATH_MAX]; - snprintf(clfile, PATH_MAX, "%s/%s/%s-%s/changelog", - alpm_option_get_dbpath(), - alpm_db_get_name(handle->db_local), - alpm_pkg_get_name(pkg), - alpm_pkg_get_version(pkg)); - return fopen(clfile, "r"); - } else if(pkg->origin == PKG_FROM_FILE) { - struct archive *archive = NULL; - struct archive_entry *entry; - const char *pkgfile = pkg->origin_data.file; - - if((archive = archive_read_new()) == NULL) { - RET_ERR(PM_ERR_LIBARCHIVE, NULL); - } - - archive_read_support_compression_all(archive); - archive_read_support_format_all(archive); - - if (archive_read_open_filename(archive, pkgfile, - ARCHIVE_DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK) { - RET_ERR(PM_ERR_PKG_OPEN, NULL); - } - - while(archive_read_next_header(archive, &entry) == ARCHIVE_OK) { - const char *entry_name = archive_entry_pathname(entry); - - if(strcmp(entry_name, ".CHANGELOG") == 0) { - return(archive); - } - } - /* we didn't find a changelog */ - archive_read_finish(archive); - errno = ENOENT; - } - return(NULL); + return pkg->ops->changelog_open(pkg); } /** * Read data from an open changelog 'file stream'. Similar to fread in - * functionality, this function takes a buffer and amount of data to read. + * functionality, this function takes a buffer and amount of data to read. If an + * error occurs pm_errno will be set. + * * @param ptr a buffer to fill with raw changelog data * @param size the size of the buffer * @param pkg the package that the changelog is being read from * @param fp a 'file stream' to the package changelog - * @return the number of characters read, or 0 if there is no more data + * @return the number of characters read, or 0 if there is no more data or an + * error occurred. */ size_t SYMEXPORT alpm_pkg_changelog_read(void *ptr, size_t size, const pmpkg_t *pkg, const void *fp) { - size_t ret = 0; - if(pkg->origin == PKG_FROM_CACHE) { - ret = fread(ptr, 1, size, (FILE*)fp); - } else if(pkg->origin == PKG_FROM_FILE) { - ret = archive_read_data((struct archive*)fp, ptr, size); - } - return(ret); + return pkg->ops->changelog_read(ptr, size, pkg, fp); } /* int SYMEXPORT alpm_pkg_changelog_feof(const pmpkg_t *pkg, void *fp) { - int ret = 0; - if(pkg->origin == PKG_FROM_CACHE) { - ret = feof((FILE*)fp); - } else if(pkg->origin == PKG_FROM_FILE) { - // note: this doesn't quite work, no feof in libarchive - ret = archive_read_data((struct archive*)fp, NULL, 0); - } - return(ret); + return pkg->ops->changelog_feof(pkg, fp); } */ @@ -531,28 +334,12 @@ int SYMEXPORT alpm_pkg_changelog_feof(const pmpkg_t *pkg, void *fp) */ int SYMEXPORT alpm_pkg_changelog_close(const pmpkg_t *pkg, void *fp) { - int ret = 0; - if(pkg->origin == PKG_FROM_CACHE) { - ret = fclose((FILE*)fp); - } else if(pkg->origin == PKG_FROM_FILE) { - ret = archive_read_finish((struct archive *)fp); - } - return(ret); + return pkg->ops->changelog_close(pkg, fp); } int SYMEXPORT alpm_pkg_has_scriptlet(pmpkg_t *pkg) { - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, return(-1)); - ASSERT(pkg != NULL, return(-1)); - - if(pkg->origin == PKG_FROM_CACHE && pkg->origin_data.db == handle->db_local - && !(pkg->infolevel & INFRQ_SCRIPTLET)) { - _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_SCRIPTLET); - } - return pkg->scriptlet; + return pkg->ops->has_scriptlet(pkg); } static void find_requiredby(pmpkg_t *pkg, pmdb_t *db, alpm_list_t **reqs) @@ -626,6 +413,7 @@ pmpkg_t *_alpm_pkg_dup(pmpkg_t *pkg) CALLOC(newpkg, 1, sizeof(pmpkg_t), RET_ERR(PM_ERR_MEMORY, NULL)); + newpkg->name_hash = pkg->name_hash; STRDUP(newpkg->filename, pkg->filename, RET_ERR(PM_ERR_MEMORY, newpkg)); STRDUP(newpkg->name, pkg->name, RET_ERR(PM_ERR_MEMORY, newpkg)); STRDUP(newpkg->version, pkg->version, RET_ERR(PM_ERR_MEMORY, newpkg)); @@ -639,7 +427,7 @@ pmpkg_t *_alpm_pkg_dup(pmpkg_t *pkg) newpkg->size = pkg->size; newpkg->isize = pkg->isize; newpkg->scriptlet = pkg->scriptlet; - newpkg->force = pkg->force; + newpkg->epoch = pkg->epoch; newpkg->reason = pkg->reason; newpkg->licenses = alpm_list_strdup(pkg->licenses); @@ -657,6 +445,7 @@ pmpkg_t *_alpm_pkg_dup(pmpkg_t *pkg) /* internal */ newpkg->origin = pkg->origin; + newpkg->ops = pkg->ops; if(newpkg->origin == PKG_FROM_FILE) { newpkg->origin_data.file = strdup(pkg->origin_data.file); } else { @@ -726,21 +515,25 @@ void _alpm_pkg_free_trans(pmpkg_t *pkg) pkg->removes = NULL; } -/* Is spkg an upgrade for locapkg? */ +/* Is spkg an upgrade for localpkg? */ int _alpm_pkg_compare_versions(pmpkg_t *spkg, pmpkg_t *localpkg) { - int cmp = 0; + int spkg_epoch, localpkg_epoch; ALPM_LOG_FUNC; - cmp = alpm_pkg_vercmp(alpm_pkg_get_version(spkg), - alpm_pkg_get_version(localpkg)); + spkg_epoch = alpm_pkg_get_epoch(spkg); + localpkg_epoch = alpm_pkg_get_epoch(localpkg); - if(cmp < 0 && alpm_pkg_has_force(spkg)) { - cmp = 1; + if(spkg_epoch > localpkg_epoch) { + return(1); + } else if(spkg_epoch < localpkg_epoch) { + return(-1); } - return(cmp); + /* equal epoch values, move on to version comparison */ + return alpm_pkg_vercmp(alpm_pkg_get_version(spkg), + alpm_pkg_get_version(localpkg)); } /* Helper function for comparing packages @@ -749,7 +542,7 @@ int _alpm_pkg_cmp(const void *p1, const void *p2) { pmpkg_t *pkg1 = (pmpkg_t *)p1; pmpkg_t *pkg2 = (pmpkg_t *)p2; - return(strcmp(pkg1->name, pkg2->name)); + return(strcoll(pkg1->name, pkg2->name)); } /* Test for existence of a package in a alpm_list_t* @@ -758,6 +551,7 @@ int _alpm_pkg_cmp(const void *p1, const void *p2) pmpkg_t *_alpm_pkg_find(alpm_list_t *haystack, const char *needle) { alpm_list_t *lp; + unsigned long needle_hash; ALPM_LOG_FUNC; @@ -765,11 +559,21 @@ pmpkg_t *_alpm_pkg_find(alpm_list_t *haystack, const char *needle) return(NULL); } + needle_hash = _alpm_hash_sdbm(needle); + for(lp = haystack; lp; lp = lp->next) { pmpkg_t *info = lp->data; - if(info && strcmp(info->name, needle) == 0) { - return(info); + if(info) { + /* a zero hash will cause a fall-through just in case */ + if(info->name_hash && info->name_hash != needle_hash) { + continue; + } + + /* finally: we had hash match, verify string match */ + if(strcmp(info->name, needle) == 0) { + return(info); + } } } return(NULL); diff --git a/lib/libalpm/package.h b/lib/libalpm/package.h index c8946448..7740d79a 100644 --- a/lib/libalpm/package.h +++ b/lib/libalpm/package.h @@ -31,27 +31,94 @@ #include "db.h" typedef enum _pmpkgfrom_t { - PKG_FROM_CACHE = 1, - PKG_FROM_FILE + PKG_FROM_FILE = 1, + PKG_FROM_LOCALDB, + PKG_FROM_SYNCDB } pmpkgfrom_t; +/** Package operations struct. This struct contains function pointers to + * all methods used to access data in a package to allow for things such + * as lazy package intialization (such as used by the file backend). Each + * backend is free to define a stuct containing pointers to a specific + * implementation of these methods. Some backends may find using the + * defined default_pkg_ops struct to work just fine for their needs. + */ +struct pkg_operations { + const char *(*get_filename) (pmpkg_t *); + const char *(*get_name) (pmpkg_t *); + const char *(*get_version) (pmpkg_t *); + const char *(*get_desc) (pmpkg_t *); + const char *(*get_url) (pmpkg_t *); + time_t (*get_builddate) (pmpkg_t *); + time_t (*get_installdate) (pmpkg_t *); + const char *(*get_packager) (pmpkg_t *); + const char *(*get_md5sum) (pmpkg_t *); + const char *(*get_arch) (pmpkg_t *); + off_t (*get_size) (pmpkg_t *); + off_t (*get_isize) (pmpkg_t *); + pmpkgreason_t (*get_reason) (pmpkg_t *); + int (*get_epoch) (pmpkg_t *); + int (*has_scriptlet) (pmpkg_t *); + + alpm_list_t *(*get_licenses) (pmpkg_t *); + alpm_list_t *(*get_groups) (pmpkg_t *); + alpm_list_t *(*get_depends) (pmpkg_t *); + alpm_list_t *(*get_optdepends) (pmpkg_t *); + alpm_list_t *(*get_conflicts) (pmpkg_t *); + alpm_list_t *(*get_provides) (pmpkg_t *); + alpm_list_t *(*get_replaces) (pmpkg_t *); + alpm_list_t *(*get_deltas) (pmpkg_t *); + alpm_list_t *(*get_files) (pmpkg_t *); + alpm_list_t *(*get_backup) (pmpkg_t *); + + void *(*changelog_open) (pmpkg_t *); + size_t (*changelog_read) (void *, size_t, const pmpkg_t *, const void *); + int (*changelog_close) (const pmpkg_t *, void *); + + /* still to add: + * checkmd5sum() ? + * compute_requiredby() + */ +}; + +/** The standard package operations struct. get fields directly from the + * struct itself with no abstraction layer or any type of lazy loading. + * The actual definition is in package.c so it can have access to the + * default accessor functions which are defined there. + */ +extern struct pkg_operations default_pkg_ops; + struct __pmpkg_t { + unsigned long name_hash; char *filename; char *name; char *version; char *desc; char *url; - time_t builddate; - time_t installdate; char *packager; char *md5sum; char *arch; + + time_t builddate; + time_t installdate; + off_t size; off_t isize; off_t download_size; + int scriptlet; - int force; + int epoch; + pmpkgreason_t reason; + pmpkgfrom_t origin; + /* origin == PKG_FROM_FILE, use pkg->origin_data.file + * origin == PKG_FROM_*DB, use pkg->origin_data.db */ + union { + pmdb_t *db; + char *file; + } origin_data; + pmdbinfrq_t infolevel; + alpm_list_t *licenses; alpm_list_t *replaces; alpm_list_t *groups; @@ -64,17 +131,8 @@ struct __pmpkg_t { alpm_list_t *deltas; alpm_list_t *delta_path; alpm_list_t *removes; /* in transaction targets only */ - /* internal */ - pmpkgfrom_t origin; - /* Replaced 'void *data' with this union as follows: - origin == PKG_FROM_CACHE, use pkg->origin_data.db - origin == PKG_FROM_FILE, use pkg->origin_data.file - */ - union { - pmdb_t *db; - char *file; - } origin_data; - pmdbinfrq_t infolevel; + + struct pkg_operations *ops; }; pmpkg_t* _alpm_pkg_new(void); diff --git a/lib/libalpm/po/Makefile.in.in b/lib/libalpm/po/Makefile.in.in index 6f2e2e94..83d8838a 100644 --- a/lib/libalpm/po/Makefile.in.in +++ b/lib/libalpm/po/Makefile.in.in @@ -1,5 +1,5 @@ # Makefile for PO directory in any package using GNU gettext. -# Copyright (C) 1995-1997, 2000-2003 by Ulrich Drepper <drepper@gnu.ai.mit.edu> +# Copyright (C) 1995-1997, 2000-2007, 2009-2010 by Ulrich Drepper <drepper@gnu.ai.mit.edu> # # This file can be copied and used freely without restrictions. It can # be used in projects which are not available under the GNU General Public @@ -8,10 +8,12 @@ # Please note that the actual code of GNU gettext is covered by the GNU # General Public License and is *not* in the public domain. # -# Origin: gettext-0.13 +# Origin: gettext-0.18 +GETTEXT_MACRO_VERSION = 0.18 PACKAGE = @PACKAGE@ VERSION = @VERSION@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ SHELL = /bin/sh @SET_MAKE@ @@ -22,18 +24,38 @@ VPATH = @srcdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ -datadir = @datadir@ datarootdir = @datarootdir@ -localedir = $(datadir)/locale +datadir = @datadir@ +localedir = @localedir@ gettextsrcdir = $(datadir)/gettext/po INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ -mkinstalldirs = @INSTALL@ -d -GMSGFMT = @GMSGFMT@ -MSGFMT = @MSGFMT@ -XGETTEXT = @XGETTEXT@ +# We use $(mkdir_p). +# In automake <= 1.9.x, $(mkdir_p) is defined either as "mkdir -p --" or as +# "$(mkinstalldirs)" or as "$(install_sh) -d". For these automake versions, +# @install_sh@ does not start with $(SHELL), so we add it. +# In automake >= 1.10, @mkdir_p@ is derived from ${MKDIR_P}, which is defined +# either as "/path/to/mkdir -p" or ".../install-sh -c -d". For these automake +# versions, $(mkinstalldirs) and $(install_sh) are unused. +mkinstalldirs = $(SHELL) @install_sh@ -d +install_sh = $(SHELL) @install_sh@ +MKDIR_P = @MKDIR_P@ +mkdir_p = @mkdir_p@ + +GMSGFMT_ = @GMSGFMT@ +GMSGFMT_no = @GMSGFMT@ +GMSGFMT_yes = @GMSGFMT_015@ +GMSGFMT = $(GMSGFMT_$(USE_MSGCTXT)) +MSGFMT_ = @MSGFMT@ +MSGFMT_no = @MSGFMT@ +MSGFMT_yes = @MSGFMT_015@ +MSGFMT = $(MSGFMT_$(USE_MSGCTXT)) +XGETTEXT_ = @XGETTEXT@ +XGETTEXT_no = @XGETTEXT@ +XGETTEXT_yes = @XGETTEXT_015@ +XGETTEXT = $(XGETTEXT_$(USE_MSGCTXT)) MSGMERGE = msgmerge MSGMERGE_UPDATE = @MSGMERGE@ --update MSGINIT = msginit @@ -46,7 +68,7 @@ UPDATEPOFILES = @UPDATEPOFILES@ DUMMYPOFILES = @DUMMYPOFILES@ DISTFILES.common = Makefile.in.in remove-potcdate.sin \ $(DISTFILES.common.extra1) $(DISTFILES.common.extra2) $(DISTFILES.common.extra3) -DISTFILES = $(DISTFILES.common) Makevars POTFILES.in $(DOMAIN).pot stamp-po \ +DISTFILES = $(DISTFILES.common) Makevars POTFILES.in \ $(POFILES) $(GMOFILES) \ $(DISTFILES.extra1) $(DISTFILES.extra2) $(DISTFILES.extra3) @@ -57,7 +79,7 @@ CATALOGS = @CATALOGS@ # Makevars gets inserted here. (Don't remove this line!) .SUFFIXES: -.SUFFIXES: .po .gmo .mo .sed .sin .nop .po-update +.SUFFIXES: .po .gmo .mo .sed .sin .nop .po-create .po-update .po.mo: @echo "$(MSGFMT) -c -o $@ $<"; \ @@ -66,19 +88,32 @@ CATALOGS = @CATALOGS@ .po.gmo: @lang=`echo $* | sed -e 's,.*/,,'`; \ test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \ - echo "$${cdcmd}rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics -o $${lang}.gmo $${lang}.po"; \ - cd $(srcdir) && rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics -o t-$${lang}.gmo $${lang}.po && mv t-$${lang}.gmo $${lang}.gmo + echo "$${cdcmd}rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics --verbose -o $${lang}.gmo $${lang}.po"; \ + cd $(srcdir) && rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics --verbose -o t-$${lang}.gmo $${lang}.po && mv t-$${lang}.gmo $${lang}.gmo .sin.sed: sed -e '/^#/d' $< > t-$@ mv t-$@ $@ -all: all-@USE_NLS@ +all: check-macro-version all-@USE_NLS@ all-yes: stamp-po all-no: +# Ensure that the gettext macros and this Makefile.in.in are in sync. +check-macro-version: + @test "$(GETTEXT_MACRO_VERSION)" = "@GETTEXT_MACRO_VERSION@" \ + || { echo "*** error: gettext infrastructure mismatch: using a Makefile.in.in from gettext version $(GETTEXT_MACRO_VERSION) but the autoconf macros are from gettext version @GETTEXT_MACRO_VERSION@" 1>&2; \ + exit 1; \ + } + +# $(srcdir)/$(DOMAIN).pot is only created when needed. When xgettext finds no +# internationalized messages, no $(srcdir)/$(DOMAIN).pot is created (because +# we don't want to bother translators with empty POT files). We assume that +# LINGUAS is empty in this case, i.e. $(POFILES) and $(GMOFILES) are empty. +# In this case, stamp-po is a nop (i.e. a phony target). + # stamp-po is a timestamp denoting the last time at which the CATALOGS have # been loosely updated. Its purpose is that when a developer or translator # checks out the package via CVS, and the $(DOMAIN).pot file is not in CVS, @@ -88,10 +123,13 @@ all-no: # $(POFILES) has been designed to not touch files that don't need to be # changed. stamp-po: $(srcdir)/$(DOMAIN).pot - test -z "$(GMOFILES)" || $(MAKE) $(GMOFILES) - @echo "touch stamp-po" - @echo timestamp > stamp-poT - @mv stamp-poT stamp-po + test ! -f $(srcdir)/$(DOMAIN).pot || \ + test -z "$(GMOFILES)" || $(MAKE) $(GMOFILES) + @test ! -f $(srcdir)/$(DOMAIN).pot || { \ + echo "touch stamp-po" && \ + echo timestamp > stamp-poT && \ + mv stamp-poT stamp-po; \ + } # Note: Target 'all' must not depend on target '$(DOMAIN).pot-update', # otherwise packages like GCC can not be built if only parts of the source @@ -100,11 +138,34 @@ stamp-po: $(srcdir)/$(DOMAIN).pot # This target rebuilds $(DOMAIN).pot; it is an expensive operation. # Note that $(DOMAIN).pot is not touched if it doesn't need to be changed. $(DOMAIN).pot-update: $(POTFILES) $(srcdir)/POTFILES.in remove-potcdate.sed - $(XGETTEXT) --default-domain=$(DOMAIN) --directory=$(top_srcdir) \ - --add-comments=TRANSLATORS: $(XGETTEXT_OPTIONS) \ - --files-from=$(srcdir)/POTFILES.in \ - --copyright-holder='$(COPYRIGHT_HOLDER)' \ - --msgid-bugs-address='$(MSGID_BUGS_ADDRESS)' + if LC_ALL=C grep 'GNU @PACKAGE@' $(top_srcdir)/* 2>/dev/null | grep -v 'libtool:' >/dev/null; then \ + package_gnu='GNU '; \ + else \ + package_gnu=''; \ + fi; \ + if test -n '$(MSGID_BUGS_ADDRESS)' || test '$(PACKAGE_BUGREPORT)' = '@'PACKAGE_BUGREPORT'@'; then \ + msgid_bugs_address='$(MSGID_BUGS_ADDRESS)'; \ + else \ + msgid_bugs_address='$(PACKAGE_BUGREPORT)'; \ + fi; \ + case `$(XGETTEXT) --version | sed 1q | sed -e 's,^[^0-9]*,,'` in \ + '' | 0.[0-9] | 0.[0-9].* | 0.1[0-5] | 0.1[0-5].* | 0.16 | 0.16.[0-1]*) \ + $(XGETTEXT) --default-domain=$(DOMAIN) --directory=$(top_srcdir) \ + --add-comments=TRANSLATORS: $(XGETTEXT_OPTIONS) @XGETTEXT_EXTRA_OPTIONS@ \ + --files-from=$(srcdir)/POTFILES.in \ + --copyright-holder='$(COPYRIGHT_HOLDER)' \ + --msgid-bugs-address="$$msgid_bugs_address" \ + ;; \ + *) \ + $(XGETTEXT) --default-domain=$(DOMAIN) --directory=$(top_srcdir) \ + --add-comments=TRANSLATORS: $(XGETTEXT_OPTIONS) @XGETTEXT_EXTRA_OPTIONS@ \ + --files-from=$(srcdir)/POTFILES.in \ + --copyright-holder='$(COPYRIGHT_HOLDER)' \ + --package-name="$${package_gnu}@PACKAGE@" \ + --package-version='@VERSION@' \ + --msgid-bugs-address="$$msgid_bugs_address" \ + ;; \ + esac test ! -f $(DOMAIN).po || { \ if test -f $(srcdir)/$(DOMAIN).pot; then \ sed -f remove-potcdate.sed < $(srcdir)/$(DOMAIN).pot > $(DOMAIN).1po && \ @@ -130,16 +191,27 @@ $(srcdir)/$(DOMAIN).pot: # Note that a PO file is not touched if it doesn't need to be changed. $(POFILES): $(srcdir)/$(DOMAIN).pot @lang=`echo $@ | sed -e 's,.*/,,' -e 's/\.po$$//'`; \ - test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \ - echo "$${cdcmd}$(MSGMERGE_UPDATE) $${lang}.po $(DOMAIN).pot"; \ - cd $(srcdir) && $(MSGMERGE_UPDATE) $${lang}.po $(DOMAIN).pot + if test -f "$(srcdir)/$${lang}.po"; then \ + test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \ + echo "$${cdcmd}$(MSGMERGE_UPDATE) $(MSGMERGE_OPTIONS) --lang=$${lang} $${lang}.po $(DOMAIN).pot"; \ + cd $(srcdir) \ + && { case `$(MSGMERGE_UPDATE) --version | sed 1q | sed -e 's,^[^0-9]*,,'` in \ + '' | 0.[0-9] | 0.[0-9].* | 0.1[0-7] | 0.1[0-7].*) \ + $(MSGMERGE_UPDATE) $(MSGMERGE_OPTIONS) $${lang}.po $(DOMAIN).pot;; \ + *) \ + $(MSGMERGE_UPDATE) $(MSGMERGE_OPTIONS) --lang=$${lang} $${lang}.po $(DOMAIN).pot;; \ + esac; \ + }; \ + else \ + $(MAKE) $${lang}.po-create; \ + fi install: install-exec install-data install-exec: install-data: install-data-@USE_NLS@ if test "$(PACKAGE)" = "gettext-tools"; then \ - $(mkinstalldirs) $(DESTDIR)$(gettextsrcdir); \ + $(mkdir_p) $(DESTDIR)$(gettextsrcdir); \ for file in $(DISTFILES.common) Makevars.template; do \ $(INSTALL_DATA) $(srcdir)/$$file \ $(DESTDIR)$(gettextsrcdir)/$$file; \ @@ -152,13 +224,12 @@ install-data: install-data-@USE_NLS@ fi install-data-no: all install-data-yes: all - $(mkinstalldirs) $(DESTDIR)$(datadir) @catalogs='$(CATALOGS)'; \ for cat in $$catalogs; do \ cat=`basename $$cat`; \ lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \ dir=$(localedir)/$$lang/LC_MESSAGES; \ - $(mkinstalldirs) $(DESTDIR)$$dir; \ + $(mkdir_p) $(DESTDIR)$$dir; \ if test -r $$cat; then realcat=$$cat; else realcat=$(srcdir)/$$cat; fi; \ $(INSTALL_DATA) $$realcat $(DESTDIR)$$dir/$(DOMAIN).mo; \ echo "installing $$realcat as $(DESTDIR)$$dir/$(DOMAIN).mo"; \ @@ -198,19 +269,18 @@ installdirs: installdirs-exec installdirs-data installdirs-exec: installdirs-data: installdirs-data-@USE_NLS@ if test "$(PACKAGE)" = "gettext-tools"; then \ - $(mkinstalldirs) $(DESTDIR)$(gettextsrcdir); \ + $(mkdir_p) $(DESTDIR)$(gettextsrcdir); \ else \ : ; \ fi installdirs-data-no: installdirs-data-yes: - $(mkinstalldirs) $(DESTDIR)$(datadir) @catalogs='$(CATALOGS)'; \ for cat in $$catalogs; do \ cat=`basename $$cat`; \ lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \ dir=$(localedir)/$$lang/LC_MESSAGES; \ - $(mkinstalldirs) $(DESTDIR)$$dir; \ + $(mkdir_p) $(DESTDIR)$$dir; \ for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \ if test -n "$$lc"; then \ if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 2>/dev/null) | grep ' -> ' >/dev/null; then \ @@ -285,11 +355,14 @@ dist distdir: $(MAKE) update-po @$(MAKE) dist2 # This is a separate target because 'update-po' must be executed before. -dist2: $(DISTFILES) +dist2: stamp-po $(DISTFILES) dists="$(DISTFILES)"; \ if test "$(PACKAGE)" = "gettext-tools"; then \ dists="$$dists Makevars.template"; \ fi; \ + if test -f $(srcdir)/$(DOMAIN).pot; then \ + dists="$$dists $(DOMAIN).pot stamp-po"; \ + fi; \ if test -f $(srcdir)/ChangeLog; then \ dists="$$dists ChangeLog"; \ fi; \ @@ -301,9 +374,9 @@ dist2: $(DISTFILES) if test -f $(srcdir)/LINGUAS; then dists="$$dists LINGUAS"; fi; \ for file in $$dists; do \ if test -f $$file; then \ - cp -p $$file $(distdir); \ + cp -p $$file $(distdir) || exit 1; \ else \ - cp -p $(srcdir)/$$file $(distdir); \ + cp -p $(srcdir)/$$file $(distdir) || exit 1; \ fi; \ done @@ -312,6 +385,13 @@ update-po: Makefile test -z "$(UPDATEPOFILES)" || $(MAKE) $(UPDATEPOFILES) $(MAKE) update-gmo +# General rule for creating PO files. + +.nop.po-create: + @lang=`echo $@ | sed -e 's/\.po-create$$//'`; \ + echo "File $$lang.po does not exist. If you are a translator, you can create it through 'msginit'." 1>&2; \ + exit 1 + # General rule for updating PO files. .nop.po-update: @@ -320,9 +400,15 @@ update-po: Makefile tmpdir=`pwd`; \ echo "$$lang:"; \ test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \ - echo "$${cdcmd}$(MSGMERGE) $$lang.po $(DOMAIN).pot -o $$lang.new.po"; \ + echo "$${cdcmd}$(MSGMERGE) $(MSGMERGE_OPTIONS) --lang=$$lang $$lang.po $(DOMAIN).pot -o $$lang.new.po"; \ cd $(srcdir); \ - if $(MSGMERGE) $$lang.po $(DOMAIN).pot -o $$tmpdir/$$lang.new.po; then \ + if { case `$(MSGMERGE) --version | sed 1q | sed -e 's,^[^0-9]*,,'` in \ + '' | 0.[0-9] | 0.[0-9].* | 0.1[0-7] | 0.1[0-7].*) \ + $(MSGMERGE) $(MSGMERGE_OPTIONS) -o $$tmpdir/$$lang.new.po $$lang.po $(DOMAIN).pot;; \ + *) \ + $(MSGMERGE) $(MSGMERGE_OPTIONS) --lang=$$lang -o $$tmpdir/$$lang.new.po $$lang.po $(DOMAIN).pot;; \ + esac; \ + }; then \ if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \ rm -f $$tmpdir/$$lang.new.po; \ else \ @@ -343,10 +429,13 @@ $(DUMMYPOFILES): update-gmo: Makefile $(GMOFILES) @: -Makefile: Makefile.in.in $(top_builddir)/config.status @POMAKEFILEDEPS@ +# Recreate Makefile by invoking config.status. Explicitly invoke the shell, +# because execution permission bits may not work on the current file system. +# Use @SHELL@, which is the shell determined by autoconf for the use by its +# scripts, not $(SHELL) which is hardwired to /bin/sh and may be deficient. +Makefile: Makefile.in.in Makevars $(top_builddir)/config.status @POMAKEFILEDEPS@ cd $(top_builddir) \ - && CONFIG_FILES=$(subdir)/$@.in CONFIG_HEADERS= \ - $(SHELL) ./config.status + && @SHELL@ ./config.status $(subdir)/$@.in po-directories force: diff --git a/lib/libalpm/po/POTFILES.in b/lib/libalpm/po/POTFILES.in index 475cf4b4..65637977 100644 --- a/lib/libalpm/po/POTFILES.in +++ b/lib/libalpm/po/POTFILES.in @@ -6,8 +6,9 @@ lib/libalpm/add.c lib/libalpm/alpm.c #lib/libalpm/alpm_list.c lib/libalpm/backup.c -lib/libalpm/be_files.c +lib/libalpm/be_local.c lib/libalpm/be_package.c +lib/libalpm/be_sync.c lib/libalpm/cache.c lib/libalpm/conflict.c lib/libalpm/db.c diff --git a/lib/libalpm/remove.c b/lib/libalpm/remove.c index 153c0426..c06a239b 100644 --- a/lib/libalpm/remove.c +++ b/lib/libalpm/remove.c @@ -42,12 +42,11 @@ #include "backup.h" #include "package.h" #include "db.h" -#include "cache.h" #include "deps.h" #include "handle.h" #include "alpm.h" -int SYMEXPORT alpm_remove_target(char *target) +int SYMEXPORT alpm_remove_target(const char *target) { pmpkg_t *info; pmtrans_t *trans; @@ -311,6 +310,10 @@ int _alpm_upgraderemove_package(pmpkg_t *oldpkg, pmpkg_t *newpkg, pmtrans_t *tra _alpm_log(PM_LOG_DEBUG, "removing old package first (%s-%s)\n", oldpkg->name, oldpkg->version); + if(trans->flags & PM_TRANS_FLAG_DBONLY) { + goto db; + } + /* copy the remove skiplist over */ skip_remove = alpm_list_join(alpm_list_strdup(trans->skip_remove),alpm_list_strdup(handle->noupgrade)); @@ -345,10 +348,11 @@ int _alpm_upgraderemove_package(pmpkg_t *oldpkg, pmpkg_t *newpkg, pmtrans_t *tra alpm_list_free(newfiles); FREELIST(skip_remove); +db: /* remove the package from the database */ _alpm_log(PM_LOG_DEBUG, "updating database\n"); _alpm_log(PM_LOG_DEBUG, "removing database entry '%s'\n", pkgname); - if(_alpm_db_remove(handle->db_local, oldpkg) == -1) { + if(_alpm_local_db_remove(handle->db_local, oldpkg) == -1) { _alpm_log(PM_LOG_ERROR, _("could not remove database entry %s-%s\n"), pkgname, alpm_pkg_get_version(oldpkg)); } @@ -447,7 +451,7 @@ int _alpm_remove_packages(pmtrans_t *trans, pmdb_t *db) /* remove the package from the database */ _alpm_log(PM_LOG_DEBUG, "updating database\n"); _alpm_log(PM_LOG_DEBUG, "removing database entry '%s'\n", pkgname); - if(_alpm_db_remove(db, info) == -1) { + if(_alpm_local_db_remove(db, info) == -1) { _alpm_log(PM_LOG_ERROR, _("could not remove database entry %s-%s\n"), pkgname, alpm_pkg_get_version(info)); } diff --git a/lib/libalpm/sync.c b/lib/libalpm/sync.c index 4cbaf0cb..69234cbf 100644 --- a/lib/libalpm/sync.c +++ b/lib/libalpm/sync.c @@ -31,7 +31,7 @@ #include <stdint.h> /* intmax_t */ #include <unistd.h> #include <time.h> -#include <dirent.h> +#include <limits.h> /* libalpm */ #include "sync.h" @@ -39,7 +39,6 @@ #include "log.h" #include "package.h" #include "db.h" -#include "cache.h" #include "deps.h" #include "conflict.h" #include "trans.h" @@ -50,6 +49,7 @@ #include "dload.h" #include "delta.h" #include "remove.h" +#include "diskspace.h" /** Check for new version of pkg in sync repos * (only the first occurrence is considered in sync) @@ -329,7 +329,7 @@ static int sync_target(alpm_list_t *dbs_sync, const char *target) * @param target the name of the sync target to add * @return 0 on success, -1 on error (pm_errno is set accordingly) */ -int SYMEXPORT alpm_sync_dbtarget(char *dbname, char *target) +int SYMEXPORT alpm_sync_dbtarget(const char *dbname, const char *target) { alpm_list_t *i; alpm_list_t *dbs_sync; @@ -362,7 +362,7 @@ int SYMEXPORT alpm_sync_dbtarget(char *dbname, char *target) * @param target the name of the sync target to add * @return 0 on success, -1 on error (pm_errno is set accordingly) */ -int SYMEXPORT alpm_sync_target(char *target) +int SYMEXPORT alpm_sync_target(const char *target) { alpm_list_t *dbs_sync; @@ -549,10 +549,10 @@ int _alpm_sync_prepare(pmtrans_t *trans, pmdb_t *db_local, alpm_list_t *dbs_sync /* if sync1 provides sync2, we remove sync2 from the targets, and vice versa */ pmdepend_t *dep1 = _alpm_splitdep(conflict->package1); pmdepend_t *dep2 = _alpm_splitdep(conflict->package2); - if(alpm_depcmp(sync1, dep2)) { + if(_alpm_depcmp(sync1, dep2)) { rsync = sync2; sync = sync1; - } else if(alpm_depcmp(sync2, dep1)) { + } else if(_alpm_depcmp(sync2, dep1)) { rsync = sync1; sync = sync2; } else { @@ -854,7 +854,7 @@ int _alpm_sync_commit(pmtrans_t *trans, pmdb_t *db_local, alpm_list_t **data) for(j = trans->add; j; j = j->next) { pmpkg_t *spkg = j->data; - if(spkg->origin == PKG_FROM_CACHE && current == spkg->origin_data.db) { + if(spkg->origin != PKG_FROM_FILE && current == spkg->origin_data.db) { const char *fname = NULL; fname = alpm_pkg_get_filename(spkg); @@ -1018,6 +1018,19 @@ int _alpm_sync_commit(pmtrans_t *trans, pmdb_t *db_local, alpm_list_t **data) EVENT(trans, PM_TRANS_EVT_FILECONFLICTS_DONE, NULL, NULL); } + /* check available disk space */ + if(handle->checkspace) { + EVENT(trans, PM_TRANS_EVT_DISKSPACE_START, NULL, NULL); + + _alpm_log(PM_LOG_DEBUG, "checking available disk space\n"); + if(_alpm_check_diskspace(trans, handle->db_local) == -1) { + _alpm_log(PM_LOG_ERROR, _("not enough free disk space\n")); + goto error; + } + + EVENT(trans, PM_TRANS_EVT_DISKSPACE_DONE, NULL, NULL); + } + /* remove conflicting and to-be-replaced packages */ if(replaces) { _alpm_log(PM_LOG_DEBUG, "removing conflicting and to-be-replaced packages\n"); diff --git a/lib/libalpm/trans.c b/lib/libalpm/trans.c index 02612ec1..5c132111 100644 --- a/lib/libalpm/trans.c +++ b/lib/libalpm/trans.c @@ -31,6 +31,7 @@ #include <sys/stat.h> #include <sys/statvfs.h> #include <errno.h> +#include <limits.h> /* libalpm */ #include "trans.h" @@ -44,7 +45,6 @@ #include "sync.h" #include "alpm.h" #include "deps.h" -#include "cache.h" /** \addtogroup alpm_trans Transaction Functions * @brief Functions to manipulate libalpm transactions @@ -323,11 +323,11 @@ static int grep(const char *fn, const char *needle) } while(!feof(fp)) { char line[1024]; - int sline = sizeof(line)-1; - fgets(line, sline, fp); - if(feof(fp)) { + if(fgets(line, sizeof(line), fp) == NULL) { continue; } + /* TODO: this will not work if the search string + * ends up being split across line reads */ if(strstr(line, needle)) { fclose(fp); return(1); @@ -344,6 +344,7 @@ int _alpm_runscriptlet(const char *root, const char *installfn, char scriptfn[PATH_MAX]; char cmdline[PATH_MAX]; char tmpdir[PATH_MAX]; + char *argv[] = { "sh", "-c", cmdline, NULL }; char *scriptpath; int clean_tmpdir = 0; int retval = 0; @@ -371,7 +372,7 @@ int _alpm_runscriptlet(const char *root, const char *installfn, /* either extract or copy the scriptlet */ snprintf(scriptfn, PATH_MAX, "%s/.INSTALL", tmpdir); - if(!strcmp(script, "pre_upgrade") || !strcmp(script, "pre_install")) { + if(strcmp(script, "pre_upgrade") == 0 || strcmp(script, "pre_install") == 0) { if(_alpm_unpack_single(installfn, tmpdir, ".INSTALL")) { retval = 1; } @@ -401,7 +402,9 @@ int _alpm_runscriptlet(const char *root, const char *installfn, scriptpath, script, ver); } - retval = _alpm_run_chroot(root, cmdline); + _alpm_log(PM_LOG_DEBUG, "executing \"%s\"\n", cmdline); + + retval = _alpm_run_chroot(root, "/bin/sh", argv); cleanup: if(clean_tmpdir && _alpm_rmrf(tmpdir)) { diff --git a/lib/libalpm/util.c b/lib/libalpm/util.c index 32eaa442..d34eab5e 100644 --- a/lib/libalpm/util.c +++ b/lib/libalpm/util.c @@ -43,13 +43,18 @@ #include <archive.h> #include <archive_entry.h> +#ifdef HAVE_LIBSSL +#include <openssl/md5.h> +#else +#include "md5.h" +#endif + /* libalpm */ #include "util.h" #include "log.h" #include "package.h" #include "alpm.h" #include "alpm_list.h" -#include "md5.h" #include "handle.h" #ifndef HAVE_STRSEP @@ -143,7 +148,15 @@ int _alpm_copyfile(const char *src, const char *dest) /* do the actual file copy */ while((len = fread(buf, 1, CPBUFSIZE, in))) { - fwrite(buf, 1, len, out); + size_t nwritten = 0; + nwritten = fwrite(buf, 1, len, out); + if((nwritten != len) || ferror(out)) { + pm_errno = PM_ERR_WRITE; + _alpm_log(PM_LOG_ERROR, _("error writing to file '%s': %s\n"), + dest, strerror(errno)); + ret = -1; + goto cleanup; + } } /* chmod dest to permissions of src, as long as it is not a symlink */ @@ -347,7 +360,7 @@ int _alpm_unpack(const char *archive, const char *prefix, alpm_list_t *list, int int readret = archive_read_extract(_archive, entry, 0); if(readret == ARCHIVE_WARN) { /* operation succeeded but a non-critical error was encountered */ - _alpm_log(PM_LOG_DEBUG, "warning extracting %s (%s)\n", + _alpm_log(PM_LOG_WARNING, _("warning given while extracting %s (%s)\n"), entryname, archive_error_string(_archive)); } else if(readret != ARCHIVE_OK) { _alpm_log(PM_LOG_ERROR, _("could not extract %s (%s)\n"), @@ -364,8 +377,8 @@ int _alpm_unpack(const char *archive, const char *prefix, alpm_list_t *list, int cleanup: umask(oldmask); archive_read_finish(_archive); - if(restore_cwd) { - chdir(cwd); + if(restore_cwd && chdir(cwd) != 0) { + _alpm_log(PM_LOG_ERROR, _("could not change directory to %s (%s)\n"), cwd, strerror(errno)); } return(ret); } @@ -398,7 +411,7 @@ int _alpm_rmrf(const char *path) for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { if(dp->d_ino) { sprintf(name, "%s/%s", path, dp->d_name); - if(strcmp(dp->d_name, "..") && strcmp(dp->d_name, ".")) { + if(strcmp(dp->d_name, "..") != 0 && strcmp(dp->d_name, ".") != 0) { errflag += _alpm_rmrf(name); } } @@ -444,10 +457,11 @@ int _alpm_logaction(int usesyslog, FILE *f, const char *fmt, va_list args) return(ret); } -int _alpm_run_chroot(const char *root, const char *cmd) +int _alpm_run_chroot(const char *root, const char *path, char *const argv[]) { char cwd[PATH_MAX]; pid_t pid; + int pipefd[2]; int restore_cwd = 0; int retval = 0; @@ -466,11 +480,17 @@ int _alpm_run_chroot(const char *root, const char *cmd) goto cleanup; } - _alpm_log(PM_LOG_DEBUG, "executing \"%s\" under chroot \"%s\"\n", cmd, root); + _alpm_log(PM_LOG_DEBUG, "executing \"%s\" under chroot \"%s\"\n", path, root); /* Flush open fds before fork() to avoid cloning buffers */ fflush(NULL); + if(pipe(pipefd) == -1) { + _alpm_log(PM_LOG_ERROR, _("could not create pipe (%s)\n"), strerror(errno)); + retval = 1; + goto cleanup; + } + /* fork- parent and child each have seperate code blocks below */ pid = fork(); if(pid == -1) { @@ -480,62 +500,74 @@ int _alpm_run_chroot(const char *root, const char *cmd) } if(pid == 0) { - FILE *pipe; /* this code runs for the child only (the actual chroot/exec) */ - _alpm_log(PM_LOG_DEBUG, "chrooting in %s\n", root); + close(1); + close(2); + while(dup2(pipefd[1], 1) == -1 && errno == EINTR); + while(dup2(pipefd[1], 2) == -1 && errno == EINTR); + close(pipefd[0]); + close(pipefd[1]); + + /* use fprintf instead of _alpm_log to send output through the parent */ if(chroot(root) != 0) { - _alpm_log(PM_LOG_ERROR, _("could not change the root directory (%s)\n"), - strerror(errno)); + fprintf(stderr, _("could not change the root directory (%s)\n"), strerror(errno)); exit(1); } if(chdir("/") != 0) { - _alpm_log(PM_LOG_ERROR, _("could not change directory to / (%s)\n"), - strerror(errno)); + fprintf(stderr, _("could not change directory to / (%s)\n"), strerror(errno)); exit(1); } umask(0022); - pipe = popen(cmd, "r"); - if(!pipe) { - _alpm_log(PM_LOG_ERROR, _("call to popen failed (%s)\n"), - strerror(errno)); - exit(1); - } - while(!feof(pipe)) { - char line[PATH_MAX]; - if(fgets(line, PATH_MAX, pipe) == NULL) - break; - alpm_logaction("%s", line); - EVENT(handle->trans, PM_TRANS_EVT_SCRIPTLET_INFO, line, NULL); - } - retval = pclose(pipe); - exit(WEXITSTATUS(retval)); + execv(path, argv); + fprintf(stderr, _("call to execv failed (%s)\n"), strerror(errno)); + exit(1); } else { /* this code runs for the parent only (wait on the child) */ - pid_t retpid; int status; - do { - retpid = waitpid(pid, &status, 0); - } while(retpid == -1 && errno == EINTR); - if(retpid == -1) { - _alpm_log(PM_LOG_ERROR, _("call to waitpid failed (%s)\n"), - strerror(errno)); + FILE *pipe; + + close(pipefd[1]); + pipe = fdopen(pipefd[0], "r"); + if(pipe == NULL) { + close(pipefd[0]); retval = 1; - goto cleanup; } else { - /* check the return status, make sure it is 0 (success) */ - if(WIFEXITED(status)) { - _alpm_log(PM_LOG_DEBUG, "call to waitpid succeeded\n"); - if(WEXITSTATUS(status) != 0) { - _alpm_log(PM_LOG_ERROR, _("command failed to execute correctly\n")); - retval = 1; - } + while(!feof(pipe)) { + char line[PATH_MAX]; + if(fgets(line, PATH_MAX, pipe) == NULL) + break; + alpm_logaction("%s", line); + EVENT(handle->trans, PM_TRANS_EVT_SCRIPTLET_INFO, line, NULL); + } + fclose(pipe); + } + + while(waitpid(pid, &status, 0) == -1) { + if(errno != EINTR) { + _alpm_log(PM_LOG_ERROR, _("call to waitpid failed (%s)\n"), strerror(errno)); + retval = 1; + goto cleanup; + } + } + + /* report error from above after the child has exited */ + if(retval != 0) { + _alpm_log(PM_LOG_ERROR, _("could not open pipe (%s)\n"), strerror(errno)); + goto cleanup; + } + /* check the return status, make sure it is 0 (success) */ + if(WIFEXITED(status)) { + _alpm_log(PM_LOG_DEBUG, "call to waitpid succeeded\n"); + if(WEXITSTATUS(status) != 0) { + _alpm_log(PM_LOG_ERROR, _("command failed to execute correctly\n")); + retval = 1; } } } cleanup: - if(restore_cwd) { - chdir(cwd); + if(restore_cwd && chdir(cwd) != 0) { + _alpm_log(PM_LOG_ERROR, _("could not change directory to %s (%s)\n"), cwd, strerror(errno)); } return(retval); @@ -551,7 +583,8 @@ int _alpm_ldconfig(const char *root) if(access(line, F_OK) == 0) { snprintf(line, PATH_MAX, "%ssbin/ldconfig", root); if(access(line, X_OK) == 0) { - _alpm_run_chroot(root, "/sbin/ldconfig"); + char *argv[] = { "ldconfig", NULL }; + _alpm_run_chroot(root, "/sbin/ldconfig", argv); } } @@ -648,6 +681,42 @@ int _alpm_lstat(const char *path, struct stat *buf) return(ret); } +#ifdef HAVE_LIBSSL +static int md5_file(const char *path, unsigned char output[16]) +{ + FILE *f; + size_t n; + MD5_CTX ctx; + unsigned char *buf; + + CALLOC(buf, 8192, sizeof(unsigned char), return(1)); + + if((f = fopen(path, "rb")) == NULL) { + free(buf); + return(1); + } + + MD5_Init(&ctx); + + while((n = fread(buf, 1, sizeof(buf), f)) > 0) { + MD5_Update(&ctx, buf, n); + } + + MD5_Final(output, &ctx); + + memset(&ctx, 0, sizeof(MD5_CTX)); + free(buf); + + if(ferror(f) != 0) { + fclose(f); + return(2); + } + + fclose(f); + return(0); +} +#endif + /** Get the md5 sum of file. * @param filename name of the file * @return the checksum on success, NULL on error @@ -665,6 +734,7 @@ char SYMEXPORT *alpm_compute_md5sum(const char *filename) /* allocate 32 chars plus 1 for null */ md5sum = calloc(33, sizeof(char)); + /* defined above for OpenSSL, otherwise defined in md5.h */ ret = md5_file(filename, output); if (ret > 0) { @@ -701,33 +771,156 @@ int _alpm_test_md5sum(const char *filepath, const char *md5sum) return(ret); } -char *_alpm_archive_fgets(char *line, size_t size, struct archive *a) +/* Note: does NOT handle sparse files on purpose for speed. */ +int _alpm_archive_fgets(struct archive *a, struct archive_read_buffer *b) { - /* for now, just read one char at a time until we get to a - * '\n' char. we can optimize this later with an internal - * buffer. */ - /* leave room for zero terminator */ - char *last = line + size - 1; - char *i; - - for(i = line; i < last; i++) { - int ret = archive_read_data(a, i, 1); - /* special check for first read- if null, return null, - * this indicates EOF */ - if(i == line && (ret <= 0 || *i == '\0')) { - return(NULL); + char *i = NULL; + int64_t offset; + int done = 0; + + while(1) { + /* have we processed this entire block? */ + if(b->block + b->block_size == b->block_offset) { + if(b->ret == ARCHIVE_EOF) { + /* reached end of archive on the last read, now we are out of data */ + goto cleanup; + } + + /* zero-copy - this is the entire next block of data. */ + b->ret = archive_read_data_block(a, (void*)&b->block, + &b->block_size, &offset); + b->block_offset = b->block; + + /* error or end of archive with no data read, cleanup */ + if(b->ret < ARCHIVE_OK || + (b->block_size == 0 && b->ret == ARCHIVE_EOF)) { + goto cleanup; + } } - /* check if read value was null or newline */ - if(ret <= 0 || *i == '\0' || *i == '\n') { - last = i + 1; - break; + + /* loop through the block looking for EOL characters */ + for(i = b->block_offset; i < (b->block + b->block_size); i++) { + /* check if read value was null or newline */ + if(*i == '\0' || *i == '\n') { + done = 1; + break; + } } + + /* allocate our buffer, or ensure our existing one is big enough */ + if(!b->line) { + /* set the initial buffer to the read block_size */ + CALLOC(b->line, b->block_size + 1, sizeof(char), + RET_ERR(PM_ERR_MEMORY, -1)); + b->line_size = b->block_size + 1; + b->line_offset = b->line; + } else { + size_t needed = (b->line_offset - b->line) + (i - b->block_offset) + 1; + if(needed > b->max_line_size) { + RET_ERR(PM_ERR_MEMORY, -1); + } + if(needed > b->line_size) { + /* need to realloc + copy data to fit total length */ + char *new; + CALLOC(new, needed, sizeof(char), RET_ERR(PM_ERR_MEMORY, -1)); + memcpy(new, b->line, b->line_size); + b->line_size = needed; + b->line_offset = new + (b->line_offset - b->line); + free(b->line); + b->line = new; + } + } + + if(done) { + size_t len = i - b->block_offset; + memcpy(b->line_offset, b->block_offset, len); + b->line_offset[len] = '\0'; + b->block_offset = ++i; + /* this is the main return point; from here you can read b->line */ + return(ARCHIVE_OK); + } else { + /* we've looked through the whole block but no newline, copy it */ + size_t len = b->block + b->block_size - b->block_offset; + memcpy(b->line_offset, b->block_offset, len); + b->line_offset += len; + b->block_offset = i; + } + } + +cleanup: + { + int ret = b->ret; + FREE(b->line); + memset(b, 0, sizeof(b)); + return(ret); + } +} + +int _alpm_splitname(const char *target, pmpkg_t *pkg) +{ + /* the format of a db entry is as follows: + * package-version-rel/ + * package name can contain hyphens, so parse from the back- go back + * two hyphens and we have split the version from the name. + */ + char *tmp, *p, *q; + + if(target == NULL || pkg == NULL) { + return(-1); + } + STRDUP(tmp, target, RET_ERR(PM_ERR_MEMORY, -1)); + p = tmp + strlen(tmp); + + /* remove any trailing '/' */ + while (*(p - 1) == '/') { + --p; + *p = '\0'; } - /* always null terminate the buffer */ - *last = '\0'; + /* do the magic parsing- find the beginning of the version string + * by doing two iterations of same loop to lop off two hyphens */ + for(q = --p; *q && *q != '-'; q--); + for(p = --q; *p && *p != '-'; p--); + if(*p != '-' || p == tmp) { + return(-1); + } + + /* copy into fields and return */ + if(pkg->version) { + FREE(pkg->version); + } + STRDUP(pkg->version, p+1, RET_ERR(PM_ERR_MEMORY, -1)); + /* insert a terminator at the end of the name (on hyphen)- then copy it */ + *p = '\0'; + if(pkg->name) { + FREE(pkg->name); + } + STRDUP(pkg->name, tmp, RET_ERR(PM_ERR_MEMORY, -1)); + pkg->name_hash = _alpm_hash_sdbm(pkg->name); + + free(tmp); + return(0); +} + +/** + * Hash the given string to an unsigned long value. + * This is the standard sdbm hashing algorithm. + * @param str string to hash + * @return the hash value of the given string + */ +unsigned long _alpm_hash_sdbm(const char *str) +{ + unsigned long hash = 0; + int c; + + if(!str) { + return(hash); + } + while((c = *str++)) { + hash = c + (hash << 6) + (hash << 16) - hash; + } - return(line); + return(hash); } /* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/util.h b/lib/libalpm/util.h index 8a3154a7..543643b1 100644 --- a/lib/libalpm/util.h +++ b/lib/libalpm/util.h @@ -27,6 +27,7 @@ #include "config.h" #include "alpm_list.h" +#include "package.h" /* pmpkg_t */ #include <stdio.h> #include <string.h> @@ -58,24 +59,42 @@ _alpm_log(PM_LOG_DEBUG, "returning error %d from %s : %s\n", err, __func__, alpm_strerrorlast()); \ return(ret); } while(0) +/** + * Used as a buffer/state holder for _alpm_archive_fgets(). + */ +struct archive_read_buffer { + char *line; + char *line_offset; + size_t line_size; + size_t max_line_size; + + char *block; + char *block_offset; + size_t block_size; + + int ret; +}; + int _alpm_makepath(const char *path); int _alpm_makepath_mode(const char *path, mode_t mode); int _alpm_copyfile(const char *src, const char *dest); char *_alpm_strtrim(char *str); -int _alpm_lckmk(); -int _alpm_lckrm(); +int _alpm_lckmk(void); +int _alpm_lckrm(void); int _alpm_unpack_single(const char *archive, const char *prefix, const char *fn); int _alpm_unpack(const char *archive, const char *prefix, alpm_list_t *list, int breakfirst); int _alpm_rmrf(const char *path); int _alpm_logaction(int usesyslog, FILE *f, const char *fmt, va_list args); -int _alpm_run_chroot(const char *root, const char *cmd); +int _alpm_run_chroot(const char *root, const char *path, char *const argv[]); int _alpm_ldconfig(const char *root); int _alpm_str_cmp(const void *s1, const void *s2); char *_alpm_filecache_find(const char *filename); const char *_alpm_filecache_setup(void); int _alpm_lstat(const char *path, struct stat *buf); int _alpm_test_md5sum(const char *filepath, const char *md5sum); -char *_alpm_archive_fgets(char *line, size_t size, struct archive *a); +int _alpm_archive_fgets(struct archive *a, struct archive_read_buffer *b); +int _alpm_splitname(const char *target, pmpkg_t *pkg); +unsigned long _alpm_hash_sdbm(const char *str); #ifndef HAVE_STRSEP char *strsep(char **, const char *); @@ -85,9 +104,6 @@ char *strsep(char **, const char *); #define SYMEXPORT __attribute__((visibility("default"))) #define SYMHIDDEN __attribute__((visibility("internal"))) -/* max percent of package size to download deltas */ -#define MAX_DELTA_RATIO 0.7 - #endif /* _ALPM_UTIL_H */ /* vim: set ts=2 sw=2 noet: */ |