index : pacman | |
Archlinux32 fork of pacman | gitolite user |
summaryrefslogtreecommitdiff |
diff --git a/lib/libalpm/Makefile.am b/lib/libalpm/Makefile.am index 3473a73a..1bda5714 100644 --- a/lib/libalpm/Makefile.am +++ b/lib/libalpm/Makefile.am @@ -25,27 +25,33 @@ 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 \ + pkghash.h pkghash.c \ remove.h remove.c \ sync.h sync.c \ trans.h trans.c \ 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..702b12e9 100644 --- a/lib/libalpm/add.c +++ b/lib/libalpm/add.c @@ -1,7 +1,7 @@ /* * add.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 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 @@ -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" @@ -50,64 +49,92 @@ #include "remove.h" #include "handle.h" -/** Add a file target to the transaction. - * @param target the name of the file target to add +/** Add a package to the transaction. + * @param pkg the package 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_pkg(pmpkg_t *pkg) { - pmpkg_t *pkg = NULL; const char *pkgname, *pkgver; - alpm_list_t *i; pmtrans_t *trans; + pmdb_t *db_local; + pmpkg_t *local; ALPM_LOG_FUNC; /* Sanity checks */ - ASSERT(target != NULL && strlen(target) != 0, RET_ERR(PM_ERR_WRONG_ARGS, -1)); + ASSERT(pkg != NULL, RET_ERR(PM_ERR_WRONG_ARGS, -1)); ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1)); trans = handle->trans; ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1)); ASSERT(trans->state == STATE_INITIALIZED, RET_ERR(PM_ERR_TRANS_NOT_INITIALIZED, -1)); ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1)); + db_local = handle->db_local; - _alpm_log(PM_LOG_DEBUG, "loading target '%s'\n", target); - - if(alpm_pkg_load(target, 1, &pkg) != 0) { - goto error; - } pkgname = alpm_pkg_get_name(pkg); pkgver = alpm_pkg_get_version(pkg); - /* check if an older version of said package is already in transaction - * packages. if so, replace it in the list */ - for(i = trans->add; i; i = i->next) { - pmpkg_t *transpkg = i->data; - if(strcmp(transpkg->name, pkgname) == 0) { - if(alpm_pkg_vercmp(transpkg->version, pkgver) < 0) { - _alpm_log(PM_LOG_WARNING, - _("replacing older version %s-%s by %s in target list\n"), - transpkg->name, transpkg->version, pkgver); - _alpm_pkg_free(i->data); - i->data = pkg; + _alpm_log(PM_LOG_DEBUG, "adding package '%s'\n", pkgname); + + if(_alpm_pkg_find(trans->add, pkgname)) { + RET_ERR(PM_ERR_TRANS_DUP_TARGET, -1); + } + + local = _alpm_db_get_pkgfromcache(db_local, pkgname); + if(local) { + const char *localpkgname = alpm_pkg_get_name(local); + const char *localpkgver = alpm_pkg_get_version(local); + int cmp = _alpm_pkg_compare_versions(pkg, local); + + if(cmp == 0) { + if(trans->flags & PM_TRANS_FLAG_NEEDED) { + /* with the NEEDED flag, packages up to date are not reinstalled */ + _alpm_log(PM_LOG_WARNING, _("%s-%s is up to date -- skipping\n"), + localpkgname, localpkgver); + return(0); } else { - _alpm_log(PM_LOG_WARNING, - _("skipping %s-%s because newer version %s is in target list\n"), - pkgname, pkgver, transpkg->version); - _alpm_pkg_free(pkg); + _alpm_log(PM_LOG_WARNING, _("%s-%s is up to date -- reinstalling\n"), + localpkgname, localpkgver); } - return(0); + } else if(cmp < 0) { + /* local version is newer */ + _alpm_log(PM_LOG_WARNING, _("downgrading package %s (%s => %s)\n"), + localpkgname, localpkgver, pkgver); } } /* add the package to the transaction */ + pkg->reason = PM_PKG_REASON_EXPLICIT; + _alpm_log(PM_LOG_DEBUG, "adding package %s-%s to the transaction targets\n", + pkgname, pkgver); trans->add = alpm_list_add(trans->add, pkg); return(0); +} + +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; -error: - _alpm_pkg_free(pkg); - return(-1); + 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, @@ -120,9 +147,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); @@ -161,14 +185,6 @@ static int extract_single_file(struct archive *archive, return(0); } - /* if a file is in the add skiplist we never extract it */ - if(alpm_list_find_str(trans->skip_add, filename)) { - _alpm_log(PM_LOG_DEBUG, "%s is in trans->skip_add, skipping extraction\n", - entryname); - archive_read_data_skip(archive); - return(0); - } - /* Check for file existence. This is one of the more crucial parts * to get 'right'. Here are the possibilities, with the filesystem * on the left and the package on the top: @@ -277,18 +293,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 +438,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); } @@ -473,8 +472,8 @@ static int extract_single_file(struct archive *archive, return(errors); } -static int commit_single_pkg(pmpkg_t *newpkg, int pkg_current, int pkg_count, - pmtrans_t *trans, pmdb_t *db) +static int commit_single_pkg(pmpkg_t *newpkg, size_t pkg_current, + size_t pkg_count, pmtrans_t *trans, pmdb_t *db) { int i, ret = 0, errors = 0; char scriptlet[PATH_MAX+1]; @@ -498,7 +497,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 +543,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 +555,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 +579,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) { @@ -595,31 +600,31 @@ static int commit_single_pkg(pmpkg_t *newpkg, int pkg_current, int pkg_count, } for(i = 0; archive_read_next_header(archive, &entry) == ARCHIVE_OK; i++) { - double percent; + int percent; if(newpkg->size != 0) { /* Using compressed size for calculations here, as newpkg->isize is not * exact when it comes to comparing to the ACTUAL uncompressed size * (missing metadata sizes) */ int64_t pos = archive_position_compressed(archive); - percent = (double)pos / (double)newpkg->size; + percent = (pos * 100) / newpkg->size; _alpm_log(PM_LOG_DEBUG, "decompression progress: " - "%f%% (%"PRId64" / %jd)\n", - percent*100.0, pos, (intmax_t)newpkg->size); - if(percent >= 1.0) { - percent = 1.0; + "%d%% (%"PRId64" / %jd)\n", + percent, pos, (intmax_t)newpkg->size); + if(percent >= 100) { + percent = 100; } } else { - percent = 0.0; + percent = 0; } if(is_upgrade) { PROGRESS(trans, PM_TRANS_PROGRESS_UPGRADE_START, - alpm_pkg_get_name(newpkg), (int)(percent * 100), pkg_count, + alpm_pkg_get_name(newpkg), percent, pkg_count, pkg_current); } else { PROGRESS(trans, PM_TRANS_PROGRESS_ADD_START, - alpm_pkg_get_name(newpkg), (int)(percent * 100), pkg_count, + alpm_pkg_get_name(newpkg), percent, pkg_count, pkg_current); } @@ -629,9 +634,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 +661,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", @@ -705,7 +710,8 @@ cleanup: int _alpm_upgrade_packages(pmtrans_t *trans, pmdb_t *db) { - int pkg_count, pkg_current; + size_t pkg_count, pkg_current; + int skip_ldconfig = 0, ret = 0; alpm_list_t *targ; ALPM_LOG_FUNC; @@ -723,18 +729,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/add.h b/lib/libalpm/add.h index eb37dc78..afc7be26 100644 --- a/lib/libalpm/add.h +++ b/lib/libalpm/add.h @@ -1,7 +1,7 @@ /* * add.h * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 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 diff --git a/lib/libalpm/alpm.c b/lib/libalpm/alpm.c index 51b9e25b..4f95832d 100644 --- a/lib/libalpm/alpm.c +++ b/lib/libalpm/alpm.c @@ -1,7 +1,7 @@ /* * alpm.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> * Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu> @@ -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" @@ -49,11 +54,21 @@ int SYMEXPORT alpm_initialize(void) if(handle == NULL) { RET_ERR(PM_ERR_MEMORY, -1); } + if(_alpm_db_register_local() == NULL) { + /* error code should be set */ + _alpm_handle_free(handle); + handle = NULL; + return(-1); + } #ifdef ENABLE_NLS bindtextdomain("libalpm", LOCALEDIR); #endif +#ifdef HAVE_LIBFETCH + fetchConnectionCacheInit(5, 1); +#endif + return(0); } @@ -73,6 +88,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..0f3b7166 100644 --- a/lib/libalpm/alpm.h +++ b/lib/libalpm/alpm.h @@ -1,7 +1,7 @@ /* * alpm.h * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> * Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu> @@ -73,8 +73,8 @@ typedef enum _pmloglevel_t { PM_LOG_FUNCTION = (1 << 3) } pmloglevel_t; -typedef void (*alpm_cb_log)(pmloglevel_t, char *, va_list); -int alpm_logaction(char *fmt, ...); +typedef void (*alpm_cb_log)(pmloglevel_t, const char *, va_list); +int alpm_logaction(const char *fmt, ...); /* * Downloading @@ -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 @@ -171,8 +174,6 @@ typedef enum _pmpkgreason_t { * Databases */ -/* Preferred interfaces db_register_local and db_register_sync */ -pmdb_t *alpm_db_register_local(void); pmdb_t *alpm_db_register_sync(const char *treename); int alpm_db_unregister(pmdb_t *db); int alpm_db_unregister_all(void); @@ -235,7 +236,6 @@ 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); off_t alpm_pkg_download_size(pmpkg_t *newpkg); alpm_list_t *alpm_pkg_unused_deltas(pmpkg_t *pkg); @@ -255,6 +255,7 @@ off_t alpm_delta_get_size(pmdelta_t *delta); */ const char *alpm_grp_get_name(const pmgrp_t *grp); alpm_list_t *alpm_grp_get_pkgs(const pmgrp_t *grp); +alpm_list_t *alpm_find_grp_pkgs(alpm_list_t *dbs, const char *name); /* * Sync @@ -272,7 +273,7 @@ typedef enum _pmtransflag_t { PM_TRANS_FLAG_NODEPS = 1, PM_TRANS_FLAG_FORCE = (1 << 1), PM_TRANS_FLAG_NOSAVE = (1 << 2), - /* (1 << 3) flag can go here */ + PM_TRANS_FLAG_NODEPVERSION = (1 << 3), PM_TRANS_FLAG_CASCADE = (1 << 4), PM_TRANS_FLAG_RECURSE = (1 << 5), PM_TRANS_FLAG_DBONLY = (1 << 6), @@ -368,6 +369,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; /*@}*/ @@ -379,6 +384,7 @@ typedef enum _pmtransconv_t { PM_TRANS_CONV_CORRUPTED_PKG = (1 << 3), PM_TRANS_CONV_LOCAL_NEWER = (1 << 4), PM_TRANS_CONV_REMOVE_PKGS = (1 << 5), + PM_TRANS_CONV_SELECT_PROVIDER = (1 << 6), } pmtransconv_t; /* Transaction Progress */ @@ -386,7 +392,9 @@ 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, + PM_TRANS_PROGRESS_INTEGRITY_START, } pmtransprog_t; /* Transaction Event callback */ @@ -397,11 +405,11 @@ typedef void (*alpm_trans_cb_conv)(pmtransconv_t, void *, void *, void *, int *); /* Transaction Progress callback */ -typedef void (*alpm_trans_cb_progress)(pmtransprog_t, const char *, int, int, int); +typedef void (*alpm_trans_cb_progress)(pmtransprog_t, const char *, int, size_t, size_t); -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,8 @@ 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_add_pkg(pmpkg_t *pkg); +int alpm_remove_pkg(pmpkg_t *pkg); /* * Dependencies and conflicts @@ -429,10 +435,10 @@ 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); +pmpkg_t *alpm_find_dbs_satisfier(alpm_list_t *dbs, const char *depstring); const char *alpm_miss_get_target(const pmdepmissing_t *miss); pmdepend_t *alpm_miss_get_dep(pmdepmissing_t *miss); @@ -480,6 +486,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, @@ -490,6 +497,7 @@ enum _pmerrno_t { PM_ERR_DB_NULL, PM_ERR_DB_NOT_NULL, PM_ERR_DB_NOT_FOUND, + PM_ERR_DB_VERSION, PM_ERR_DB_WRITE, PM_ERR_DB_REMOVE, /* Servers */ @@ -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/alpm_list.c b/lib/libalpm/alpm_list.c index 80ba1ee7..4cab665f 100644 --- a/lib/libalpm/alpm_list.c +++ b/lib/libalpm/alpm_list.c @@ -1,7 +1,7 @@ /* * alpm_list.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 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 @@ -269,7 +269,7 @@ alpm_list_t SYMEXPORT *alpm_list_mmerge(alpm_list_t *left, alpm_list_t *right, a * * @return the resultant list */ -alpm_list_t SYMEXPORT *alpm_list_msort(alpm_list_t *list, int n, alpm_list_fn_cmp fn) +alpm_list_t SYMEXPORT *alpm_list_msort(alpm_list_t *list, size_t n, alpm_list_fn_cmp fn) { if (n > 1) { alpm_list_t *left = list; @@ -287,6 +287,53 @@ alpm_list_t SYMEXPORT *alpm_list_msort(alpm_list_t *list, int n, alpm_list_fn_cm /** * @brief Remove an item from the list. + * item is not freed; this is the respnsiblity of the caller. + * + * @param haystack the list to remove the item from + * @param item the item to remove from the list + * + * @return the resultant list + */ +alpm_list_t SYMEXPORT *alpm_list_remove_item(alpm_list_t *haystack, + alpm_list_t *item) +{ + if(haystack == NULL || item == NULL) { + return(haystack); + } + + if(item == haystack) { + /* Special case: removing the head node which has a back reference to + * the tail node */ + haystack = item->next; + if(haystack) { + haystack->prev = item->prev; + } + item->prev = NULL; + } else if(item == haystack->prev) { + /* Special case: removing the tail node, so we need to fix the back + * reference on the head node. We also know tail != head. */ + if(item->prev) { + /* i->next should always be null */ + item->prev->next = item->next; + haystack->prev = item->prev; + item->prev = NULL; + } + } else { + /* Normal case, non-head and non-tail node */ + if(item->next) { + item->next->prev = item->prev; + } + if(item->prev) { + item->prev->next = item->next; + } + } + + return(haystack); +} + + +/** + * @brief Remove an item from the list. * * @param haystack the list to remove the item from * @param needle the data member of the item we're removing @@ -295,9 +342,10 @@ alpm_list_t SYMEXPORT *alpm_list_msort(alpm_list_t *list, int n, alpm_list_fn_cm * * @return the resultant list */ -alpm_list_t SYMEXPORT *alpm_list_remove(alpm_list_t *haystack, const void *needle, alpm_list_fn_cmp fn, void **data) +alpm_list_t SYMEXPORT *alpm_list_remove(alpm_list_t *haystack, + const void *needle, alpm_list_fn_cmp fn, void **data) { - alpm_list_t *i = haystack, *tmp = NULL; + alpm_list_t *i = haystack; if(data) { *data = NULL; @@ -312,44 +360,16 @@ alpm_list_t SYMEXPORT *alpm_list_remove(alpm_list_t *haystack, const void *needl i = i->next; continue; } - tmp = i->next; if(fn(i->data, needle) == 0) { - /* we found a matching item */ - if(i == haystack) { - /* Special case: removing the head node which has a back reference to - * the tail node */ - haystack = i->next; - if(haystack) { - haystack->prev = i->prev; - } - i->prev = NULL; - } else if(i == haystack->prev) { - /* Special case: removing the tail node, so we need to fix the back - * reference on the head node. We also know tail != head. */ - if(i->prev) { - /* i->next should always be null */ - i->prev->next = i->next; - haystack->prev = i->prev; - i->prev = NULL; - } - } else { - /* Normal case, non-head and non-tail node */ - if(i->next) { - i->next->prev = i->prev; - } - if(i->prev) { - i->prev->next = i->next; - } - } + haystack = alpm_list_remove_item(haystack, i); if(data) { *data = i->data; } - i->data = NULL; free(i); - i = NULL; + break; } else { - i = tmp; + i = i->next; } } @@ -511,7 +531,7 @@ inline alpm_list_t SYMEXPORT *alpm_list_first(const alpm_list_t *list) * * @return an alpm_list_t node for index `n` */ -alpm_list_t SYMEXPORT *alpm_list_nth(const alpm_list_t *list, int n) +alpm_list_t SYMEXPORT *alpm_list_nth(const alpm_list_t *list, size_t n) { const alpm_list_t *i = list; while(n--) { @@ -574,9 +594,9 @@ void SYMEXPORT *alpm_list_getdata(const alpm_list_t *node) * * @return the number of list items */ -int SYMEXPORT alpm_list_count(const alpm_list_t *list) +size_t SYMEXPORT alpm_list_count(const alpm_list_t *list) { - unsigned int i = 0; + size_t i = 0; const alpm_list_t *lp = list; while(lp) { ++i; diff --git a/lib/libalpm/alpm_list.h b/lib/libalpm/alpm_list.h index bd639f7d..1f6393a6 100644 --- a/lib/libalpm/alpm_list.h +++ b/lib/libalpm/alpm_list.h @@ -1,7 +1,7 @@ /* * alpm_list.h * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 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 @@ -56,7 +56,8 @@ alpm_list_t *alpm_list_add(alpm_list_t *list, void *data); alpm_list_t *alpm_list_add_sorted(alpm_list_t *list, void *data, alpm_list_fn_cmp fn); alpm_list_t *alpm_list_join(alpm_list_t *first, alpm_list_t *second); alpm_list_t *alpm_list_mmerge(alpm_list_t *left, alpm_list_t *right, alpm_list_fn_cmp fn); -alpm_list_t *alpm_list_msort(alpm_list_t *list, int n, alpm_list_fn_cmp fn); +alpm_list_t *alpm_list_msort(alpm_list_t *list, size_t n, alpm_list_fn_cmp fn); +alpm_list_t *alpm_list_remove_item(alpm_list_t *haystack, alpm_list_t *item); alpm_list_t *alpm_list_remove(alpm_list_t *haystack, const void *needle, alpm_list_fn_cmp fn, void **data); alpm_list_t *alpm_list_remove_str(alpm_list_t *haystack, const char *needle, char **data); alpm_list_t *alpm_list_remove_dupes(const alpm_list_t *list); @@ -67,13 +68,13 @@ alpm_list_t *alpm_list_reverse(alpm_list_t *list); /* item accessors */ alpm_list_t *alpm_list_first(const alpm_list_t *list); -alpm_list_t *alpm_list_nth(const alpm_list_t *list, int n); +alpm_list_t *alpm_list_nth(const alpm_list_t *list, size_t n); alpm_list_t *alpm_list_next(const alpm_list_t *list); alpm_list_t *alpm_list_last(const alpm_list_t *list); void *alpm_list_getdata(const alpm_list_t *entry); /* misc */ -int alpm_list_count(const alpm_list_t *list); +size_t alpm_list_count(const alpm_list_t *list); void *alpm_list_find(const alpm_list_t *haystack, const void *needle, alpm_list_fn_cmp fn); void *alpm_list_find_ptr(const alpm_list_t *haystack, const void *needle); char *alpm_list_find_str(const alpm_list_t *haystack, const char *needle); diff --git a/lib/libalpm/backup.c b/lib/libalpm/backup.c index 2ef65a2b..ca955ca4 100644 --- a/lib/libalpm/backup.c +++ b/lib/libalpm/backup.c @@ -1,7 +1,7 @@ /* * backup.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2005 by Judd Vinet <jvinet@zeroflux.org> * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> * Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu> diff --git a/lib/libalpm/backup.h b/lib/libalpm/backup.h index 25469b90..9475aa2a 100644 --- a/lib/libalpm/backup.h +++ b/lib/libalpm/backup.h @@ -1,7 +1,7 @@ /* * backup.h * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 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 diff --git a/lib/libalpm/be_files.c b/lib/libalpm/be_files.c deleted file mode 100644 index 0ee8a3bb..00000000 --- a/lib/libalpm/be_files.c +++ /dev/null @@ -1,977 +0,0 @@ -/* - * be_files.c - * - * Copyright (c) 2006 by Christian Hamar <krics@linuxforum.hu> - * Copyright (c) 2006 by Miklos Vajna <vmiklos@frugalware.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 <unistd.h> -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <string.h> -#include <stdint.h> /* intmax_t */ -#include <sys/stat.h> -#include <dirent.h> -#include <ctype.h> -#include <time.h> -#include <limits.h> /* PATH_MAX */ -#include <locale.h> /* setlocale */ - -/* libarchive */ -#include <archive.h> -#include <archive_entry.h> - -/* 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 "deps.h" -#include "dload.h" - - -static int checkdbdir(pmdb_t *db) -{ - struct stat buf; - const char *path = _alpm_db_path(db); - - 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) -{ - struct archive *_archive; - struct archive_entry *entry; - - 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, 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); - } - - while(archive_read_next_header(_archive, &entry) == ARCHIVE_OK) { - const struct stat *st; - const char *entryname; /* the name of the file in the archive */ - - st = archive_entry_stat(entry); - entryname = archive_entry_pathname(entry); - - if(S_ISDIR(st->st_mode)) { - char *name = strdup(entryname); - *dirlist = alpm_list_add(*dirlist, name); - } - } - archive_read_finish(_archive); - - *dirlist = alpm_list_msort(*dirlist, alpm_list_count(*dirlist), _alpm_str_cmp); - return(0); -} - -/* create list of directories in db */ -static int dirlist_from_fs(const char *syncdbpath, alpm_list_t **dirlist) -{ - DIR *dbdir; - struct dirent *ent = NULL; - struct stat sbuf; - char path[PATH_MAX]; - - dbdir = opendir(syncdbpath); - if (dbdir != NULL) { - while((ent = readdir(dbdir)) != NULL) { - char *name = ent->d_name; - size_t len; - char *entry; - - if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { - continue; - } - - /* 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; - } - - 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); - } - - *dirlist = alpm_list_msort(*dirlist, alpm_list_count(*dirlist), _alpm_str_cmp); - return(0); -} - -/* remove old directories from dbdir */ -static int remove_olddir(const char *syncdbpath, alpm_list_t *dirlist) -{ - 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); -} - -/** 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, *dbfilepath; - const char *dbpath, *syncdbpath; - alpm_list_t *newdirlist = NULL, *olddirlist = NULL; - alpm_list_t *onlynew = NULL, *onlyold = NULL; - 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)); - /* 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); - } - - len = strlen(db->treename) + strlen(DBEXT) + 1; - MALLOC(dbfile, len, RET_ERR(PM_ERR_MEMORY, -1)); - sprintf(dbfile, "%s" DBEXT, db->treename); - - dbpath = alpm_option_get_dbpath(); - - ret = _alpm_download_single_file(dbfile, db->servers, dbpath, force); - free(dbfile); - - 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); - } - - 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; - } - - alpm_list_diff_sorted(olddirlist, newdirlist, _alpm_str_cmp, &onlyold, &onlynew); - - ret = remove_olddir(syncdbpath, onlyold); - if(ret) { - goto cleanup; - } - } - - /* Cache needs to be rebuilt */ - _alpm_db_free_pkgcache(db); - - checkdbdir(db); - ret = _alpm_unpack(dbfilepath, syncdbpath, onlynew, 0); - -cleanup: - FREELIST(newdirlist); - FREELIST(olddirlist); - alpm_list_free(onlynew); - alpm_list_free(onlyold); - - free(dbfilepath); - - if(ret) { - RET_ERR(PM_ERR_SYSTEM, -1); - } - - return(0); -} - - -static int 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); - - /* 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)); - - free(tmp); - return(0); -} - -int _alpm_db_populate(pmdb_t *db) -{ - int count = 0; - struct dirent *ent = NULL; - struct stat sbuf; - char path[PATH_MAX]; - const char *dbpath; - DIR *dbdir; - - ALPM_LOG_FUNC; - - ASSERT(db != NULL, RET_ERR(PM_ERR_DB_NULL, -1)); - - dbpath = _alpm_db_path(db); - 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)) { - continue; - } - - pkg = _alpm_pkg_new(); - if(pkg == NULL) { - closedir(dbdir); - return(-1); - } - /* split the db entry name */ - if(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_CACHE; - pkg->origin_data.db = db; - - /* explicitly read with only 'BASE' data, accessors will handle the rest */ - if(_alpm_db_read(db, pkg, INFRQ_BASE) == -1) { - _alpm_log(PM_LOG_ERROR, _("corrupted database entry '%s'\n"), name); - _alpm_pkg_free(pkg); - continue; - } - - /* 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++; - } - - closedir(dbdir); - db->pkgcache = alpm_list_msort(db->pkgcache, count, _alpm_pkg_cmp); - return(count); -} - -/* Note: the return value must be freed by the caller */ -static char *get_pkgpath(pmdb_t *db, pmpkg_t *info) -{ - size_t len; - char *pkgpath; - const char *dbpath; - - dbpath = _alpm_db_path(db); - len = strlen(dbpath) + strlen(info->name) + strlen(info->version) + 3; - MALLOC(pkgpath, len, RET_ERR(PM_ERR_MEMORY, NULL)); - sprintf(pkgpath, "%s%s-%s/", dbpath, info->name, info->version); - return(pkgpath); -} - -int _alpm_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 *pkgpath = NULL; - - ALPM_LOG_FUNC; - - if(db == NULL) { - RET_ERR(PM_ERR_DB_NULL, -1); - } - - if(info == NULL || info->name == NULL || info->version == NULL) { - _alpm_log(PM_LOG_DEBUG, "invalid package entry provided to _alpm_db_read, skipping\n"); - return(-1); - } - - if(info->origin == PKG_FROM_FILE) { - _alpm_log(PM_LOG_DEBUG, "request to read database info for a file-based package '%s', skipping...\n", info->name); - return(-1); - } - - /* bitmask logic here: - * infolevel: 00001111 - * inforeq: 00010100 - * & result: 00000100 - * == to inforeq? nope, we need to load more info. */ - if((info->infolevel & inforeq) == inforeq) { - /* already loaded all of this info, do nothing */ - return(0); - } - _alpm_log(PM_LOG_FUNCTION, "loading package data for %s : level=0x%x\n", - info->name, inforeq); - - /* clear out 'line', to be certain - and to make valgrind happy */ - memset(line, 0, sline+1); - - pkgpath = get_pkgpath(db, info); - - if(access(pkgpath, F_OK)) { - /* directory doesn't exist or can't be opened */ - _alpm_log(PM_LOG_DEBUG, "cannot find '%s-%s' in db '%s'\n", - info->name, info->version, db->treename); - goto error; - } - - /* DESC */ - if(inforeq & INFRQ_DESC && !(info->infolevel & INFRQ_DESC)) { - snprintf(path, PATH_MAX, "%sdesc", 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)) { - if(fgets(line, 256, fp) == NULL) { - break; - } - _alpm_strtrim(line); - if(strcmp(line, "%NAME%") == 0) { - if(fgets(line, sline, fp) == NULL) { - goto error; - } - if(strcmp(_alpm_strtrim(line), info->name) != 0) { - _alpm_log(PM_LOG_ERROR, _("%s database is inconsistent: name " - "mismatch on package %s\n"), db->treename, info->name); - } - } else if(strcmp(line, "%VERSION%") == 0) { - if(fgets(line, sline, 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) { - 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))) { - 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) { - 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))) { - 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) { - goto error; - } - STRDUP(info->arch, _alpm_strtrim(line), goto error); - } else if(strcmp(line, "%BUILDDATE%") == 0) { - if(fgets(line, sline, fp) == NULL) { - goto error; - } - _alpm_strtrim(line); - - char first = tolower((unsigned char)line[0]); - if(first > 'a' && first < 'z') { - struct tm tmp_tm = {0}; /* initialize to null in case of failure */ - setlocale(LC_TIME, "C"); - strptime(line, "%a %b %e %H:%M:%S %Y", &tmp_tm); - info->builddate = mktime(&tmp_tm); - setlocale(LC_TIME, ""); - } else { - info->builddate = atol(line); - } - } else if(strcmp(line, "%INSTALLDATE%") == 0) { - if(fgets(line, sline, fp) == NULL) { - goto error; - } - _alpm_strtrim(line); - - char first = tolower((unsigned char)line[0]); - if(first > 'a' && first < 'z') { - struct tm tmp_tm = {0}; /* initialize to null in case of failure */ - setlocale(LC_TIME, "C"); - strptime(line, "%a %b %e %H:%M:%S %Y", &tmp_tm); - info->installdate = mktime(&tmp_tm); - setlocale(LC_TIME, ""); - } else { - info->installdate = atol(line); - } - } else if(strcmp(line, "%PACKAGER%") == 0) { - if(fgets(line, sline, fp) == NULL) { - goto error; - } - STRDUP(info->packager, _alpm_strtrim(line), goto error); - } else if(strcmp(line, "%REASON%") == 0) { - if(fgets(line, sline, fp) == NULL) { - goto error; - } - info->reason = (pmpkgreason_t)atol(_alpm_strtrim(line)); - } else if(strcmp(line, "%SIZE%") == 0 || 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. - */ - if(fgets(line, sline, 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; - } - } 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) { - 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; - } - STRDUP(info->md5sum, _alpm_strtrim(line), goto error); - } else if(strcmp(line, "%REPLACES%") == 0) { - while(fgets(line, sline, 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, "%FORCE%") == 0) { - info->force = 1; - } - } - fclose(fp); - fp = NULL; - } - - /* FILES */ - if(inforeq & INFRQ_FILES && !(info->infolevel & INFRQ_FILES)) { - snprintf(path, PATH_MAX, "%sfiles", 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(fgets(line, 256, fp)) { - _alpm_strtrim(line); - if(strcmp(line, "%FILES%") == 0) { - while(fgets(line, sline, 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))) { - char *linedup; - STRDUP(linedup, _alpm_strtrim(line), goto error); - info->backup = alpm_list_add(info->backup, linedup); - } - } - } - fclose(fp); - fp = NULL; - } - - /* DEPENDS */ - if(inforeq & INFRQ_DEPENDS && !(info->infolevel & 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 && !(info->infolevel & 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 && !(info->infolevel & INFRQ_SCRIPTLET)) { - snprintf(path, PATH_MAX, "%sinstall", pkgpath); - if(access(path, F_OK) == 0) { - info->scriptlet = 1; - } - } - - /* internal */ - info->infolevel |= inforeq; - - free(pkgpath); - return(0); - -error: - free(pkgpath); - if(fp) { - fclose(fp); - } - return(-1); -} - -int _alpm_db_prepare(pmdb_t *db, pmpkg_t *info) -{ - mode_t oldmask; - int retval = 0; - char *pkgpath = NULL; - - if(checkdbdir(db) != 0) { - return(-1); - } - - oldmask = umask(0000); - pkgpath = get_pkgpath(db, info); - - if((retval = mkdir(pkgpath, 0755)) != 0) { - _alpm_log(PM_LOG_ERROR, _("could not create directory %s: %s\n"), - pkgpath, strerror(errno)); - } - - free(pkgpath); - umask(oldmask); - - return(retval); -} - -int _alpm_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; - - if(db == NULL || info == NULL) { - return(-1); - } - - pkgpath = get_pkgpath(db, info); - - /* make sure we have a sane umask */ - oldmask = umask(0022); - - if(strcmp(db->treename, "local") == 0) { - local = 1; - } - - /* DESC */ - if(inforeq & INFRQ_DESC) { - _alpm_log(PM_LOG_DEBUG, "writing %s-%s DESC information back to db\n", - info->name, info->version); - snprintf(path, PATH_MAX, "%sdesc", 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; - } - fprintf(fp, "%%NAME%%\n%s\n\n" - "%%VERSION%%\n%s\n\n", info->name, info->version); - if(info->desc) { - fprintf(fp, "%%DESC%%\n" - "%s\n\n", info->desc); - } - if(info->groups) { - fputs("%GROUPS%\n", fp); - for(lp = info->groups; lp; lp = lp->next) { - fprintf(fp, "%s\n", (char *)lp->data); - } - fprintf(fp, "\n"); - } - if(info->replaces) { - fputs("%REPLACES%\n", fp); - for(lp = info->replaces; lp; lp = lp->next) { - fprintf(fp, "%s\n", (char *)lp->data); - } - fprintf(fp, "\n"); - } - if(local) { - 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); - } - } - 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->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; - } - - /* 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->depends) { - fputs("%DEPENDS%\n", fp); - for(lp = info->depends; lp; lp = lp->next) { - char *depstring = alpm_dep_compute_string(lp->data); - fprintf(fp, "%s\n", depstring); - free(depstring); - } - fprintf(fp, "\n"); - } - if(info->optdepends) { - fputs("%OPTDEPENDS%\n", fp); - for(lp = info->optdepends; lp; lp = lp->next) { - fprintf(fp, "%s\n", (char *)lp->data); - } - fprintf(fp, "\n"); - } - if(info->conflicts) { - fputs("%CONFLICTS%\n", fp); - for(lp = info->conflicts; lp; lp = lp->next) { - fprintf(fp, "%s\n", (char *)lp->data); - } - fprintf(fp, "\n"); - } - if(info->provides) { - fputs("%PROVIDES%\n", fp); - for(lp = info->provides; lp; lp = lp->next) { - fprintf(fp, "%s\n", (char *)lp->data); - } - fprintf(fp, "\n"); - } - fclose(fp); - fp = NULL; - } - - /* INSTALL */ - /* nothing needed here (script is automatically extracted) */ - -cleanup: - umask(oldmask); - free(pkgpath); - - if(fp) { - fclose(fp); - } - - return(retval); -} - -int _alpm_db_remove(pmdb_t *db, pmpkg_t *info) -{ - int ret = 0; - char *pkgpath = NULL; - - ALPM_LOG_FUNC; - - if(db == NULL || info == NULL) { - RET_ERR(PM_ERR_DB_NULL, -1); - } - - pkgpath = get_pkgpath(db, info); - - ret = _alpm_rmrf(pkgpath); - free(pkgpath); - if(ret != 0) { - ret = -1; - } - return(ret); -} - -/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/be_local.c b/lib/libalpm/be_local.c new file mode 100644 index 00000000..848ecc58 --- /dev/null +++ b/lib/libalpm/be_local.c @@ -0,0 +1,1003 @@ +/* + * be_local.c + * + * Copyright (c) 2006-2011 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 <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <stdint.h> /* intmax_t */ +#include <sys/stat.h> +#include <dirent.h> +#include <time.h> +#include <limits.h> /* PATH_MAX */ + +/* libarchive */ +#include <archive.h> +#include <archive_entry.h> + +/* libalpm */ +#include "db.h" +#include "alpm_list.h" +#include "log.h" +#include "util.h" +#include "alpm.h" +#include "handle.h" +#include "package.h" +#include "group.h" +#include "deps.h" +#include "dload.h" + + +#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) + + +/* 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) +{ + LAZY_LOAD(INFRQ_DESC, NULL); + return pkg->filename; +} + +static const char *_cache_get_name(pmpkg_t *pkg) +{ + ASSERT(pkg != NULL, return(NULL)); + return pkg->name; +} + +static const char *_cache_get_version(pmpkg_t *pkg) +{ + ASSERT(pkg != NULL, return(NULL)); + return pkg->version; +} + +static const char *_cache_get_desc(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, NULL); + return pkg->desc; +} + +static const char *_cache_get_url(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, NULL); + return pkg->url; +} + +static time_t _cache_get_builddate(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, 0); + return pkg->builddate; +} + +static time_t _cache_get_installdate(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, 0); + return pkg->installdate; +} + +static const char *_cache_get_packager(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, NULL); + return pkg->packager; +} + +static const char *_cache_get_md5sum(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, NULL); + return pkg->md5sum; +} + +static const char *_cache_get_arch(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, NULL); + return pkg->arch; +} + +static off_t _cache_get_size(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, -1); + return pkg->size; +} + +static off_t _cache_get_isize(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, -1); + return pkg->isize; +} + +static pmpkgreason_t _cache_get_reason(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, -1); + return pkg->reason; +} + +static alpm_list_t *_cache_get_licenses(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, NULL); + return pkg->licenses; +} + +static alpm_list_t *_cache_get_groups(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, NULL); + return pkg->groups; +} + +static int _cache_has_scriptlet(pmpkg_t *pkg) +{ + ALPM_LOG_FUNC; + + /* Sanity checks */ + 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; +} + +static alpm_list_t *_cache_get_conflicts(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, NULL); + return pkg->conflicts; +} + +static alpm_list_t *_cache_get_provides(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, NULL); + return pkg->provides; +} + +static alpm_list_t *_cache_get_replaces(pmpkg_t *pkg) +{ + LAZY_LOAD(INFRQ_DESC, NULL); + return pkg->replaces; +} + +/* local packages can not have deltas */ +static alpm_list_t *_cache_get_deltas(pmpkg_t *pkg) +{ + return NULL; +} + +static alpm_list_t *_cache_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_LOCALDB + && !(pkg->infolevel & INFRQ_FILES)) { + _alpm_local_db_read(pkg->origin_data.db, pkg, INFRQ_FILES); + } + return pkg->files; +} + +static alpm_list_t *_cache_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_LOCALDB + && !(pkg->infolevel & INFRQ_FILES)) { + _alpm_local_db_read(pkg->origin_data.db, pkg, INFRQ_FILES); + } + return pkg->backup; +} + +/** + * 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; + + /* 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"); +} + +/** + * 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) ); +} + + +/** 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, + .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) +{ + struct stat buf; + const char *path = _alpm_db_path(db); + + 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); +} + +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)); + } + + return(0); +#endif +} + +static int local_db_populate(pmdb_t *db) +{ + size_t est_count; + int count = 0; + struct stat buf; + struct dirent *ent = NULL; + const char *dbpath; + DIR *dbdir; + + ALPM_LOG_FUNC; + + ASSERT(db != NULL, RET_ERR(PM_ERR_DB_NULL, -1)); + + dbpath = _alpm_db_path(db); + if(dbpath == NULL) { + RET_ERR(PM_ERR_DB_OPEN, -1); + } + dbdir = opendir(dbpath); + if(dbdir == NULL) { + if(errno == ENOENT) { + /* no database existing yet is not an error */ + return(0); + } + RET_ERR(PM_ERR_DB_OPEN, -1); + } + if(fstat(dirfd(dbdir), &buf) != 0) { + RET_ERR(PM_ERR_DB_OPEN, -1); + } + if(buf.st_nlink >= 2) { + est_count = buf.st_nlink; + } else { + /* Some filesystems don't subscribe to the two-implicit links school of + * thought, e.g. BTRFS, HFS+. See + * http://kerneltrap.org/mailarchive/linux-btrfs/2010/1/23/6723483/thread + */ + est_count = 0; + while((ent = readdir(dbdir)) != NULL) { + est_count++; + } + rewinddir(dbdir); + } + if(est_count >= 2) { + /* subtract the two extra pointers to get # of children */ + est_count -= 2; + } + + /* initialize hash at 50% full */ + db->pkgcache = _alpm_pkghash_create(est_count * 2); + if(db->pkgcache == NULL){ + closedir(dbdir); + RET_ERR(PM_ERR_MEMORY, -1); + } + + while((ent = readdir(dbdir)) != NULL) { + const char *name = ent->d_name; + + pmpkg_t *pkg; + + if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { + continue; + } + if(!is_dir(dbpath, ent)) { + continue; + } + + pkg = _alpm_pkg_new(); + if(pkg == NULL) { + closedir(dbdir); + RET_ERR(PM_ERR_MEMORY, -1); + } + /* split the db entry name */ + 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_pkghash_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_LOCALDB; + pkg->origin_data.db = db; + pkg->ops = &local_pkg_ops; + + /* explicitly read with only 'BASE' data, accessors will handle the rest */ + 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; + } + + /* 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_pkghash_add(db->pkgcache, pkg); + count++; + } + + closedir(dbdir); + if(count > 0) { + db->pkgcache->list = alpm_list_msort(db->pkgcache->list, (size_t)count, _alpm_pkg_cmp); + } + return(count); +} + +/* Note: the return value must be freed by the caller */ +static char *get_pkgpath(pmdb_t *db, pmpkg_t *info) +{ + size_t len; + char *pkgpath; + const char *dbpath; + + dbpath = _alpm_db_path(db); + len = strlen(dbpath) + strlen(info->name) + strlen(info->version) + 3; + MALLOC(pkgpath, len, RET_ERR(PM_ERR_MEMORY, NULL)); + sprintf(pkgpath, "%s%s-%s/", dbpath, info->name, info->version); + return(pkgpath); +} + + +int _alpm_local_db_read(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq) +{ + FILE *fp = NULL; + char path[PATH_MAX]; + char line[1024]; + char *pkgpath = NULL; + + ALPM_LOG_FUNC; + + if(db == NULL) { + RET_ERR(PM_ERR_DB_NULL, -1); + } + + if(info == NULL || info->name == NULL || info->version == NULL) { + _alpm_log(PM_LOG_DEBUG, "invalid package entry provided to _alpm_local_db_read, skipping\n"); + return(-1); + } + + if(info->origin != PKG_FROM_LOCALDB) { + _alpm_log(PM_LOG_DEBUG, + "request to read info for a non-local package '%s', skipping...\n", + info->name); + return(-1); + } + + /* bitmask logic here: + * infolevel: 00001111 + * inforeq: 00010100 + * & result: 00000100 + * == to inforeq? nope, we need to load more info. */ + if((info->infolevel & inforeq) == inforeq) { + /* already loaded all of this info, do nothing */ + return(0); + } + _alpm_log(PM_LOG_FUNCTION, "loading package data for %s : level=0x%x\n", + info->name, inforeq); + + /* clear out 'line', to be certain - and to make valgrind happy */ + memset(line, 0, sizeof(line)); + + pkgpath = get_pkgpath(db, info); + + if(access(pkgpath, F_OK)) { + /* directory doesn't exist or can't be opened */ + _alpm_log(PM_LOG_DEBUG, "cannot find '%s-%s' in db '%s'\n", + info->name, info->version, db->treename); + goto error; + } + + /* DESC */ + if(inforeq & INFRQ_DESC && !(info->infolevel & INFRQ_DESC)) { + snprintf(path, PATH_MAX, "%sdesc", 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)) { + if(fgets(line, sizeof(line), fp) == NULL) { + break; + } + _alpm_strtrim(line); + if(strcmp(line, "%NAME%") == 0) { + if(fgets(line, sizeof(line), fp) == NULL) { + goto error; + } + if(strcmp(_alpm_strtrim(line), info->name) != 0) { + _alpm_log(PM_LOG_ERROR, _("%s database is inconsistent: name " + "mismatch on package %s\n"), db->treename, info->name); + } + } else if(strcmp(line, "%VERSION%") == 0) { + 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, "%DESC%") == 0) { + 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, sizeof(line), fp) && strlen(_alpm_strtrim(line))) { + char *linedup; + STRDUP(linedup, line, goto error); + info->groups = alpm_list_add(info->groups, linedup); + } + } else if(strcmp(line, "%URL%") == 0) { + 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, sizeof(line), fp) && strlen(_alpm_strtrim(line))) { + char *linedup; + STRDUP(linedup, line, goto error); + info->licenses = alpm_list_add(info->licenses, linedup); + } + } else if(strcmp(line, "%ARCH%") == 0) { + 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, sizeof(line), fp) == NULL) { + goto error; + } + _alpm_strtrim(line); + info->builddate = _alpm_parsedate(line); + } else if(strcmp(line, "%INSTALLDATE%") == 0) { + if(fgets(line, sizeof(line), fp) == NULL) { + goto error; + } + _alpm_strtrim(line); + info->installdate = _alpm_parsedate(line); + } else if(strcmp(line, "%PACKAGER%") == 0) { + 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, sizeof(line), fp) == NULL) { + goto error; + } + info->reason = (pmpkgreason_t)atol(_alpm_strtrim(line)); + } 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, sizeof(line), fp) == NULL) { + goto error; + } + info->size = atol(_alpm_strtrim(line)); + /* 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, line, goto error); + info->replaces = alpm_list_add(info->replaces, linedup); + } + } else if(strcmp(line, "%DEPENDS%") == 0) { + while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) { + pmdepend_t *dep = _alpm_splitdep(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, line, goto error); + 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, 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, line, goto error); + info->provides = alpm_list_add(info->provides, linedup); + } + } + } + fclose(fp); + fp = NULL; + } + + /* FILES */ + if(inforeq & INFRQ_FILES && !(info->infolevel & INFRQ_FILES)) { + snprintf(path, PATH_MAX, "%sfiles", 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(fgets(line, sizeof(line), fp)) { + _alpm_strtrim(line); + if(strcmp(line, "%FILES%") == 0) { + while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) { + char *linedup; + STRDUP(linedup, line, goto error); + info->files = alpm_list_add(info->files, linedup); + } + } else if(strcmp(line, "%BACKUP%") == 0) { + while(fgets(line, sizeof(line), fp) && strlen(_alpm_strtrim(line))) { + char *linedup; + STRDUP(linedup, line, goto error); + info->backup = alpm_list_add(info->backup, linedup); + } + } + } + fclose(fp); + fp = NULL; + } + + /* INSTALL */ + if(inforeq & INFRQ_SCRIPTLET && !(info->infolevel & INFRQ_SCRIPTLET)) { + snprintf(path, PATH_MAX, "%sinstall", pkgpath); + if(access(path, F_OK) == 0) { + info->scriptlet = 1; + } + } + + /* internal */ + info->infolevel |= inforeq; + + free(pkgpath); + return(0); + +error: + free(pkgpath); + if(fp) { + fclose(fp); + } + return(-1); +} + +int _alpm_local_db_prepare(pmdb_t *db, pmpkg_t *info) +{ + mode_t oldmask; + int retval = 0; + char *pkgpath = NULL; + + if(checkdbdir(db) != 0) { + return(-1); + } + + oldmask = umask(0000); + pkgpath = get_pkgpath(db, info); + + if((retval = mkdir(pkgpath, 0755)) != 0) { + _alpm_log(PM_LOG_ERROR, _("could not create directory %s: %s\n"), + pkgpath, strerror(errno)); + } + + free(pkgpath); + umask(oldmask); + + return(retval); +} + +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; + char *pkgpath = NULL; + + ALPM_LOG_FUNC; + + if(db == NULL || info == NULL) { + return(-1); + } + + pkgpath = get_pkgpath(db, info); + + /* make sure we have a sane umask */ + oldmask = umask(0022); + + if(strcmp(db->treename, "local") != 0) { + return(-1); + } + + /* DESC */ + if(inforeq & INFRQ_DESC) { + _alpm_log(PM_LOG_DEBUG, "writing %s-%s DESC information back to db\n", + info->name, info->version); + snprintf(path, PATH_MAX, "%sdesc", 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; + } + fprintf(fp, "%%NAME%%\n%s\n\n" + "%%VERSION%%\n%s\n\n", info->name, info->version); + if(info->desc) { + fprintf(fp, "%%DESC%%\n" + "%s\n\n", info->desc); + } + if(info->groups) { + fputs("%GROUPS%\n", fp); + for(lp = info->groups; lp; lp = lp->next) { + fprintf(fp, "%s\n", (char *)lp->data); + } + fprintf(fp, "\n"); + } + if(info->replaces) { + fputs("%REPLACES%\n", fp); + for(lp = info->replaces; lp; lp = lp->next) { + fprintf(fp, "%s\n", (char *)lp->data); + } + fprintf(fp, "\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); + } + if(info->depends) { + fputs("%DEPENDS%\n", fp); + for(lp = info->depends; lp; lp = lp->next) { + char *depstring = alpm_dep_compute_string(lp->data); + fprintf(fp, "%s\n", depstring); + free(depstring); + } + fprintf(fp, "\n"); + } + if(info->optdepends) { + fputs("%OPTDEPENDS%\n", fp); + for(lp = info->optdepends; lp; lp = lp->next) { + fprintf(fp, "%s\n", (char *)lp->data); + } + fprintf(fp, "\n"); + } + if(info->conflicts) { + fputs("%CONFLICTS%\n", fp); + for(lp = info->conflicts; lp; lp = lp->next) { + fprintf(fp, "%s\n", (char *)lp->data); + } + fprintf(fp, "\n"); + } + if(info->provides) { + fputs("%PROVIDES%\n", fp); + for(lp = info->provides; lp; lp = lp->next) { + fprintf(fp, "%s\n", (char *)lp->data); + } + 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; + } + + /* INSTALL */ + /* nothing needed here (script is automatically extracted) */ + +cleanup: + umask(oldmask); + free(pkgpath); + + if(fp) { + fclose(fp); + } + + return(retval); +} + +int _alpm_local_db_remove(pmdb_t *db, pmpkg_t *info) +{ + int ret = 0; + char *pkgpath = NULL; + + ALPM_LOG_FUNC; + + if(db == NULL || info == NULL) { + RET_ERR(PM_ERR_DB_NULL, -1); + } + + pkgpath = get_pkgpath(db, info); + + ret = _alpm_rmrf(pkgpath); + free(pkgpath); + if(ret != 0) { + ret = -1; + } + return(ret); +} + +static int local_db_version(pmdb_t *db) +{ + struct dirent *ent = NULL; + const char *dbpath; + DIR *dbdir; + int version; + + dbpath = _alpm_db_path(db); + if(dbpath == NULL) { + RET_ERR(PM_ERR_DB_OPEN, -1); + } + dbdir = opendir(dbpath); + if(dbdir == NULL) { + if(errno == ENOENT) { + /* database dir doesn't exist yet */ + version = 2; + goto done; + } else { + RET_ERR(PM_ERR_DB_OPEN, -1); + } + } + + while((ent = readdir(dbdir)) != NULL) { + const char *name = ent->d_name; + char path[PATH_MAX]; + + if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { + continue; + } + if(!is_dir(dbpath, ent)) { + continue; + } + + snprintf(path, PATH_MAX, "%s%s/depends", dbpath, name); + if(access(path, F_OK) == 0) { + /* we found a depends file- bail */ + version = 1; + goto done; + } + } + /* we found no depends file after full scan */ + version = 2; + +done: + if(dbdir) { + closedir(dbdir); + } + + _alpm_log(PM_LOG_DEBUG, "local database version %d\n", version); + return(version); +} + +struct db_operations local_db_ops = { + .populate = local_db_populate, + .unregister = _alpm_db_unregister, + .version = local_db_version, +}; + +pmdb_t *_alpm_db_register_local(void) +{ + pmdb_t *db; + + ALPM_LOG_FUNC; + + _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..f4837890 100644 --- a/lib/libalpm/be_package.c +++ b/lib/libalpm/be_package.c @@ -1,7 +1,7 @@ /* * be_package.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 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 @@ -24,8 +24,7 @@ #include <stdlib.h> #include <string.h> #include <limits.h> -#include <ctype.h> -#include <locale.h> /* setlocale */ +#include <errno.h> /* libarchive */ #include <archive.h> @@ -39,6 +38,109 @@ #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. + */ +static struct pkg_operations *get_file_pkg_ops(void) +{ + static struct pkg_operations file_pkg_ops; + static int file_pkg_ops_initialized = 0; + if(!file_pkg_ops_initialized) { + file_pkg_ops = default_pkg_ops; + file_pkg_ops.changelog_open = _package_changelog_open; + file_pkg_ops.changelog_read = _package_changelog_read; + file_pkg_ops.changelog_close = _package_changelog_close; + file_pkg_ops_initialized = 1; + } + 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 +149,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; } @@ -68,57 +175,52 @@ static int parse_descfile(struct archive *a, pmpkg_t *newpkg) newpkg->name ? newpkg->name : "error", linenum); } else { key = _alpm_strtrim(key); + while(*ptr == ' ') ptr++; 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, "pkgbase") == 0) { + /* not used atm */ + } 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, "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")) { - char first = tolower((unsigned char)ptr[0]); - if(first > 'a' && first < 'z') { - struct tm tmp_tm = {0}; /* initialize to null in case of failure */ - setlocale(LC_TIME, "C"); - strptime(ptr, "%a %b %e %H:%M:%S %Y", &tmp_tm); - newpkg->builddate = mktime(&tmp_tm); - setlocale(LC_TIME, ""); - } else { - newpkg->builddate = atol(ptr); - } - } else if(!strcmp(key, "packager")) { + } else if(strcmp(key, "builddate") == 0) { + newpkg->builddate = _alpm_parsedate(ptr); + } 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, "force") == 0) { + /* deprecated, skip it */ + } else if(strcmp(key, "makepkgopt") == 0) { /* not used atm */ } else { - _alpm_log(PM_LOG_DEBUG, "%s: syntax error in description file line %d\n", - newpkg->name ? newpkg->name : "error", linenum); + _alpm_log(PM_LOG_DEBUG, "%s: unknown key '%s' in description file line %d\n", + newpkg->name ? newpkg->name : "error", key, linenum); } } line[0] = '\0'; @@ -174,6 +276,8 @@ static pmpkg_t *pkg_load(const char *pkgfile, int full) newpkg->filename = strdup(pkgfile); newpkg->size = st.st_size; + _alpm_log(PM_LOG_DEBUG, "starting package load for %s\n", pkgfile); + /* If full is false, only read through the archive until we find our needed * metadata. If it is true, read through the entire archive, which serves * as a verfication of integrity and allows us to create the filelist. */ @@ -202,7 +306,7 @@ static pmpkg_t *pkg_load(const char *pkgfile, int full) } else if(*entry_name == '.') { /* for now, ignore all files starting with '.' that haven't * already been handled (for future possibilities) */ - } else { + } else if(full) { /* Keep track of all files for filelist generation */ newpkg->files = alpm_list_add(newpkg->files, strdup(entry_name)); } @@ -232,11 +336,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 +353,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..21914944 --- /dev/null +++ b/lib/libalpm/be_sync.c @@ -0,0 +1,485 @@ +/* + * be_sync.c + * + * Copyright (c) 2006-2011 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 <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 + * alpm_list_t *syncs = alpm_option_get_syncdbs(); + * if(alpm_trans_init(0, NULL, NULL, NULL) == 0) { + * for(i = syncs; i; i = alpm_list_next(i)) { + * pmdb_t *db = alpm_list_getdata(i); + * result = alpm_db_update(0, db); + * alpm_trans_release(); + * + * if(result < 0) { + * printf("Unable to update database: %s\n", alpm_strerrorlast()); + * } else if(result == 1) { + * 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, -1 on error (pm_errno is set accordingly), 1 if up to + * 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, pmpkg_t *likely_pkg); + +/* + * This is the data table used to generate the estimating function below. + * "Weighted Avg" means averaging the bottom table values; thus each repo, big + * or small, will have equal influence. "Unweighted Avg" means averaging the + * sums of the top table columns, thus each package has equal influence. The + * final values are calculated by (surprise) averaging the averages, because + * why the hell not. + * + * Database Pkgs tar bz2 gz xz + * community 2096 5294080 256391 421227 301296 + * core 180 460800 25257 36850 29356 + * extra 2606 6635520 294647 470818 339392 + * multilib 126 327680 16120 23261 18732 + * testing 76 204800 10902 14348 12100 + * + * Bytes Per Package + * community 2096 2525.80 122.32 200.97 143.75 + * core 180 2560.00 140.32 204.72 163.09 + * extra 2606 2546.25 113.06 180.67 130.23 + * multilib 126 2600.63 127.94 184.61 148.67 + * testing 76 2694.74 143.45 188.79 159.21 + + * Weighted Avg 2585.48 129.42 191.95 148.99 + * Unweighted Avg 2543.39 118.74 190.16 137.93 + * Average of Avgs 2564.44 124.08 191.06 143.46 + */ +static size_t estimate_package_count(struct stat *st, struct archive *archive) +{ + unsigned int per_package; + + switch(archive_compression(archive)) { + case ARCHIVE_COMPRESSION_NONE: + per_package = 2564; + break; + case ARCHIVE_COMPRESSION_GZIP: + per_package = 191; + break; + case ARCHIVE_COMPRESSION_BZIP2: + per_package = 124; + break; + case ARCHIVE_COMPRESSION_COMPRESS: + per_package = 193; + break; + case ARCHIVE_COMPRESSION_LZMA: + case ARCHIVE_COMPRESSION_XZ: + per_package = 143; + break; + case ARCHIVE_COMPRESSION_UU: + per_package = 3543; + break; + default: + /* assume it is at least somewhat compressed */ + per_package = 200; + } + return((size_t)(st->st_size / per_package) + 1); +} + +static int sync_db_populate(pmdb_t *db) +{ + size_t est_count; + int count = 0; + struct stat buf; + struct archive *archive; + struct archive_entry *entry; + pmpkg_t *pkg = NULL; + + 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)); + archive_read_finish(archive); + RET_ERR(PM_ERR_DB_OPEN, 1); + } + if(stat(_alpm_db_path(db), &buf) != 0) { + RET_ERR(PM_ERR_DB_OPEN, 1); + } + est_count = estimate_package_count(&buf, archive); + + /* initialize hash at 66% full */ + db->pkgcache = _alpm_pkghash_create(est_count * 3 / 2); + if(db->pkgcache == NULL) { + RET_ERR(PM_ERR_MEMORY, -1); + } + + while(archive_read_next_header(archive, &entry) == ARCHIVE_OK) { + const struct stat *st; + + st = archive_entry_stat(entry); + + if(S_ISDIR(st->st_mode)) { + const char *name; + + pkg = _alpm_pkg_new(); + if(pkg == NULL) { + archive_read_finish(archive); + RET_ERR(PM_ERR_MEMORY, -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_pkghash_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_pkghash_add(db->pkgcache, pkg); + count++; + } else { + /* we have desc, depends or deltas - parse it */ + sync_db_read(db, archive, entry, pkg); + } + } + + if(count > 0) { + db->pkgcache->list = alpm_list_msort(db->pkgcache->list, (size_t)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, pmpkg_t *likely_pkg) +{ + const char *entryname = NULL, *filename; + char *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--); + filename = q + 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 */ + if(likely_pkg && strcmp(likely_pkg->name, pkgname) == 0) { + pkg = likely_pkg; + } else { + if(db->pkgcache == NULL) { + RET_ERR(PM_ERR_MEMORY, -1); + } + pkg = _alpm_pkghash_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); + pkg->builddate = _alpm_parsedate(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, "%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); + /* TODO: return 0 always? */ + return(0); +} + +static int sync_db_version(pmdb_t *db) +{ + return(2); +} + +struct db_operations sync_db_ops = { + .populate = sync_db_populate, + .unregister = _alpm_db_unregister, + .version = sync_db_version, +}; + +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); + if(db == NULL) { + RET_ERR(PM_ERR_DB_CREATE, NULL); + } + db->ops = &sync_db_ops; + + 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..6faced16 100644 --- a/lib/libalpm/conflict.c +++ b/lib/libalpm/conflict.c @@ -1,7 +1,7 @@ /* * conflict.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> * Copyright (c) 2006 by David Kimpe <dnaku@frugalware.org> @@ -38,10 +38,10 @@ #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) +pmconflict_t *_alpm_conflict_new(const char *package1, const char *package2, + const char *reason) { pmconflict_t *conflict; @@ -76,20 +76,20 @@ pmconflict_t *_alpm_conflict_dup(const pmconflict_t *conflict) return(newconflict); } -int _alpm_conflict_isin(pmconflict_t *needle, alpm_list_t *haystack) +static int conflict_isin(pmconflict_t *needle, alpm_list_t *haystack) { alpm_list_t *i; + const char *npkg1 = needle->package1; + const char *npkg2 = needle->package2; ALPM_LOG_FUNC; for(i = haystack; i; i = i->next) { pmconflict_t *conflict = i->data; - char *cpkg1 = conflict->package1; - 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))) { + const char *cpkg1 = conflict->package1; + const char *cpkg2 = conflict->package2; + if((strcmp(cpkg1, npkg1) == 0 && strcmp(cpkg2, npkg2) == 0) + || (strcmp(cpkg1, npkg2) == 0 && strcmp(cpkg2, npkg1) == 0)) { return(1); } } @@ -97,28 +97,6 @@ int _alpm_conflict_isin(pmconflict_t *needle, alpm_list_t *haystack) return(0); } -/** Check if pkg1 conflicts with pkg2 - * @param pkg1 package we are looking at - * @param conflict name of the possible conflict - * @param pkg2 package to check - * @return 0 for no conflict, non-zero otherwise - */ -static int does_conflict(pmpkg_t *pkg1, const char *conflict, pmpkg_t *pkg2) -{ - const char *pkg1name = alpm_pkg_get_name(pkg1); - const char *pkg2name = alpm_pkg_get_name(pkg2); - pmdepend_t *conf = _alpm_splitdep(conflict); - int match = 0; - - match = alpm_depcmp(pkg2, conf); - if(match) { - _alpm_log(PM_LOG_DEBUG, "package %s conflicts with %s (by %s)\n", - pkg1name, pkg2name, conflict); - } - _alpm_dep_free(conf); - return(match); -} - /** Adds the pkg1/pkg2 conflict to the baddeps list * @param *baddeps list to add conflict to * @param pkg1 first package @@ -128,7 +106,9 @@ static void add_conflict(alpm_list_t **baddeps, const char *pkg1, const char *pkg2, const char *reason) { pmconflict_t *conflict = _alpm_conflict_new(pkg1, pkg2, reason); - if(conflict && !_alpm_conflict_isin(conflict, *baddeps)) { + _alpm_log(PM_LOG_DEBUG, "package %s conflicts with %s (by %s)\n", + pkg1, pkg2, reason); + if(conflict && !conflict_isin(conflict, *baddeps)) { *baddeps = alpm_list_add(*baddeps, conflict); } else { _alpm_conflict_free(conflict); @@ -159,6 +139,7 @@ static void check_conflict(alpm_list_t *list1, alpm_list_t *list2, for(j = alpm_pkg_get_conflicts(pkg1); j; j = j->next) { const char *conflict = j->data; + pmdepend_t *parsed_conflict = _alpm_splitdep(conflict); for(k = list2; k; k = k->next) { pmpkg_t *pkg2 = k->data; @@ -169,7 +150,7 @@ static void check_conflict(alpm_list_t *list1, alpm_list_t *list2, continue; } - if(does_conflict(pkg1, conflict, pkg2)) { + if(_alpm_depcmp(pkg2, parsed_conflict)) { if(order >= 0) { add_conflict(baddeps, pkg1name, pkg2name, conflict); } else { @@ -177,6 +158,7 @@ static void check_conflict(alpm_list_t *list1, alpm_list_t *list2, } } } + _alpm_dep_free(parsed_conflict); } } } @@ -208,8 +190,8 @@ alpm_list_t *_alpm_outerconflicts(pmdb_t *db, alpm_list_t *packages) return(NULL); } - alpm_list_t *dblist = alpm_list_diff(_alpm_db_get_pkgcache(db), packages, - _alpm_pkg_cmp); + alpm_list_t *dblist = alpm_list_diff(_alpm_db_get_pkgcache(db), + packages, _alpm_pkg_cmp); /* two checks to be done here for conflicts */ _alpm_log(PM_LOG_DEBUG, "check targets vs db\n"); @@ -321,7 +303,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)); @@ -384,7 +366,7 @@ static int dir_belongsto_pkg(char *dirpath, pmpkg_t *pkg) return(0); } } else { - if(alpm_list_find_str(alpm_pkg_get_files(pkg),path)) { + if(alpm_list_find_str(alpm_pkg_get_files(pkg), path)) { continue; } else { closedir(dir); @@ -403,8 +385,8 @@ alpm_list_t *_alpm_db_find_fileconflicts(pmdb_t *db, pmtrans_t *trans, alpm_list_t *upgrade, alpm_list_t *remove) { alpm_list_t *i, *j, *conflicts = NULL; - int numtargs = alpm_list_count(upgrade); - int current; + size_t numtargs = alpm_list_count(upgrade); + size_t current; ALPM_LOG_FUNC; @@ -416,7 +398,7 @@ alpm_list_t *_alpm_db_find_fileconflicts(pmdb_t *db, pmtrans_t *trans, * be possible with real transactions. Right now we only do half as much * here as we do when we actually extract files in add.c with our 12 * different cases. */ - for(current = 1, i = upgrade; i; i = i->next, current++) { + for(current = 0, i = upgrade; i; i = i->next, current++) { alpm_list_t *k, *tmpfiles = NULL; pmpkg_t *p1, *p2, *dbpkg; char path[PATH_MAX+1]; @@ -426,8 +408,8 @@ alpm_list_t *_alpm_db_find_fileconflicts(pmdb_t *db, pmtrans_t *trans, continue; } - double percent = (double)current / numtargs; - PROGRESS(trans, PM_TRANS_PROGRESS_CONFLICTS_START, "", (percent * 100), + int percent = (current * 100) / numtargs; + PROGRESS(trans, PM_TRANS_PROGRESS_CONFLICTS_START, "", percent, numtargs, current); /* CHECK 1: check every target against every target */ _alpm_log(PM_LOG_DEBUG, "searching for file conflicts: %s\n", @@ -555,6 +537,8 @@ alpm_list_t *_alpm_db_find_fileconflicts(pmdb_t *db, pmtrans_t *trans, } FREELIST(tmpfiles); } + PROGRESS(trans, PM_TRANS_PROGRESS_CONFLICTS_START, "", 100, + numtargs, current); return(conflicts); } diff --git a/lib/libalpm/conflict.h b/lib/libalpm/conflict.h index e60e5b3b..418d3f61 100644 --- a/lib/libalpm/conflict.h +++ b/lib/libalpm/conflict.h @@ -1,7 +1,7 @@ /* * conflict.h * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 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 @@ -40,7 +40,6 @@ struct __pmfileconflict_t { pmconflict_t *_alpm_conflict_new(const char *package1, const char *package2, const char *reason); pmconflict_t *_alpm_conflict_dup(const pmconflict_t *conflict); void _alpm_conflict_free(pmconflict_t *conflict); -int _alpm_conflict_isin(pmconflict_t *needle, alpm_list_t *haystack); alpm_list_t *_alpm_innerconflicts(alpm_list_t *packages); alpm_list_t *_alpm_outerconflicts(pmdb_t *db, alpm_list_t *packages); alpm_list_t *_alpm_db_find_fileconflicts(pmdb_t *db, pmtrans_t *trans, diff --git a/lib/libalpm/db.c b/lib/libalpm/db.c index c8a91a2b..fb64faed 100644 --- a/lib/libalpm/db.c +++ b/lib/libalpm/db.c @@ -1,7 +1,7 @@ /* * db.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> * Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu> @@ -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 @@ -64,23 +64,8 @@ pmdb_t SYMEXPORT *alpm_db_register_sync(const char *treename) return(_alpm_db_register_sync(treename)); } -/** Register the local package database. - * @return a pmdb_t* representing the local database, or NULL on error - */ -pmdb_t SYMEXPORT *alpm_db_register_local(void) -{ - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, NULL)); - /* Do not register a database if a transaction is on-going */ - ASSERT(handle->trans == NULL, RET_ERR(PM_ERR_TRANS_NOT_NULL, NULL)); - - return(_alpm_db_register_local()); -} - /* 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 +81,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 +91,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 +143,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); } @@ -168,7 +157,7 @@ int SYMEXPORT alpm_db_setserver(pmdb_t *db, const char *url) alpm_list_t *i; int found = 0; char *newurl; - int len = 0; + size_t len = 0; ALPM_LOG_FUNC; @@ -321,7 +310,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 +330,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 +346,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 +394,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); @@ -420,6 +405,14 @@ const char *_alpm_db_path(pmdb_t *db) return(db->_path); } +int _alpm_db_version(pmdb_t *db) +{ + if(!db) { + return(-1); + } + return(db->ops->version(db)); +} + int _alpm_db_cmp(const void *d1, const void *d2) { pmdb_t *db1 = (pmdb_t *)d1; @@ -503,52 +496,258 @@ 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(_alpm_db_get_pkgcache(db), + (alpm_list_fn_free)_alpm_pkg_free); + _alpm_pkghash_free(db->pkgcache); + db->pkgcache_loaded = 0; + + _alpm_db_free_grpcache(db); +} + +pmpkghash_t *_alpm_db_get_pkgcache_hash(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) +alpm_list_t *_alpm_db_get_pkgcache(pmdb_t *db) { - pmdb_t *db; - alpm_list_t *i; + ALPM_LOG_FUNC; + + pmpkghash_t *hash = _alpm_db_get_pkgcache_hash(db); + + if(hash == NULL) { + return(NULL); + } + + return(hash->list); +} + +/* "duplicate" pkg then add it to pkgcache */ +int _alpm_db_add_pkgincache(pmdb_t *db, pmpkg_t *pkg) +{ + 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_pkghash_add_sorted(db->pkgcache, newpkg); + + _alpm_db_free_grpcache(db); + + return(0); +} + +int _alpm_db_remove_pkgfromcache(pmdb_t *db, pmpkg_t *pkg) +{ + pmpkg_t *data = NULL; + + 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_pkghash_remove(db->pkgcache, pkg, &data); + 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); + } + + pmpkghash_t *pkgcache = _alpm_db_get_pkgcache_hash(db); + if(!pkgcache) { + _alpm_log(PM_LOG_DEBUG, "warning: failed to get '%s' from NULL pkgcache\n", + target); + return(NULL); + } + + return(_alpm_pkghash_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..75776d71 100644 --- a/lib/libalpm/db.h +++ b/lib/libalpm/db.h @@ -1,7 +1,7 @@ /* * db.h * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> * Copyright (c) 2006 by Miklos Vajna <vmiklos@frugalware.org> @@ -23,22 +23,31 @@ #define _ALPM_DB_H #include "alpm.h" -#include <limits.h> +#include "pkghash.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 *); + int (*version) (pmdb_t *); +}; + /* Database */ struct __pmdb_t { char *treename; @@ -46,26 +55,48 @@ 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; + pmpkghash_t *pkgcache; alpm_list_t *grpcache; alpm_list_t *servers; + + struct db_operations *ops; }; + /* db.c, database general calls */ +pmdb_t *_alpm_db_new(const char *treename, int is_local); void _alpm_db_free(pmdb_t *db); const char *_alpm_db_path(pmdb_t *db); +int _alpm_db_version(pmdb_t *db); 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); + +/* 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); +pmpkghash_t *_alpm_db_get_pkgcache_hash(pmdb_t *db); +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.c b/lib/libalpm/delta.c index 72835005..10c982f2 100644 --- a/lib/libalpm/delta.c +++ b/lib/libalpm/delta.c @@ -1,7 +1,7 @@ /* * delta.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2007-2006 by Judd Vinet <jvinet@zeroflux.org> * * This program is free software; you can redistribute it and/or modify diff --git a/lib/libalpm/delta.h b/lib/libalpm/delta.h index 76283380..d7a81c47 100644 --- a/lib/libalpm/delta.h +++ b/lib/libalpm/delta.h @@ -1,7 +1,7 @@ /* * delta.h * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2007-2006 by Judd Vinet <jvinet@zeroflux.org> * * This program is free software; you can redistribute it and/or modify @@ -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..ea579cda 100644 --- a/lib/libalpm/deps.c +++ b/lib/libalpm/deps.c @@ -1,7 +1,7 @@ /* * deps.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> * Copyright (c) 2005, 2006 by Miklos Vajna <vmiklos@frugalware.org> @@ -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) @@ -140,8 +139,8 @@ alpm_list_t *_alpm_sortbydeps(alpm_list_t *targets, int reverse) vertex->state = -1; int found = 0; while(vertex->childptr && !found) { - pmgraph_t *nextchild = (vertex->childptr)->data; - vertex->childptr = (vertex->childptr)->next; + pmgraph_t *nextchild = vertex->childptr->data; + vertex->childptr = vertex->childptr->next; if (nextchild->state == 0) { found = 1; nextchild->parent = vertex; @@ -150,12 +149,15 @@ alpm_list_t *_alpm_sortbydeps(alpm_list_t *targets, int reverse) else if(nextchild->state == -1) { pmpkg_t *vertexpkg = vertex->data; pmpkg_t *childpkg = nextchild->data; + const char *message; + _alpm_log(PM_LOG_WARNING, _("dependency cycle detected:\n")); if(reverse) { - _alpm_log(PM_LOG_WARNING, _("%s will be removed after its %s dependency\n"), vertexpkg->name, childpkg->name); + message =_("%s will be removed after its %s dependency\n"); } else { - _alpm_log(PM_LOG_WARNING, _("%s will be installed before its %s dependency\n"), vertexpkg->name, childpkg->name); + message =_("%s will be installed before its %s dependency\n"); } + _alpm_log(PM_LOG_WARNING, message, vertexpkg->name, childpkg->name); } } if(!found) { @@ -196,36 +198,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_tolerant(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. @@ -234,7 +225,7 @@ alpm_list_t SYMEXPORT *alpm_deptest(pmdb_t *db, alpm_list_t *targets) * @param reversedeps handles the backward dependencies * @param remove an alpm_list_t* of packages to be removed * @param upgrade an alpm_list_t* of packages to be upgraded (remove-then-upgrade) - * @return an alpm_list_t* of pmpkg_t* of missing_t pointers. + * @return an alpm_list_t* of pmpkg_t* of pmdepmissing_t pointers. */ alpm_list_t SYMEXPORT *alpm_checkdeps(alpm_list_t *pkglist, int reversedeps, alpm_list_t *remove, alpm_list_t *upgrade) @@ -242,14 +233,13 @@ alpm_list_t SYMEXPORT *alpm_checkdeps(alpm_list_t *pkglist, int reversedeps, alpm_list_t *i, *j; alpm_list_t *targets, *dblist = NULL, *modified = NULL; alpm_list_t *baddeps = NULL; - pmdepmissing_t *miss = NULL; ALPM_LOG_FUNC; 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); @@ -270,6 +260,7 @@ alpm_list_t SYMEXPORT *alpm_checkdeps(alpm_list_t *pkglist, int reversedeps, if(!_alpm_find_dep_satisfier(upgrade, depend) && !_alpm_find_dep_satisfier(dblist, depend)) { /* Unsatisfied dependency in the upgrade list */ + pmdepmissing_t *miss; char *missdepstring = alpm_dep_compute_string(depend); _alpm_log(PM_LOG_DEBUG, "checkdeps: missing dependency '%s' for package '%s'\n", missdepstring, alpm_pkg_get_name(tp)); @@ -294,6 +285,7 @@ alpm_list_t SYMEXPORT *alpm_checkdeps(alpm_list_t *pkglist, int reversedeps, if(causingpkg && !_alpm_find_dep_satisfier(upgrade, depend) && !_alpm_find_dep_satisfier(dblist, depend)) { + pmdepmissing_t *miss; char *missdepstring = alpm_dep_compute_string(depend); _alpm_log(PM_LOG_DEBUG, "checkdeps: transaction would break '%s' dependency of '%s'\n", missdepstring, alpm_pkg_get_name(lp)); @@ -304,6 +296,7 @@ alpm_list_t SYMEXPORT *alpm_checkdeps(alpm_list_t *pkglist, int reversedeps, } } } + alpm_list_free(modified); alpm_list_free(dblist); @@ -331,89 +324,116 @@ static int dep_vercmp(const char *version1, pmdepmod_t mod, return(equal); } -int SYMEXPORT alpm_depcmp(pmpkg_t *pkg, pmdepend_t *dep) +/* nodepversion: skip version checking */ +static int _depcmp(pmpkg_t *pkg, pmdepend_t *dep, int nodepversion) { alpm_list_t *i; - - ALPM_LOG_FUNC; - - const char *pkgname = alpm_pkg_get_name(pkg); - const char *pkgversion = alpm_pkg_get_version(pkg); int satisfy = 0; + int depmod; + + if(nodepversion) { + depmod = PM_DEP_MOD_ANY; + } else { + depmod = dep->mod; + } /* 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(pkg->name, dep->name) == 0 + && dep_vercmp(pkg->version, depmod, 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); + satisfy = (depmod == PM_DEP_MOD_ANY + && 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. Cast is to silence sign conversion warning; + * we know provver >= provision if we are here. */ + size_t namelen = (size_t)(provver - provision); provver += 1; - satisfy = (strcmp(provname, dep->name) == 0 - && dep_vercmp(provver, dep->mod, dep->version)); + satisfy = (strlen(dep->name) == namelen + && strncmp(provision, dep->name, namelen) == 0 + && dep_vercmp(provver, depmod, dep->version)); } - free(provname); } return(satisfy); } +/* tolerant : respects NODEPVERSION flag */ +int _alpm_depcmp_tolerant(pmpkg_t *pkg, pmdepend_t *dep) +{ + int nodepversion = 0; + int flags = alpm_trans_get_flags(); + + if (flags != -1) { + nodepversion = flags & PM_TRANS_FLAG_NODEPVERSION; + } + + return(_depcmp(pkg, dep, nodepversion)); +} + +/* strict : ignores NODEPVERSION flag */ +int _alpm_depcmp(pmpkg_t *pkg, pmdepend_t *dep) +{ + return(_depcmp(pkg, dep, 0)); +} + pmdepend_t *_alpm_splitdep(const char *depstring) { pmdepend_t *depend; - char *ptr = NULL; - char *newstr = NULL; + const char *ptr, *version = NULL; if(depstring == NULL) { return(NULL); } - STRDUP(newstr, depstring, RET_ERR(PM_ERR_MEMORY, NULL)); CALLOC(depend, 1, sizeof(pmdepend_t), RET_ERR(PM_ERR_MEMORY, NULL)); /* Find a version comparator if one exists. If it does, set the type and * increment the ptr accordingly so we can copy the right strings. */ - if((ptr = strstr(newstr, ">="))) { + if((ptr = strstr(depstring, ">="))) { depend->mod = PM_DEP_MOD_GE; - *ptr = '\0'; - ptr += 2; - } else if((ptr = strstr(newstr, "<="))) { + version = ptr + 2; + } else if((ptr = strstr(depstring, "<="))) { depend->mod = PM_DEP_MOD_LE; - *ptr = '\0'; - ptr += 2; - } else if((ptr = strstr(newstr, "="))) { /* Note: we must do =,<,> checks after <=, >= checks */ + version = ptr + 2; + } else if((ptr = strstr(depstring, "="))) { + /* Note: we must do =,<,> checks after <=, >= checks */ depend->mod = PM_DEP_MOD_EQ; - *ptr = '\0'; - ptr += 1; - } else if((ptr = strstr(newstr, "<"))) { + version = ptr + 1; + } else if((ptr = strstr(depstring, "<"))) { depend->mod = PM_DEP_MOD_LT; - *ptr = '\0'; - ptr += 1; - } else if((ptr = strstr(newstr, ">"))) { + version = ptr + 1; + } else if((ptr = strstr(depstring, ">"))) { depend->mod = PM_DEP_MOD_GT; - *ptr = '\0'; - ptr += 1; + version = ptr + 1; } else { - /* no version specified - copy the name and return it */ + /* no version specified, leave version and ptr NULL */ depend->mod = PM_DEP_MOD_ANY; - STRDUP(depend->name, newstr, RET_ERR(PM_ERR_MEMORY, NULL)); - depend->version = NULL; - free(newstr); - return(depend); } - /* 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)); - STRDUP(depend->version, ptr, RET_ERR(PM_ERR_MEMORY, NULL)); - free(newstr); + /* copy the right parts to the right places */ + STRNDUP(depend->name, depstring, ptr - depstring, + RET_ERR(PM_ERR_MEMORY, NULL)); + depend->name_hash = _alpm_hash_sdbm(depend->name); + if(version) { + STRDUP(depend->version, version, RET_ERR(PM_ERR_MEMORY, NULL)); + } return(depend); } @@ -424,6 +444,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; @@ -505,6 +526,28 @@ void _alpm_recursedeps(pmdb_t *db, alpm_list_t *targs, int include_explicit) } } +/** Find a package satisfying a specified dependency. + * First look for a literal, going through each db one by one. Then look for + * providers. The first satisfier found is returned. + * The dependency can include versions with depmod operators. + * @param dbs an alpm_list_t* of pmdb_t where the satisfier will be searched + * @param depstring package or provision name, versioned or not + * @return a pmpkg_t* satisfying depstring + */ +pmpkg_t SYMEXPORT *alpm_find_dbs_satisfier(alpm_list_t *dbs, const char *depstring) +{ + pmdepend_t *dep; + pmpkg_t *pkg; + + ASSERT(dbs, return(NULL)); + + dep = _alpm_splitdep(depstring); + ASSERT(dep, return(NULL)); + pkg = _alpm_resolvedep(dep, dbs, NULL, 1); + _alpm_dep_free(dep); + return(pkg); +} + /** * helper function for resolvedeps: search for dep satisfier in dbs * @@ -522,10 +565,14 @@ pmpkg_t *_alpm_resolvedep(pmdepend_t *dep, alpm_list_t *dbs, { alpm_list_t *i, *j; int ignored = 0; + + alpm_list_t *providers = NULL; + int count; + /* 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_tolerant(pkg, dep) && !_alpm_pkg_find(excluding, pkg->name)) { if(_alpm_pkg_should_ignore(pkg)) { int install = 0; if (prompt) { @@ -546,7 +593,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_tolerant(pkg, dep) && strcmp(pkg->name, dep->name) != 0 && !_alpm_pkg_find(excluding, pkg->name)) { if(_alpm_pkg_should_ignore(pkg)) { int install = 0; @@ -561,12 +608,40 @@ pmpkg_t *_alpm_resolvedep(pmdepend_t *dep, alpm_list_t *dbs, continue; } } - _alpm_log(PM_LOG_WARNING, _("provider package was selected (%s provides %s)\n"), - pkg->name, dep->name); - return(pkg); + _alpm_log(PM_LOG_DEBUG, "provider found (%s provides %s)\n", + pkg->name, dep->name); + providers = alpm_list_add(providers, pkg); + /* keep looking for other providers in the all dbs */ } } } + + /* first check if one provider is already installed locally */ + for(i = providers; i; i = i->next) { + pmpkg_t *pkg = i->data; + if (_alpm_pkghash_find(_alpm_db_get_pkgcache_hash(handle->db_local), pkg->name)) { + alpm_list_free(providers); + return(pkg); + } + } + count = alpm_list_count(providers); + if (count >= 1) { + /* default to first provider if there is no QUESTION callback */ + int index = 0; + if(count > 1) { + /* if there is more than one provider, we ask the user */ + QUESTION(handle->trans, PM_TRANS_CONV_SELECT_PROVIDER, + providers, dep, NULL, &index); + } + if(index >= 0 && index < count) { + pmpkg_t *pkg = alpm_list_getdata(alpm_list_nth(providers, index)); + alpm_list_free(providers); + return(pkg); + } + alpm_list_free(providers); + providers = NULL; + } + if(ignored) { /* resolvedeps will override these */ pm_errno = PM_ERR_PKG_IGNORED; } else { @@ -598,6 +673,7 @@ int _alpm_resolvedeps(alpm_list_t *localpkgs, alpm_list_t *dbs_sync, pmpkg_t *pk alpm_list_t *preferred, alpm_list_t **packages, alpm_list_t *remove, alpm_list_t **data) { + int ret = 0; alpm_list_t *i, *j; alpm_list_t *targ; alpm_list_t *deps = NULL; @@ -622,14 +698,18 @@ int _alpm_resolvedeps(alpm_list_t *localpkgs, alpm_list_t *dbs_sync, pmpkg_t *pk targ = alpm_list_add(NULL, tpkg); deps = alpm_checkdeps(localpkgs, 0, remove, targ); alpm_list_free(targ); + for(j = deps; j; j = j->next) { pmdepmissing_t *miss = j->data; pmdepend_t *missdep = alpm_miss_get_dep(miss); - /* check if one of the packages in the [*packages] list already satisfies this dependency */ + /* check if one of the packages in the [*packages] list already satisfies + * this dependency */ if(_alpm_find_dep_satisfier(*packages, missdep)) { + _alpm_depmiss_free(miss); continue; } - /* check if one of the packages in the [preferred] list already satisfies this dependency */ + /* check if one of the packages in the [preferred] list already satisfies + * this dependency */ pmpkg_t *spkg = _alpm_find_dep_satisfier(preferred, missdep); if(!spkg) { /* find a satisfier package in the given repositories */ @@ -638,33 +718,32 @@ int _alpm_resolvedeps(alpm_list_t *localpkgs, alpm_list_t *dbs_sync, pmpkg_t *pk if(!spkg) { pm_errno = PM_ERR_UNSATISFIED_DEPS; char *missdepstring = alpm_dep_compute_string(missdep); - _alpm_log(PM_LOG_WARNING, _("cannot resolve \"%s\", a dependency of \"%s\"\n"), + _alpm_log(PM_LOG_WARNING, + _("cannot resolve \"%s\", a dependency of \"%s\"\n"), missdepstring, tpkg->name); free(missdepstring); if(data) { - pmdepmissing_t *missd = _alpm_depmiss_new(miss->target, - miss->depend, miss->causingpkg); - if(missd) { - *data = alpm_list_add(*data, missd); - } + *data = alpm_list_add(*data, miss); } - alpm_list_free(*packages); - *packages = packages_copy; - alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_depmiss_free); - alpm_list_free(deps); - return(-1); + ret = -1; } else { _alpm_log(PM_LOG_DEBUG, "pulling dependency %s (needed by %s)\n", alpm_pkg_get_name(spkg), alpm_pkg_get_name(tpkg)); *packages = alpm_list_add(*packages, spkg); + _alpm_depmiss_free(miss); } } - alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_depmiss_free); alpm_list_free(deps); } - alpm_list_free(packages_copy); + + if(ret != 0) { + alpm_list_free(*packages); + *packages = packages_copy; + } else { + alpm_list_free(packages_copy); + } _alpm_log(PM_LOG_DEBUG, "finished resolving dependencies\n"); - return(0); + return(ret); } /* Does pkg1 depend on pkg2, ie. does pkg2 satisfy a dependency of pkg1? */ @@ -672,7 +751,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_tolerant(pkg2, i->data)) { return(1); } } diff --git a/lib/libalpm/deps.h b/lib/libalpm/deps.h index ffc3aeeb..86070ab4 100644 --- a/lib/libalpm/deps.h +++ b/lib/libalpm/deps.h @@ -1,7 +1,7 @@ /* * deps.h * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> * Copyright (c) 2006 by Miklos Vajna <vmiklos@frugalware.org> @@ -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,8 @@ 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); +int _alpm_depcmp_tolerant(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..253e1185 --- /dev/null +++ b/lib/libalpm/diskspace.c @@ -0,0 +1,347 @@ +/* + * diskspace.c + * + * Copyright (c) 2010-2011 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" + +#include <errno.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 + +/* 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; + /* the negation will sort all mountpoints before their parent */ + 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; + struct statvfs fsp; + + fp = setmntent(MOUNTED, "r"); + + if (fp == NULL) { + return(NULL); + } + + while((mnt = getmntent(fp))) { + if(!mnt) { + _alpm_log(PM_LOG_WARNING, _("could not get filesystem information\n")); + continue; + } + if(statvfs(mnt->mnt_dir, &fsp) != 0) { + _alpm_log(PM_LOG_WARNING, + _("could not get filesystem information for %s: %s\n"), + mnt->mnt_dir, strerror(errno)); + continue; + } + + CALLOC(mp, 1, sizeof(alpm_mountpoint_t), RET_ERR(PM_ERR_MEMORY, NULL)); + mp->mount_dir = strdup(mnt->mnt_dir); + mp->mount_dir_len = strlen(mp->mount_dir); + memcpy(&(mp->fsp), &fsp, sizeof(struct statvfs)); + mp->read_only = fsp.f_flag & ST_RDONLY; + + 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++) { + CALLOC(mp, 1, sizeof(alpm_mountpoint_t), RET_ERR(PM_ERR_MEMORY, NULL)); + mp->mount_dir = strdup(fsp->f_mntonname); + mp->mount_dir_len = strlen(mp->mount_dir); + memcpy(&(mp->fsp), fsp, sizeof(FSSTATSTYPE)); +#if defined HAVE_STRUCT_STATVFS_F_FLAG + mp->read_only = fsp->f_flag & ST_RDONLY; +#elif defined HAVE_STRUCT_STATFS_F_FLAGS + mp->read_only = fsp->f_flags & MNT_RDONLY; +#endif + + 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_mountpoint_t *match_mount_point(const alpm_list_t *mount_points, + const char *real_path) +{ + const alpm_list_t *mp; + + for(mp = mount_points; mp != NULL; mp = mp->next) { + alpm_mountpoint_t *data = mp->data; + + if(strncmp(data->mount_dir, real_path, data->mount_dir_len) == 0) { + return(data); + } + } + + /* 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_mountpoint_t *mp; + struct stat st; + char path[PATH_MAX]; + const char *filename = file->data; + + snprintf(path, PATH_MAX, "%s%s", handle->root, filename); + _alpm_lstat(path, &st); + + /* skip directories and symlinks to be consistent with libarchive that + * reports them to be zero size */ + if(S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode)) { + continue; + } + + mp = match_mount_point(mount_points, path); + if(mp == NULL) { + _alpm_log(PM_LOG_WARNING, + _("could not determine mount point for file %s"), filename); + continue; + } + + /* the addition of (divisor - 1) performs ceil() with integer division */ + mp->blocks_needed -= + (st.st_size + mp->fsp.f_bsize - 1l) / mp->fsp.f_bsize; + mp->used |= USED_REMOVE; + } + + 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; + + 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_mountpoint_t *mp; + const char *filename; + mode_t mode; + char path[PATH_MAX]; + + filename = archive_entry_pathname(entry); + mode = archive_entry_mode(entry); + + /* libarchive reports these as zero size anyways */ + /* NOTE: if we do start accounting for directory size, a dir matching a + * mountpoint needs to be attributed to the parent, not the mountpoint. */ + if(S_ISDIR(mode) || S_ISLNK(mode)) { + continue; + } + + /* approximate space requirements for db entries */ + if(filename[0] == '.') { + filename = alpm_option_get_dbpath(); + } + + snprintf(path, PATH_MAX, "%s%s", handle->root, filename); + + mp = match_mount_point(mount_points, path); + if(mp == NULL) { + _alpm_log(PM_LOG_WARNING, + _("could not determine mount point for file %s"), filename); + continue; + } + + /* the addition of (divisor - 1) performs ceil() with integer division */ + mp->blocks_needed += + (archive_entry_size(entry) + mp->fsp.f_bsize - 1l) / mp->fsp.f_bsize; + mp->used |= USED_INSTALL; + + if(archive_read_data_skip(archive)) { + _alpm_log(PM_LOG_ERROR, _("error while reading package %s: %s\n"), + pkg->name, archive_error_string(archive)); + pm_errno = PM_ERR_LIBARCHIVE; + break; + } + } + + archive_read_finish(archive); + +cleanup: + return(ret); +} + +int _alpm_check_diskspace(pmtrans_t *trans, pmdb_t *db_local) +{ + alpm_list_t *mount_points, *i; + size_t replaces = 0, current = 0, numtargs; + int abort = 0; + alpm_list_t *targ; + + numtargs = alpm_list_count(trans->add); + 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++) { + pmpkg_t *local_pkg; + int percent = (current * 100) / numtargs; + PROGRESS(trans, PM_TRANS_PROGRESS_DISKSPACE_START, "", percent, + numtargs, current); + + local_pkg = targ->data; + calculate_removed_size(mount_points, local_pkg); + } + } + + for(targ = trans->add; targ; targ = targ->next, current++) { + pmpkg_t *pkg, *local_pkg; + int percent = (current * 100) / numtargs; + PROGRESS(trans, PM_TRANS_PROGRESS_DISKSPACE_START, "", percent, + numtargs, current); + + pkg = targ->data; + /* is this package already installed? */ + local_pkg = _alpm_db_get_pkgfromcache(db_local, pkg->name); + if(local_pkg) { + calculate_removed_size(mount_points, local_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 && data->read_only) { + _alpm_log(PM_LOG_ERROR, _("Partition %s is mounted read only\n"), + data->mount_dir); + abort = 1; + } else if(data->used & USED_INSTALL) { + /* cushion is roughly min(5% capacity, 20MiB) */ + long fivepc = ((long)data->fsp.f_blocks / 20) + 1; + long twentymb = (20 * 1024 * 1024 / (long)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) { + _alpm_log(PM_LOG_ERROR, _("Partition %s too full: %ld blocks needed, %ld blocks free)\n"), + data->mount_dir, data->max_blocks_needed + cushion, + (unsigned long)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..2894a0c2 --- /dev/null +++ b/lib/libalpm/diskspace.h @@ -0,0 +1,53 @@ +/* + * diskspace.h + * + * Copyright (c) 2010-2011 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" + +enum mount_used_level { + USED_REMOVE = 1, + USED_INSTALL = (1 << 1), +}; + +typedef struct __alpm_mountpoint_t { + /* mount point information */ + char *mount_dir; + size_t mount_dir_len; + /* storage for additional disk usage calculations */ + long blocks_needed; + long max_blocks_needed; + enum mount_used_level used; + int read_only; + 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..7a98eb12 100644 --- a/lib/libalpm/dload.c +++ b/lib/libalpm/dload.c @@ -1,7 +1,7 @@ /* * download.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 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 @@ -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; } @@ -131,13 +128,13 @@ static int download_internal(const char *url, const char *localpath, destfile = get_destfile(localpath, filename); tempfile = get_tempfile(localpath, filename); - if(stat(tempfile, &st) == 0 && st.st_size > 0) { + if(stat(tempfile, &st) == 0 && S_ISREG(st.st_mode) && st.st_size > 0) { _alpm_log(PM_LOG_DEBUG, "tempfile found, attempting continuation\n"); local_time = fileurl->last_modified = st.st_mtime; local_size = fileurl->offset = (off_t)st.st_size; dl_thisfile = st.st_size; localf = fopen(tempfile, "ab"); - } else if(!force && stat(destfile, &st) == 0 && st.st_size > 0) { + } else if(!force && stat(destfile, &st) == 0 && S_ISREG(st.st_mode) && st.st_size > 0) { _alpm_log(PM_LOG_DEBUG, "destfile found, using mtime only\n"); local_time = fileurl->last_modified = st.st_mtime; local_size = /* no fu->off here */ (off_t)st.st_size; @@ -175,6 +172,14 @@ static int download_internal(const char *url, const char *localpath, /* NOTE: libfetch does not reset the error code, be sure to do it before * calls into the library */ + /* TODO: if we call fetchStat() and get a redirect (disabling automagic + * redirect following), we should repeat the file locator stuff and get a new + * filename rather than only base if off the first URL, and then verify + * get_filename() didn't return ''. Of course, libfetch might not even allow + * us to even get that URL...FS#22645. This would allow us to download things + * without totally puking like + * http://www.archlinux.org/packages/community/x86_64/exim/download/ */ + /* find out the remote size *and* mtime in one go. there is a lot of * trouble in trying to do both size and "if-modified-since" logic in a * non-stat request, so avoid it. */ @@ -250,8 +255,8 @@ static int download_internal(const char *url, const char *localpath, while((nread = fetchIO_read(dlf, buffer, PM_DLBUF_LEN)) > 0) { check_stop(); size_t nwritten = 0; - nwritten = fwrite(buffer, 1, nread, localf); - if((nwritten != nread) || ferror(localf)) { + nwritten = fwrite(buffer, 1, (size_t)nread, 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)); @@ -299,7 +304,11 @@ static int download_internal(const char *url, const char *localpath, tv[1].tv_sec = ust.mtime; utimes(tempfile, tv); } - rename(tempfile, destfile); + if(rename(tempfile, destfile)) { + _alpm_log(PM_LOG_ERROR, _("could not rename %s to %s (%s)\n"), + tempfile, destfile, strerror(errno)); + ret = -1; + } ret = 0; cleanup: @@ -338,7 +347,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/dload.h b/lib/libalpm/dload.h index e60174e4..63266491 100644 --- a/lib/libalpm/dload.h +++ b/lib/libalpm/dload.h @@ -1,7 +1,7 @@ /* * dload.h * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 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 diff --git a/lib/libalpm/error.c b/lib/libalpm/error.c index 8d8d0458..21fbb48f 100644 --- a/lib/libalpm/error.c +++ b/lib/libalpm/error.c @@ -1,7 +1,7 @@ /* * error.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 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 @@ -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 free disk space"); /* Interface */ case PM_ERR_HANDLE_NULL: return _("library not initialized"); @@ -78,6 +77,8 @@ const char SYMEXPORT *alpm_strerror(int err) return _("database already registered"); case PM_ERR_DB_NOT_FOUND: return _("could not find database"); + case PM_ERR_DB_VERSION: + return _("database is incorrect version"); case PM_ERR_DB_WRITE: return _("could not update database"); case PM_ERR_DB_REMOVE: @@ -136,6 +137,8 @@ const char SYMEXPORT *alpm_strerror(int err) /* Miscellaenous */ case PM_ERR_RETRIEVE: return _("failed to retrieve some files"); + case PM_ERR_WRITE: + return _("failed to copy some file"); case PM_ERR_INVALID_REGEX: return _("invalid regular expression"); /* Errors from external libraries- our own wrapper error */ @@ -145,7 +148,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/graph.h b/lib/libalpm/graph.h index c82e6811..07986f66 100644 --- a/lib/libalpm/graph.h +++ b/lib/libalpm/graph.h @@ -1,7 +1,7 @@ /* * graph.h - helpful graph structure and setup/teardown methods * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 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 @@ -21,8 +21,7 @@ #include <sys/types.h> /* off_t */ #include "alpm_list.h" -#include "util.h" /* MALLOC() */ -#include "alpm.h" +#include "util.h" /* CALLOC() */ struct __pmgraph_t { char state; /* 0: untouched, -1: entered, other: leaving time */ @@ -38,15 +37,7 @@ static pmgraph_t *_alpm_graph_new(void) { pmgraph_t *graph = NULL; - MALLOC(graph, sizeof(pmgraph_t), RET_ERR(PM_ERR_MEMORY, NULL)); - - if(graph) { - graph->state = 0; - graph->data = NULL; - graph->parent = NULL; - graph->children = NULL; - graph->childptr = NULL; - } + CALLOC(graph, 1, sizeof(pmgraph_t), RET_ERR(PM_ERR_MEMORY, NULL)); return(graph); } diff --git a/lib/libalpm/group.c b/lib/libalpm/group.c index 061bb0e4..398c2588 100644 --- a/lib/libalpm/group.c +++ b/lib/libalpm/group.c @@ -1,7 +1,7 @@ /* * group.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 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 diff --git a/lib/libalpm/group.h b/lib/libalpm/group.h index 5d8cf775..c92684e3 100644 --- a/lib/libalpm/group.h +++ b/lib/libalpm/group.h @@ -1,7 +1,7 @@ /* * group.h * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 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 diff --git a/lib/libalpm/handle.c b/lib/libalpm/handle.c index aa34cf45..d4ebe82a 100644 --- a/lib/libalpm/handle.c +++ b/lib/libalpm/handle.c @@ -1,7 +1,7 @@ /* * handle.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> * Copyright (c) 2005, 2006 by Miklos Vajna <vmiklos@frugalware.org> @@ -49,7 +49,6 @@ pmhandle_t *_alpm_handle_new() ALPM_LOG_FUNC; CALLOC(handle, 1, sizeof(pmhandle_t), RET_ERR(PM_ERR_MEMORY, NULL)); - handle->lckfd = -1; return(handle); } @@ -232,6 +231,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 +558,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..2d962fe6 100644 --- a/lib/libalpm/handle.h +++ b/lib/libalpm/handle.h @@ -1,7 +1,7 @@ /* * handle.h * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 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 @@ -34,7 +34,7 @@ typedef struct _pmhandle_t { pmdb_t *db_local; /* local db pointer */ alpm_list_t *dbs_sync; /* List of (pmdb_t *) */ FILE *logstream; /* log file stream pointer */ - int lckfd; /* lock file descriptor if one exists */ + FILE *lckstream; /* lock file stream pointer if one exists */ pmtrans_t *trans; /* callback functions */ @@ -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/log.c b/lib/libalpm/log.c index 86bb8257..09c6fb84 100644 --- a/lib/libalpm/log.c +++ b/lib/libalpm/log.c @@ -1,7 +1,7 @@ /* * log.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 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 @@ -42,7 +42,7 @@ * @param fmt output format * @return 0 on success, -1 on error (pm_errno is set accordingly) */ -int SYMEXPORT alpm_logaction(char *fmt, ...) +int SYMEXPORT alpm_logaction(const char *fmt, ...) { int ret; va_list args; @@ -88,7 +88,7 @@ int SYMEXPORT alpm_logaction(char *fmt, ...) /** @} */ -void _alpm_log(pmloglevel_t flag, char *fmt, ...) +void _alpm_log(pmloglevel_t flag, const char *fmt, ...) { va_list args; alpm_cb_log logcb = alpm_option_get_logcb(); diff --git a/lib/libalpm/log.h b/lib/libalpm/log.h index d358733f..9a2961fb 100644 --- a/lib/libalpm/log.h +++ b/lib/libalpm/log.h @@ -1,7 +1,7 @@ /* * log.h * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 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 @@ -29,7 +29,7 @@ #define ALPM_LOG_FUNC #endif -void _alpm_log(pmloglevel_t flag, char *fmt, ...) __attribute__((format(printf,2,3))); +void _alpm_log(pmloglevel_t flag, const char *fmt, ...) __attribute__((format(printf,2,3))); #endif /* _ALPM_LOG_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..d4b3b9c0 100644 --- a/lib/libalpm/package.c +++ b/lib/libalpm/package.c @@ -1,7 +1,7 @@ /* * package.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> * Copyright (c) 2005, 2006 by Christian Hamar <krics@linuxforum.hu> @@ -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,189 @@ 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_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, + .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)); - - if(pkg->origin == PKG_FROM_CACHE && !(pkg->infolevel & INFRQ_DESC)) { - _alpm_db_read(pkg->origin_data.db, pkg, INFRQ_DESC); - } - return pkg->reason; + return pkg->ops->get_reason(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 +289,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 +327,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 +406,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 +420,6 @@ 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->reason = pkg->reason; newpkg->licenses = alpm_list_strdup(pkg->licenses); @@ -657,6 +437,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 +507,13 @@ 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; - ALPM_LOG_FUNC; - cmp = alpm_pkg_vercmp(alpm_pkg_get_version(spkg), + return alpm_pkg_vercmp(alpm_pkg_get_version(spkg), alpm_pkg_get_version(localpkg)); - - if(cmp < 0 && alpm_pkg_has_force(spkg)) { - cmp = 1; - } - - return(cmp); } /* Helper function for comparing packages @@ -749,7 +522,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 +531,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 +539,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..b161d5f1 100644 --- a/lib/libalpm/package.h +++ b/lib/libalpm/package.h @@ -1,7 +1,7 @@ /* * package.h * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> * Copyright (c) 2006 by David Kimpe <dnaku@frugalware.org> @@ -31,27 +31,92 @@ #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 (*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; + 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 +129,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/pkghash.c b/lib/libalpm/pkghash.c new file mode 100644 index 00000000..db98f94b --- /dev/null +++ b/lib/libalpm/pkghash.c @@ -0,0 +1,331 @@ +/* + * pkghash.c + * + * Copyright (c) 2011 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 "pkghash.h" +#include "util.h" +#include "log.h" + +/* List of primes for possible sizes of hash tables. + * + * The maximum table size is the last prime under 1,000,000. That is + * more than an order of magnitude greater than the number of packages + * in any Linux distribution. + */ +static const size_t prime_list[] = +{ + 11ul, 13ul, 17ul, 19ul, 23ul, 29ul, 31ul, 37ul, 41ul, 43ul, 47ul, + 53ul, 59ul, 61ul, 67ul, 71ul, 73ul, 79ul, 83ul, 89ul, 97ul, 103ul, + 109ul, 113ul, 127ul, 137ul, 139ul, 149ul, 157ul, 167ul, 179ul, 193ul, + 199ul, 211ul, 227ul, 241ul, 257ul, 277ul, 293ul, 313ul, 337ul, 359ul, + 383ul, 409ul, 439ul, 467ul, 503ul, 541ul, 577ul, 619ul, 661ul, 709ul, + 761ul, 823ul, 887ul, 953ul, 1031ul, 1109ul, 1193ul, 1289ul, 1381ul, + 1493ul, 1613ul, 1741ul, 1879ul, 2029ul, 2179ul, 2357ul, 2549ul, + 2753ul, 2971ul, 3209ul, 3469ul, 3739ul, 4027ul, 4349ul, 4703ul, + 5087ul, 5503ul, 5953ul, 6427ul, 6949ul, 7517ul, 8123ul, 8783ul, + 9497ul, 10273ul, 11113ul, 12011ul, 12983ul, 14033ul, 15173ul, + 16411ul, 17749ul, 19183ul, 20753ul, 22447ul, 24281ul, 26267ul, + 28411ul, 30727ul, 33223ul, 35933ul, 38873ul, 42043ul, 45481ul, + 49201ul, 53201ul, 57557ul, 62233ul, 67307ul, 72817ul, 78779ul, + 85229ul, 92203ul, 99733ul, 107897ul, 116731ul, 126271ul, 136607ul, + 147793ul, 159871ul, 172933ul, 187091ul, 202409ul, 218971ul, 236897ul, + 256279ul, 277261ul, 299951ul, 324503ul, 351061ul, 379787ul, 410857ul, + 444487ul, 480881ul, 520241ul, 562841ul, 608903ul, 658753ul, 712697ul, + 771049ul, 834181ul, 902483ul, 976369ul +}; + +/* Allocate a hash table with at least "size" buckets */ +pmpkghash_t *_alpm_pkghash_create(size_t size) +{ + pmpkghash_t *hash = NULL; + size_t i, loopsize; + + MALLOC(hash, sizeof(pmpkghash_t), RET_ERR(PM_ERR_MEMORY, NULL)); + + hash->list = NULL; + hash->entries = 0; + hash->buckets = 0; + + loopsize = sizeof(prime_list) / sizeof(*prime_list); + for(i = 0; i < loopsize; i++) { + if(prime_list[i] > size) { + hash->buckets = prime_list[i]; + break; + } + } + + if(hash->buckets < size) { + _alpm_log(PM_LOG_ERROR, _("database larger than maximum size\n")); + free(hash); + return(NULL); + } + + CALLOC(hash->hash_table, hash->buckets, sizeof(alpm_list_t*), \ + free(hash); RET_ERR(PM_ERR_MEMORY, NULL)); + + return(hash); +} + +static size_t get_hash_position(unsigned long name_hash, pmpkghash_t *hash) +{ + size_t position; + alpm_list_t *ptr; + + position = name_hash % hash->buckets; + + /* collision resolution using open addressing with linear probing */ + while((ptr = hash->hash_table[position]) != NULL) { + position = (position + 1) % hash->buckets; + } + + return(position); +} + +/* Expand the hash table size to the next increment and rebin the entries */ +static pmpkghash_t *rehash(pmpkghash_t *oldhash) +{ + pmpkghash_t *newhash; + size_t newsize, position, i; + + /* Hash tables will need resized in two cases: + * - adding packages to the local database + * - poor estimation of the number of packages in sync database + * + * For small hash tables sizes (<500) the increase in size is by a + * minimum of a factor of 2 for optimal rehash efficiency. For + * larger database sizes, this increase is reduced to avoid excess + * memory allocation as both scenarios requiring a rehash should not + * require a table size increase that large. */ + if(oldhash->buckets < 500) { + newsize = oldhash->buckets * 2; + } else if(oldhash->buckets < 2000) { + newsize = oldhash->buckets * 3 / 2; + } else if(oldhash->buckets < 5000) { + newsize = oldhash->buckets * 4 / 3; + } else { + newsize = oldhash->buckets + 1; + } + + newhash = _alpm_pkghash_create(newsize); + if(newhash == NULL) { + /* creation of newhash failed, stick with old one... */ + return(oldhash); + } + + newhash->list = oldhash->list; + oldhash->list = NULL; + + for(i = 0; i < oldhash->buckets; i++) { + if(oldhash->hash_table[i] != NULL) { + pmpkg_t *package = oldhash->hash_table[i]->data; + + position = get_hash_position(package->name_hash, newhash); + + newhash->hash_table[position] = oldhash->hash_table[i]; + oldhash->hash_table[i] = NULL; + } + } + + newhash->entries = oldhash->entries; + + _alpm_pkghash_free(oldhash); + + return(newhash); +} + +static pmpkghash_t *pkghash_add_pkg(pmpkghash_t *hash, pmpkg_t *pkg, int sorted) +{ + alpm_list_t *ptr; + size_t position; + + if(pkg == NULL || hash == NULL) { + return(hash); + } + + if((hash->entries + 1) / MAX_HASH_LOAD > hash->buckets) { + hash = rehash(hash); + } + + position = get_hash_position(pkg->name_hash, hash); + + ptr = calloc(1, sizeof(alpm_list_t)); + if(ptr == NULL) { + return(hash); + } + + ptr->data = pkg; + ptr->next = NULL; + ptr->prev = ptr; + + hash->hash_table[position] = ptr; + if(!sorted){ + hash->list = alpm_list_join(hash->list, ptr); + }else{ + hash->list = alpm_list_mmerge(hash->list, ptr, _alpm_pkg_cmp); + } + + hash->entries += 1; + return(hash); +} + +pmpkghash_t *_alpm_pkghash_add(pmpkghash_t *hash, pmpkg_t *pkg) +{ + return(pkghash_add_pkg(hash, pkg, 0)); +} + +pmpkghash_t *_alpm_pkghash_add_sorted(pmpkghash_t *hash, pmpkg_t *pkg) +{ + return(pkghash_add_pkg(hash, pkg, 1)); +} + +static size_t move_one_entry(pmpkghash_t *hash, size_t start, size_t end) +{ + /* Iterate backwards from 'end' to 'start', seeing if any of the items + * would hash to 'start'. If we find one, we move it there and break. If + * we get all the way back to position and find none that hash to it, we + * also end iteration. Iterating backwards helps prevent needless shuffles; + * we will never need to move more than one item per function call. The + * return value is our current iteration location; if this is equal to + * 'start' we can stop this madness. */ + while(end != start) { + alpm_list_t *i = hash->hash_table[end]; + pmpkg_t *info = i->data; + size_t new_position = get_hash_position(info->name_hash, hash); + + if(new_position == start) { + hash->hash_table[start] = i; + hash->hash_table[end] = NULL; + break; + } + + /* the odd math ensures we are always positive, e.g. + * e.g. (0 - 1) % 47 == -1 + * e.g. (47 + 0 - 1) % 47 == 46 */ + end = (hash->buckets + end - 1) % hash->buckets; + } + return(end); +} + +/** + * @brief Remove a package from a pkghash. + * + * @param hash the hash to remove the package from + * @param pkg the package we are removing + * @param data output parameter containing the removed item + * + * @return the resultant hash + */ +pmpkghash_t *_alpm_pkghash_remove(pmpkghash_t *hash, pmpkg_t *pkg, + pmpkg_t **data) +{ + alpm_list_t *i; + size_t position; + + if(data) { + *data = NULL; + } + + if(pkg == NULL || hash == NULL) { + return(hash); + } + + position = pkg->name_hash % hash->buckets; + while((i = hash->hash_table[position]) != NULL) { + pmpkg_t *info = i->data; + + if(info->name_hash == pkg->name_hash && + strcmp(info->name, pkg->name) == 0) { + size_t stop, prev; + + /* remove from list and hash */ + hash->list = alpm_list_remove_item(hash->list, i); + if(data) { + *data = info; + } + hash->hash_table[position] = NULL; + free(i); + hash->entries -= 1; + + /* Potentially move entries following removed entry to keep open + * addressing collision resolution working. We start by finding the + * next null bucket to know how far we have to look. */ + stop = (position + 1) % hash->buckets; + while(hash->hash_table[stop] != NULL && stop != position) { + stop = (stop + 1) % hash->buckets; + } + stop = (hash->buckets + stop - 1) % hash->buckets; + + /* We now search backwards from stop to position. If we find an + * item that now hashes to position, we will move it, and then try + * to plug the new hole we just opened up, until we finally don't + * move anything. */ + while((prev = move_one_entry(hash, position, stop)) != position) { + position = prev; + } + + return(hash); + } + + position = (position + 1) % hash->buckets; + } + + return(hash); +} + +void _alpm_pkghash_free(pmpkghash_t *hash) +{ + size_t i; + if(hash != NULL) { + for(i = 0; i < hash->buckets; i++) { + free(hash->hash_table[i]); + } + free(hash->hash_table); + } + free(hash); +} + +pmpkg_t *_alpm_pkghash_find(pmpkghash_t *hash, const char *name) +{ + alpm_list_t *lp; + unsigned long name_hash; + size_t position; + + ALPM_LOG_FUNC; + + if(name == NULL || hash == NULL) { + return(NULL); + } + + name_hash = _alpm_hash_sdbm(name); + + position = name_hash % hash->buckets; + + while((lp = hash->hash_table[position]) != NULL) { + pmpkg_t *info = lp->data; + + if(info->name_hash == name_hash && strcmp(info->name, name) == 0) { + return(info); + } + + position = (position + 1) % hash->buckets; + } + + return(NULL); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/pkghash.h b/lib/libalpm/pkghash.h new file mode 100644 index 00000000..617e60bd --- /dev/null +++ b/lib/libalpm/pkghash.h @@ -0,0 +1,60 @@ +/* + * pkghash.h + * + * Copyright (c) 2011 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_PKGHASH_H +#define _ALPM_PKGHASH_H + +#include <stdlib.h> + +#include "alpm.h" +#include "alpm_list.h" + + +/** + * @brief A hash table for holding pmpkg_t objects. + * + * A combination of a hash table and a list, allowing for fast look-up + * by package name but also iteration over the packages. + */ +struct __pmpkghash_t { + /** data held by the hash table */ + alpm_list_t **hash_table; + /** number of buckets in hash table */ + size_t buckets; + /** number of entries in hash table */ + size_t entries; + /** head node of the hash table data in normal list format */ + alpm_list_t *list; +}; + +typedef struct __pmpkghash_t pmpkghash_t; + +pmpkghash_t *_alpm_pkghash_create(size_t size); + +pmpkghash_t *_alpm_pkghash_add(pmpkghash_t *hash, pmpkg_t *pkg); +pmpkghash_t *_alpm_pkghash_add_sorted(pmpkghash_t *hash, pmpkg_t *pkg); +pmpkghash_t *_alpm_pkghash_remove(pmpkghash_t *hash, pmpkg_t *pkg, pmpkg_t **data); + +void _alpm_pkghash_free(pmpkghash_t *hash); + +pmpkg_t *_alpm_pkghash_find(pmpkghash_t *hash, const char *name); + +#define MAX_HASH_LOAD 0.7 + +#endif /* _ALPM_PKGHASH_H */ 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/Makevars b/lib/libalpm/po/Makevars index 31144f5f..6430515f 100644 --- a/lib/libalpm/po/Makevars +++ b/lib/libalpm/po/Makevars @@ -19,7 +19,7 @@ COPYRIGHT_HOLDER = Pacman Development Team <pacman-dev@archlinux.org> # This is the email address or URL to which the translators shall report # bugs in the untranslated strings. -MSGID_BUGS_ADDRESS = pacman-dev@archlinux.org +MSGID_BUGS_ADDRESS = http://bugs.archlinux.org/index.php?project=3 # This is the list of locale categories, beyond LC_MESSAGES, for which the # message catalogs shall be used. It is usually empty. diff --git a/lib/libalpm/po/POTFILES.in b/lib/libalpm/po/POTFILES.in index 475cf4b4..49e9da16 100644 --- a/lib/libalpm/po/POTFILES.in +++ b/lib/libalpm/po/POTFILES.in @@ -6,13 +6,14 @@ 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/cache.c +lib/libalpm/be_sync.c lib/libalpm/conflict.c lib/libalpm/db.c lib/libalpm/delta.c lib/libalpm/deps.c +lib/libalpm/diskspace.c lib/libalpm/dload.c lib/libalpm/error.c lib/libalpm/group.c @@ -20,6 +21,7 @@ lib/libalpm/handle.c lib/libalpm/log.c #lib/libalpm/md5.c lib/libalpm/package.c +lib/libalpm/pkghash.c lib/libalpm/remove.c lib/libalpm/sync.c lib/libalpm/trans.c diff --git a/lib/libalpm/remove.c b/lib/libalpm/remove.c index 153c0426..5def92a6 100644 --- a/lib/libalpm/remove.c +++ b/lib/libalpm/remove.c @@ -1,7 +1,7 @@ /* * remove.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> * Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu> @@ -42,51 +42,33 @@ #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_pkg(pmpkg_t *pkg) { - pmpkg_t *info; pmtrans_t *trans; - pmdb_t *db_local; - alpm_list_t *p; + const char *pkgname; ALPM_LOG_FUNC; /* Sanity checks */ - ASSERT(target != NULL && strlen(target) != 0, RET_ERR(PM_ERR_WRONG_ARGS, -1)); + ASSERT(pkg != NULL, RET_ERR(PM_ERR_WRONG_ARGS, -1)); ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1)); trans = handle->trans; - db_local = handle->db_local; ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1)); - ASSERT(trans->state == STATE_INITIALIZED, RET_ERR(PM_ERR_TRANS_NOT_INITIALIZED, -1)); - ASSERT(db_local != NULL, RET_ERR(PM_ERR_DB_NULL, -1)); + ASSERT(trans->state == STATE_INITIALIZED, + RET_ERR(PM_ERR_TRANS_NOT_INITIALIZED, -1)); + pkgname = alpm_pkg_get_name(pkg); - if(_alpm_pkg_find(trans->remove, target)) { + if(_alpm_pkg_find(trans->remove, pkgname)) { RET_ERR(PM_ERR_TRANS_DUP_TARGET, -1); } - if((info = _alpm_db_get_pkgfromcache(db_local, target)) != NULL) { - _alpm_log(PM_LOG_DEBUG, "adding %s in the target list\n", info->name); - trans->remove = alpm_list_add(trans->remove, _alpm_pkg_dup(info)); - return(0); - } - - _alpm_log(PM_LOG_DEBUG, "could not find %s in database\n", target); - pmgrp_t *grp = alpm_db_readgrp(db_local, target); - if(grp == NULL) { - RET_ERR(PM_ERR_PKG_NOT_FOUND, -1); - } - for(p = alpm_grp_get_pkgs(grp); p; p = alpm_list_next(p)) { - pmpkg_t *pkg = alpm_list_getdata(p); - _alpm_log(PM_LOG_DEBUG, "adding %s in the target list\n", pkg->name); - trans->remove = alpm_list_add(trans->remove, _alpm_pkg_dup(pkg)); - } - + _alpm_log(PM_LOG_DEBUG, "adding %s in the target list\n", pkgname); + trans->remove = alpm_list_add(trans->remove, _alpm_pkg_dup(pkg)); return(0); } @@ -299,10 +281,12 @@ static void unlink_file(pmpkg_t *info, char *filename, alpm_list_t *skip_remove, } } -int _alpm_upgraderemove_package(pmpkg_t *oldpkg, pmpkg_t *newpkg, pmtrans_t *trans) +int _alpm_upgraderemove_package(pmpkg_t *oldpkg, pmpkg_t *newpkg, + pmtrans_t *trans) { alpm_list_t *skip_remove, *b; alpm_list_t *newfiles, *lp; + size_t filenum; alpm_list_t *files = alpm_pkg_get_files(oldpkg); const char *pkgname = alpm_pkg_get_name(oldpkg); @@ -311,9 +295,14 @@ 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)); + skip_remove = alpm_list_join( + alpm_list_strdup(trans->skip_remove), + alpm_list_strdup(handle->noupgrade)); /* Add files in the NEW backup array to the skip_remove array * so this removal operation doesn't kill them */ /* old package backup list */ @@ -337,6 +326,9 @@ int _alpm_upgraderemove_package(pmpkg_t *oldpkg, pmpkg_t *newpkg, pmtrans_t *tra } } + filenum = alpm_list_count(files); + _alpm_log(PM_LOG_DEBUG, "removing %ld files\n", (unsigned long)filenum); + /* iterate through the list backwards, unlinking files */ newfiles = alpm_list_reverse(files); for(lp = newfiles; lp; lp = alpm_list_next(lp)) { @@ -345,10 +337,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)); } @@ -365,7 +358,7 @@ int _alpm_remove_packages(pmtrans_t *trans, pmdb_t *db) { pmpkg_t *info; alpm_list_t *targ, *lp; - int pkg_count; + size_t pkg_count; ALPM_LOG_FUNC; @@ -379,7 +372,7 @@ int _alpm_remove_packages(pmtrans_t *trans, pmdb_t *db) char scriptlet[PATH_MAX]; info = (pmpkg_t*)targ->data; const char *pkgname = NULL; - int targcount = alpm_list_count(targ); + size_t targcount = alpm_list_count(targ); if(handle->trans->state == STATE_INTERRUPTED) { return(0); @@ -402,6 +395,9 @@ int _alpm_remove_packages(pmtrans_t *trans, pmdb_t *db) if(!(trans->flags & PM_TRANS_FLAG_DBONLY)) { alpm_list_t *files = alpm_pkg_get_files(info); + alpm_list_t *newfiles; + size_t filenum; + for(lp = files; lp; lp = lp->next) { if(!can_remove_file(lp->data, NULL)) { _alpm_log(PM_LOG_DEBUG, "not removing package '%s', can't remove all files\n", @@ -410,9 +406,8 @@ int _alpm_remove_packages(pmtrans_t *trans, pmdb_t *db) } } - int filenum = alpm_list_count(files); - alpm_list_t *newfiles; - _alpm_log(PM_LOG_DEBUG, "removing %d files\n", filenum); + filenum = alpm_list_count(files); + _alpm_log(PM_LOG_DEBUG, "removing %ld files\n", (unsigned long)filenum); /* init progress bar */ PROGRESS(trans, PM_TRANS_PROGRESS_REMOVE_START, info->name, 0, @@ -421,14 +416,13 @@ int _alpm_remove_packages(pmtrans_t *trans, pmdb_t *db) /* iterate through the list backwards, unlinking files */ newfiles = alpm_list_reverse(files); for(lp = newfiles; lp; lp = alpm_list_next(lp)) { - double percent; + int percent; unlink_file(info, lp->data, NULL, trans->flags & PM_TRANS_FLAG_NOSAVE); /* update progress bar after each file */ - percent = (double)position / (double)filenum; + percent = (position * 100) / filenum; PROGRESS(trans, PM_TRANS_PROGRESS_REMOVE_START, info->name, - (double)(percent * 100), pkg_count, - (pkg_count - targcount + 1)); + percent, pkg_count, (pkg_count - targcount + 1)); position++; } alpm_list_free(newfiles); @@ -447,7 +441,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/remove.h b/lib/libalpm/remove.h index 55858909..a67e37a1 100644 --- a/lib/libalpm/remove.h +++ b/lib/libalpm/remove.h @@ -1,7 +1,7 @@ /* * remove.h * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 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 diff --git a/lib/libalpm/sync.c b/lib/libalpm/sync.c index 4cbaf0cb..fdd37608 100644 --- a/lib/libalpm/sync.c +++ b/lib/libalpm/sync.c @@ -1,7 +1,7 @@ /* * sync.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> * Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu> @@ -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) @@ -202,177 +202,46 @@ int SYMEXPORT alpm_sync_sysupgrade(int enable_downgrade) return(0); } -static int sync_pkg(pmpkg_t *spkg, alpm_list_t *pkg_list) +/** Find group members across a list of databases. + * If a member exists in several databases, only the first database is used. + * IgnorePkg is also handled. + * @param dbs the list of pmdb_t * + * @pram name the name of the group + * @return the list of pmpkg_t * (caller is responsible for alpm_list_free) + */ +alpm_list_t SYMEXPORT *alpm_find_grp_pkgs(alpm_list_t *dbs, + const char *name) { - pmtrans_t *trans; - pmdb_t *db_local; - pmpkg_t *local; + alpm_list_t *i, *j, *pkgs = NULL, *ignorelist = NULL; - ALPM_LOG_FUNC; - - trans = handle->trans; - db_local = handle->db_local; + for(i = dbs; i; i = i->next) { + pmdb_t *db = i->data; + pmgrp_t *grp = alpm_db_readgrp(db, name); - if(_alpm_pkg_find(pkg_list, alpm_pkg_get_name(spkg))) { - RET_ERR(PM_ERR_TRANS_DUP_TARGET, -1); - } + if(!grp) + continue; - local = _alpm_db_get_pkgfromcache(db_local, alpm_pkg_get_name(spkg)); - if(local) { - int cmp = _alpm_pkg_compare_versions(spkg, local); - if(cmp == 0) { - if(trans->flags & PM_TRANS_FLAG_NEEDED) { - /* with the NEEDED flag, packages up to date are not reinstalled */ - _alpm_log(PM_LOG_WARNING, _("%s-%s is up to date -- skipping\n"), - alpm_pkg_get_name(local), alpm_pkg_get_version(local)); - return(0); - } else { - _alpm_log(PM_LOG_WARNING, _("%s-%s is up to date -- reinstalling\n"), - alpm_pkg_get_name(local), alpm_pkg_get_version(local)); + for(j = alpm_grp_get_pkgs(grp); j; j = j->next) { + pmpkg_t *pkg = j->data; + if(_alpm_pkg_find(ignorelist, alpm_pkg_get_name(pkg))) { + continue; } - } else if(cmp < 0) { - /* local version is newer */ - _alpm_log(PM_LOG_WARNING, _("downgrading package %s (%s => %s)\n"), - alpm_pkg_get_name(local), alpm_pkg_get_version(local), - alpm_pkg_get_version(spkg)); - } - } - - /* add the package to the transaction */ - spkg->reason = PM_PKG_REASON_EXPLICIT; - _alpm_log(PM_LOG_DEBUG, "adding package %s-%s to the transaction targets\n", - alpm_pkg_get_name(spkg), alpm_pkg_get_version(spkg)); - trans->add = alpm_list_add(trans->add, spkg); - - return(0); -} - -static int sync_group(alpm_list_t *dbs_sync, const char *target) -{ - alpm_list_t *i, *j; - alpm_list_t *known_pkgs = NULL; - pmgrp_t *grp; - int found = 0; - - ALPM_LOG_FUNC; - - _alpm_log(PM_LOG_DEBUG, "%s package not found, searching for group...\n", target); - for(i = dbs_sync; i; i = i->next) { - pmdb_t *db = i->data; - grp = alpm_db_readgrp(db, target); - if(grp) { - found = 1; - for(j = alpm_grp_get_pkgs(grp); j; j = j->next) { - pmpkg_t *pkg = j->data; - - /* check if group member is ignored */ - if(_alpm_pkg_should_ignore(pkg)) { - int install = 0; - QUESTION(handle->trans, PM_TRANS_CONV_INSTALL_IGNOREPKG, pkg, - NULL, NULL, &install); - if(install == 0) { - _alpm_log(PM_LOG_WARNING, _("skipping target: %s\n"), alpm_pkg_get_name(pkg)); - continue; - } - } - - if(sync_pkg(pkg, known_pkgs) == -1) { - if(pm_errno == PM_ERR_TRANS_DUP_TARGET || pm_errno == PM_ERR_PKG_IGNORED) { - /* just skip duplicate or ignored targets */ - continue; - } else { - alpm_list_free(known_pkgs); - return(-1); - } - } - known_pkgs = alpm_list_add(known_pkgs, pkg); + if(_alpm_pkg_should_ignore(pkg)) { + ignorelist = alpm_list_add(ignorelist, pkg); + int install = 0; + QUESTION(handle->trans, PM_TRANS_CONV_INSTALL_IGNOREPKG, pkg, + NULL, NULL, &install); + if(!install) + continue; + } + if(!_alpm_pkg_find(pkgs, alpm_pkg_get_name(pkg))) { + pkgs = alpm_list_add(pkgs, pkg); } } } - alpm_list_free(known_pkgs); - - if(!found) { - /* pass through any 'found but ignored' errors */ - if(pm_errno != PM_ERR_PKG_IGNORED) { - pm_errno = PM_ERR_PKG_NOT_FOUND; - } - return(-1); - } - - return(0); -} - -static int sync_target(alpm_list_t *dbs_sync, const char *target) -{ - pmpkg_t *spkg; - pmdepend_t *dep; /* provisions and dependencies are also allowed */ - - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(target != NULL && strlen(target) != 0, RET_ERR(PM_ERR_WRONG_ARGS, -1)); - ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1)); - - dep = _alpm_splitdep(target); - spkg = _alpm_resolvedep(dep, dbs_sync, NULL, 1); - _alpm_dep_free(dep); - - if(spkg != NULL) { - return(sync_pkg(spkg, handle->trans->add)); - } - - return(sync_group(dbs_sync, target)); -} - -/** Add a sync target to the transaction. - * @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) -{ - alpm_list_t *i; - alpm_list_t *dbs_sync; - - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1)); - dbs_sync = handle->dbs_sync; - - /* we are looking for a package in a specific database */ - alpm_list_t *dbs = NULL; - _alpm_log(PM_LOG_DEBUG, "searching for target '%s' in repo '%s'\n", target, dbname); - for(i = dbs_sync; i; i = i->next) { - pmdb_t *db = i->data; - if(strcmp(db->treename, dbname) == 0) { - dbs = alpm_list_add(NULL, db); - break; - } - } - if(dbs == NULL) { - RET_ERR(PM_ERR_PKG_REPO_NOT_FOUND, -1); - } - int ret = sync_target(dbs, target); - alpm_list_free(dbs); - return(ret); -} - -/** Add a sync target to the transaction. - * @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) -{ - alpm_list_t *dbs_sync; - - ALPM_LOG_FUNC; - - /* Sanity checks */ - ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1)); - dbs_sync = handle->dbs_sync; - - return(sync_target(dbs_sync,target)); + alpm_list_free(ignorelist); + return(pkgs); } /** Compute the size of the files that will be downloaded to install a @@ -385,7 +254,7 @@ static int compute_download_size(pmpkg_t *newpkg) char *fpath; off_t size = 0; - if(newpkg->origin == PKG_FROM_FILE) { + if(newpkg->origin != PKG_FROM_SYNCDB) { newpkg->infolevel |= INFRQ_DSIZE; newpkg->download_size = 0; return(0); @@ -464,7 +333,7 @@ int _alpm_sync_prepare(pmtrans_t *trans, pmdb_t *db_local, alpm_list_t *dbs_sync /* Compute the fake local database for resolvedeps (partial fix for the phonon/qt issue) */ alpm_list_t *localpkgs = alpm_list_diff(_alpm_db_get_pkgcache(db_local), trans->add, _alpm_pkg_cmp); - /* Resolve packages in the transaction one at a time, in addtion + /* Resolve packages in the transaction one at a time, in addition building up a list of packages which could not be resolved. */ for(i = trans->add; i; i = i->next) { pmpkg_t *pkg = i->data; @@ -549,10 +418,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 { @@ -822,7 +691,7 @@ int _alpm_sync_commit(pmtrans_t *trans, pmdb_t *db_local, alpm_list_t **data) { alpm_list_t *i, *j, *files = NULL; alpm_list_t *deltas = NULL; - int replaces = 0; + size_t numtargs, current = 0, replaces = 0; int errors = 0; const char *cachedir = NULL; int ret = -1; @@ -854,7 +723,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); @@ -949,14 +818,18 @@ int _alpm_sync_commit(pmtrans_t *trans, pmdb_t *db_local, alpm_list_t **data) } /* Check integrity of packages */ + numtargs = alpm_list_count(trans->add); EVENT(trans, PM_TRANS_EVT_INTEGRITY_START, NULL, NULL); errors = 0; - for(i = trans->add; i; i = i->next) { + for(i = trans->add; i; i = i->next, current++) { pmpkg_t *spkg = i->data; + int percent = (current * 100) / numtargs; if(spkg->origin == PKG_FROM_FILE) { continue; /* pkg_load() has been already called, this package is valid */ } + PROGRESS(trans, PM_TRANS_PROGRESS_INTEGRITY_START, "", percent, + numtargs, current); const char *filename = alpm_pkg_get_filename(spkg); const char *md5sum = alpm_pkg_get_md5sum(spkg); @@ -983,11 +856,14 @@ int _alpm_sync_commit(pmtrans_t *trans, pmdb_t *db_local, alpm_list_t **data) i->data = pkgfile; _alpm_pkg_free_trans(spkg); /* spkg has been removed from the target list */ } + PROGRESS(trans, PM_TRANS_PROGRESS_INTEGRITY_START, "", 100, + numtargs, current); + EVENT(trans, PM_TRANS_EVT_INTEGRITY_DONE, NULL, NULL); if(errors) { pm_errno = PM_ERR_PKG_INVALID; goto error; } - EVENT(trans, PM_TRANS_EVT_INTEGRITY_DONE, NULL, NULL); + if(trans->flags & PM_TRANS_FLAG_DOWNLOADONLY) { ret = 0; goto error; @@ -1018,6 +894,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/sync.h b/lib/libalpm/sync.h index 000a09cc..90a2d40d 100644 --- a/lib/libalpm/sync.h +++ b/lib/libalpm/sync.h @@ -1,7 +1,7 @@ /* * sync.h * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> * Copyright (c) 2005, 2006 by Miklos Vajna <vmiklos@frugalware.org> diff --git a/lib/libalpm/trans.c b/lib/libalpm/trans.c index 02612ec1..4b29f9a8 100644 --- a/lib/libalpm/trans.c +++ b/lib/libalpm/trans.c @@ -1,7 +1,7 @@ /* * trans.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> * Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu> @@ -31,6 +31,8 @@ #include <sys/stat.h> #include <sys/statvfs.h> #include <errno.h> +#include <limits.h> +#include <fcntl.h> /* libalpm */ #include "trans.h" @@ -44,13 +46,57 @@ #include "sync.h" #include "alpm.h" #include "deps.h" -#include "cache.h" /** \addtogroup alpm_trans Transaction Functions * @brief Functions to manipulate libalpm transactions * @{ */ +/* Create a lock file */ +static int make_lock(pmhandle_t *handle) +{ + int fd; + char *dir, *ptr; + + /* create the dir of the lockfile first */ + dir = strdup(handle->lockfile); + ptr = strrchr(dir, '/'); + if(ptr) { + *ptr = '\0'; + } + if(_alpm_makepath(dir)) { + FREE(dir); + return(-1); + } + FREE(dir); + + do { + fd = open(handle->lockfile, O_WRONLY | O_CREAT | O_EXCL, 0000); + } while (fd == -1 && errno == EINTR); + if(fd > 0) { + FILE *f = fdopen(fd, "w"); + fprintf(f, "%ld\n", (long)getpid()); + fflush(f); + fsync(fd); + handle->lckstream = f; + return(0); + } + return(-1); +} + +/* Remove a lock file */ +static int remove_lock(pmhandle_t *handle) +{ + if(handle->lckstream != NULL) { + fclose(handle->lckstream); + handle->lckstream = NULL; + } + if(unlink(handle->lockfile) == -1 && errno != ENOENT) { + return(-1); + } + return(0); +} + /** Initialize the transaction. * @param flags flags of the transaction (like nodeps, etc) * @param event event callback function pointer @@ -59,10 +105,12 @@ * @return 0 on success, -1 on error (pm_errno is set accordingly) */ int SYMEXPORT alpm_trans_init(pmtransflag_t flags, - alpm_trans_cb_event event, alpm_trans_cb_conv conv, - alpm_trans_cb_progress progress) + alpm_trans_cb_event event, alpm_trans_cb_conv conv, + alpm_trans_cb_progress progress) { pmtrans_t *trans; + const int required_db_version = 2; + int db_version; ALPM_LOG_FUNC; @@ -73,12 +121,20 @@ int SYMEXPORT alpm_trans_init(pmtransflag_t flags, /* lock db */ if(!(flags & PM_TRANS_FLAG_NOLOCK)) { - handle->lckfd = _alpm_lckmk(); - if(handle->lckfd == -1) { + if(make_lock(handle)) { RET_ERR(PM_ERR_HANDLE_LOCK, -1); } } + /* check database version */ + db_version = _alpm_db_version(handle->db_local); + if(db_version < required_db_version) { + _alpm_log(PM_LOG_ERROR, + _("%s database version is too old\n"), handle->db_local->treename); + remove_lock(handle); + RET_ERR(PM_ERR_DB_VERSION, -1); + } + trans = _alpm_trans_new(); if(trans == NULL) { RET_ERR(PM_ERR_MEMORY, -1); @@ -218,7 +274,7 @@ int SYMEXPORT alpm_trans_commit(alpm_list_t **data) /** Interrupt a transaction. * @return 0 on success, -1 on error (pm_errno is set accordingly) */ -int SYMEXPORT alpm_trans_interrupt() +int SYMEXPORT alpm_trans_interrupt(void) { pmtrans_t *trans; @@ -240,7 +296,7 @@ int SYMEXPORT alpm_trans_interrupt() /** Release a transaction. * @return 0 on success, -1 on error (pm_errno is set accordingly) */ -int SYMEXPORT alpm_trans_release() +int SYMEXPORT alpm_trans_release(void) { pmtrans_t *trans; @@ -260,14 +316,7 @@ int SYMEXPORT alpm_trans_release() /* unlock db */ if(!nolock_flag) { - if(handle->lckfd != -1) { - int fd; - do { - fd = close(handle->lckfd); - } while(fd == -1 && errno == EINTR); - handle->lckfd = -1; - } - if(_alpm_lckrm()) { + if(remove_lock(handle)) { _alpm_log(PM_LOG_WARNING, _("could not remove lock file %s\n"), alpm_option_get_lockfile()); alpm_logaction("warning: could not remove lock file %s\n", @@ -280,7 +329,7 @@ int SYMEXPORT alpm_trans_release() /** @} */ -pmtrans_t *_alpm_trans_new() +pmtrans_t *_alpm_trans_new(void) { pmtrans_t *trans; @@ -305,7 +354,6 @@ void _alpm_trans_free(pmtrans_t *trans) alpm_list_free_inner(trans->remove, (alpm_list_fn_free)_alpm_pkg_free); alpm_list_free(trans->remove); - FREELIST(trans->skip_add); FREELIST(trans->skip_remove); FREE(trans); @@ -323,11 +371,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 +392,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 +420,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 +450,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/trans.h b/lib/libalpm/trans.h index 51136423..6702881b 100644 --- a/lib/libalpm/trans.h +++ b/lib/libalpm/trans.h @@ -1,7 +1,7 @@ /* * trans.h * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> * Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu> @@ -41,7 +41,6 @@ struct __pmtrans_t { pmtransstate_t state; alpm_list_t *add; /* list of (pmpkg_t *) */ alpm_list_t *remove; /* list of (pmpkg_t *) */ - alpm_list_t *skip_add; /* list of (char *) */ alpm_list_t *skip_remove; /* list of (char *) */ alpm_trans_cb_event cb_event; alpm_trans_cb_conv cb_conv; diff --git a/lib/libalpm/util.c b/lib/libalpm/util.c index 32eaa442..9281f0e6 100644 --- a/lib/libalpm/util.c +++ b/lib/libalpm/util.c @@ -1,7 +1,7 @@ /* * util.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> * Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu> @@ -28,7 +28,6 @@ #include <stdlib.h> #include <stdarg.h> #include <string.h> -#include <fcntl.h> #include <unistd.h> #include <ctype.h> #include <dirent.h> @@ -38,18 +37,24 @@ #include <sys/types.h> #include <sys/stat.h> #include <sys/wait.h> +#include <locale.h> /* setlocale */ /* libarchive */ #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 */ @@ -196,46 +209,6 @@ char *_alpm_strtrim(char *str) return(str); } -/* Create a lock file */ -int _alpm_lckmk() -{ - int fd; - char *dir, *ptr; - const char *file = alpm_option_get_lockfile(); - - /* create the dir of the lockfile first */ - dir = strdup(file); - ptr = strrchr(dir, '/'); - if(ptr) { - *ptr = '\0'; - } - _alpm_makepath(dir); - FREE(dir); - - do { - fd = open(file, O_WRONLY | O_CREAT | O_EXCL, 0000); - } while (fd == -1 && errno == EINTR); - if(fd > 0) { - FILE *f = fdopen(fd, "w"); - fprintf(f, "%ld\n", (long)getpid()); - fflush(f); - fsync(fd); - fclose(f); - return(fd); - } - return(-1); -} - -/* Remove a lock file */ -int _alpm_lckrm() -{ - const char *file = alpm_option_get_lockfile(); - if(unlink(file) == -1 && errno != ENOENT) { - return(-1); - } - return(0); -} - /* Compression functions */ /** @@ -347,7 +320,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 +337,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 +371,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 +417,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 +440,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 +460,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 +543,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); } } @@ -574,19 +567,20 @@ char *_alpm_filecache_find(const char* filename) char path[PATH_MAX]; char *retpath; alpm_list_t *i; + struct stat buf; /* Loop through the cache dirs until we find a matching file */ for(i = alpm_option_get_cachedirs(); i; i = alpm_list_next(i)) { snprintf(path, PATH_MAX, "%s%s", (char*)alpm_list_getdata(i), filename); - if(access(path, R_OK) == 0) { + if(stat(path, &buf) == 0 && S_ISREG(buf.st_mode)) { retpath = strdup(path); _alpm_log(PM_LOG_DEBUG, "found cached pkg: %s\n", retpath); return(retpath); } } /* package wasn't found in any cachedir */ - return(NULL); + RET_ERR(PM_ERR_PKG_NOT_FOUND, NULL); } /** Check the alpm cachedirs for existance and find a writable one. @@ -635,7 +629,7 @@ int _alpm_lstat(const char *path, struct stat *buf) { int ret; char *newpath = strdup(path); - int len = strlen(newpath); + size_t len = strlen(newpath); /* strip the trailing slash if one exists */ if(len != 0 && newpath[len - 1] == '/') { @@ -648,6 +642,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 +695,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 +732,191 @@ 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 = (size_t)((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 = (size_t)(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 = (size_t)(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. + */ + const char *version, *end; + + if(target == NULL || pkg == NULL) { + return(-1); } + end = target + strlen(target); - /* always null terminate the buffer */ - *last = '\0'; + /* remove any trailing '/' */ + while (*(end - 1) == '/') { + --end; + } - return(line); + /* do the magic parsing- find the beginning of the version string + * by doing two iterations of same loop to lop off two hyphens */ + for(version = end - 1; *version && *version != '-'; version--); + for(version = version - 1; *version && *version != '-'; version--); + if(*version != '-' || version == target) { + return(-1); + } + + /* copy into fields and return */ + if(pkg->version) { + FREE(pkg->version); + } + /* version actually points to the dash, so need to increment 1 and account + * for potential end character */ + STRNDUP(pkg->version, version + 1, end - version - 1, + RET_ERR(PM_ERR_MEMORY, -1)); + + if(pkg->name) { + FREE(pkg->name); + } + STRNDUP(pkg->name, target, version - target, RET_ERR(PM_ERR_MEMORY, -1)); + pkg->name_hash = _alpm_hash_sdbm(pkg->name); + + 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(hash); +} + +long _alpm_parsedate(const char *line) +{ + if(isalpha((unsigned char)line[0])) { + /* 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); + setlocale(LC_TIME, ""); + return(mktime(&tmp_tm)); + } + return(atol(line)); +} + +#ifndef HAVE_STRNDUP +/* A quick and dirty implementation derived from glibc */ +static size_t strnlen(const char *s, size_t max) +{ + register const char *p; + for(p = s; *p && max--; ++p); + return(p - s); } +char *strndup(const char *s, size_t n) +{ + size_t len = strnlen(s, n); + char *new = (char *) malloc(len + 1); + + if (new == NULL) + return NULL; + + new[len] = '\0'; + return (char *) memcpy(new, s, len); +} +#endif + /* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/util.h b/lib/libalpm/util.h index 8a3154a7..d65f7734 100644 --- a/lib/libalpm/util.h +++ b/lib/libalpm/util.h @@ -1,7 +1,7 @@ /* * util.h * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> * Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu> @@ -27,6 +27,7 @@ #include "config.h" #include "alpm_list.h" +#include "package.h" /* pmpkg_t */ #include <stdio.h> #include <string.h> @@ -49,6 +50,7 @@ #define CALLOC(p, l, s, action) do { p = calloc(l, s); if(p == NULL) { ALLOC_FAIL(s); action; } } while(0) /* This strdup macro is NULL safe- copying NULL will yield NULL */ #define STRDUP(r, s, action) do { if(s != NULL) { r = strdup(s); if(r == NULL) { ALLOC_FAIL(strlen(s)); action; } } else { r = NULL; } } while(0) +#define STRNDUP(r, s, l, action) do { if(s != NULL) { r = strndup(s, l); if(r == NULL) { ALLOC_FAIL(strlen(s)); action; } } else { r = NULL; } } while(0) #define FREE(p) do { free(p); p = NULL; } while(0) @@ -58,36 +60,54 @@ _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_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); +long _alpm_parsedate(const char *line); #ifndef HAVE_STRSEP char *strsep(char **, const char *); #endif +#ifndef HAVE_STRNDUP +char *strndup(const char *s, size_t n); +#endif + /* check exported library symbols with: nm -C -D <lib> */ #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: */ diff --git a/lib/libalpm/version.c b/lib/libalpm/version.c index fb327df3..eba66210 100644 --- a/lib/libalpm/version.c +++ b/lib/libalpm/version.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 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 @@ -23,23 +23,66 @@ /* libalpm */ #include "util.h" -/** Compare two version strings and determine which one is 'newer'. - * Returns a value comparable to the way strcmp works. Returns 1 - * if a is newer than b, 0 if a and b are the same version, or -1 - * if b is newer than a. - * - * This function has been adopted from the rpmvercmp function located - * at lib/rpmvercmp.c, and was most recently updated against rpm - * version 4.4.2.3. Small modifications have been made to make it more - * consistent with the libalpm coding style. - * - * Keep in mind that the pkgrel is only compared if it is available - * on both versions handed to this function. For example, comparing - * 1.5-1 and 1.5 will yield 0; comparing 1.5-1 and 1.5-2 will yield - * -1 as expected. This is mainly for supporting versioned dependencies - * that do not include the pkgrel. +/** + * Some functions in this file have been adopted from the rpm source, notably + * 'rpmvercmp' located at lib/rpmvercmp.c and 'parseEVR' located at + * lib/rpmds.c. It was most recently updated against rpm version 4.8.1. Small + * modifications have been made to make it more consistent with the libalpm + * coding style. */ -int SYMEXPORT alpm_pkg_vercmp(const char *a, const char *b) + +/** + * Split EVR into epoch, version, and release components. + * @param evr [epoch:]version[-release] string + * @retval *ep pointer to epoch + * @retval *vp pointer to version + * @retval *rp pointer to release + */ +static void parseEVR(char *evr, const char **ep, const char **vp, + const char **rp) +{ + const char *epoch; + const char *version; + const char *release; + char *s, *se; + + s = evr; + /* s points to epoch terminator */ + while (*s && isdigit(*s)) s++; + /* se points to version terminator */ + se = strrchr(s, '-'); + + if(*s == ':') { + epoch = evr; + *s++ = '\0'; + version = s; + if(*epoch == '\0') { + epoch = "0"; + } + } else { + /* different from RPM- always assume 0 epoch */ + epoch = "0"; + version = evr; + } + if(se) { + *se++ = '\0'; + release = se; + } else { + release = NULL; + } + + if(ep) *ep = epoch; + if(vp) *vp = version; + if(rp) *rp = release; +} + +/** + * Compare alpha and numeric segments of two versions. + * return 1: a is newer than b + * 0: a and b are the same version + * -1: b is newer than a + */ +static int rpmvercmp(const char *a, const char *b) { char oldch1, oldch2; char *str1, *str2; @@ -49,13 +92,6 @@ int SYMEXPORT alpm_pkg_vercmp(const char *a, const char *b) int isnum; int ret = 0; - /* libalpm added code. ensure our strings are not null */ - if(!a) { - if(!b) return(0); - return(-1); - } - if(!b) return(1); - /* easy comparison to see if versions are identical */ if(strcmp(a, b) == 0) return(0); @@ -147,22 +183,6 @@ int SYMEXPORT alpm_pkg_vercmp(const char *a, const char *b) one = ptr1; *ptr2 = oldch2; two = ptr2; - - /* libalpm added code. check if version strings have hit the pkgrel - * portion. depending on which strings have hit, take correct action. - * this is all based on the premise that we only have one dash in - * the version string, and it separates pkgver from pkgrel. */ - if(*ptr1 == '-' && *ptr2 == '-') { - /* no-op, continue comparing since we are equivalent throughout */ - } else if(*ptr1 == '-') { - /* ptr1 has hit the pkgrel and ptr2 has not. continue version - * comparison after stripping the pkgrel from ptr1. */ - *ptr1 = '\0'; - } else if(*ptr2 == '-') { - /* ptr2 has hit the pkgrel and ptr1 has not. continue version - * comparison after stripping the pkgrel from ptr2. */ - *ptr2 = '\0'; - } } /* this catches the case where all numeric and alpha segments have */ @@ -179,7 +199,7 @@ int SYMEXPORT alpm_pkg_vercmp(const char *a, const char *b) * - if one is an alpha, two is newer. * - otherwise one is newer. * */ - if ( ( !*one && !isalpha((int)*two) ) + if ( (!*one && !isalpha((int)*two)) || isalpha((int)*one) ) { ret = -1; } else { @@ -192,4 +212,61 @@ cleanup: return(ret); } +/** Compare two version strings and determine which one is 'newer'. + * Returns a value comparable to the way strcmp works. Returns 1 + * if a is newer than b, 0 if a and b are the same version, or -1 + * if b is newer than a. + * + * Different epoch values for version strings will override any further + * comparison. If no epoch is provided, 0 is assumed. + * + * Keep in mind that the pkgrel is only compared if it is available + * on both versions handed to this function. For example, comparing + * 1.5-1 and 1.5 will yield 0; comparing 1.5-1 and 1.5-2 will yield + * -1 as expected. This is mainly for supporting versioned dependencies + * that do not include the pkgrel. + */ +int SYMEXPORT alpm_pkg_vercmp(const char *a, const char *b) +{ + char *full1, *full2; + const char *epoch1, *ver1, *rel1; + const char *epoch2, *ver2, *rel2; + int ret; + + /* ensure our strings are not null */ + if(!a && !b) { + return(0); + } else if(!a) { + return(-1); + } else if(!b) { + return(1); + } + /* another quick shortcut- if full version specs are equal */ + if(strcmp(a, b) == 0) { + return(0); + } + + /* Parse both versions into [epoch:]version[-release] triplets. We probably + * don't need epoch and release to support all the same magic, but it is + * easier to just run it all through the same code. */ + full1 = strdup(a); + full2 = strdup(b); + + /* parseEVR modifies passed in version, so have to dupe it first */ + parseEVR(full1, &epoch1, &ver1, &rel1); + parseEVR(full2, &epoch2, &ver2, &rel2); + + ret = rpmvercmp(epoch1, epoch2); + if(ret == 0) { + ret = rpmvercmp(ver1, ver2); + if(ret == 0 && rel1 && rel2) { + ret = rpmvercmp(rel1, rel2); + } + } + + free(full1); + free(full2); + return(ret); +} + /* vim: set ts=2 sw=2 noet: */ |