libalpm
Arch Linux Package Manager Library
sync.c
Go to the documentation of this file.
00001 /*
00002  *  sync.c
00003  *
00004  *  Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org>
00005  *  Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
00006  *  Copyright (c) 2005 by Aurelien Foret <orelien@chez.com>
00007  *  Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu>
00008  *  Copyright (c) 2005, 2006 by Miklos Vajna <vmiklos@frugalware.org>
00009  *
00010  *  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version.
00014  *
00015  *  This program is distributed in the hope that it will be useful,
00016  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  *  GNU General Public License for more details.
00019  *
00020  *  You should have received a copy of the GNU General Public License
00021  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00022  */
00023 
00024 #include <sys/types.h> /* off_t */
00025 #include <stdlib.h>
00026 #include <stdio.h>
00027 #include <string.h>
00028 #include <stdint.h> /* intmax_t */
00029 #include <unistd.h>
00030 #include <limits.h>
00031 
00032 /* libalpm */
00033 #include "sync.h"
00034 #include "alpm_list.h"
00035 #include "log.h"
00036 #include "package.h"
00037 #include "db.h"
00038 #include "deps.h"
00039 #include "conflict.h"
00040 #include "trans.h"
00041 #include "add.h"
00042 #include "util.h"
00043 #include "handle.h"
00044 #include "alpm.h"
00045 #include "dload.h"
00046 #include "delta.h"
00047 #include "remove.h"
00048 #include "diskspace.h"
00049 #include "signing.h"
00050 
00051 /** Check for new version of pkg in sync repos
00052  * (only the first occurrence is considered in sync)
00053  */
00054 alpm_pkg_t SYMEXPORT *alpm_sync_newversion(alpm_pkg_t *pkg, alpm_list_t *dbs_sync)
00055 {
00056     alpm_list_t *i;
00057     alpm_pkg_t *spkg = NULL;
00058 
00059     ASSERT(pkg != NULL, return NULL);
00060     pkg->handle->pm_errno = 0;
00061 
00062     for(i = dbs_sync; !spkg && i; i = i->next) {
00063         spkg = _alpm_db_get_pkgfromcache(i->data, pkg->name);
00064     }
00065 
00066     if(spkg == NULL) {
00067         _alpm_log(pkg->handle, ALPM_LOG_DEBUG, "'%s' not found in sync db => no upgrade\n",
00068                 pkg->name);
00069         return NULL;
00070     }
00071 
00072     /* compare versions and see if spkg is an upgrade */
00073     if(_alpm_pkg_compare_versions(spkg, pkg) > 0) {
00074         _alpm_log(pkg->handle, ALPM_LOG_DEBUG, "new version of '%s' found (%s => %s)\n",
00075                     pkg->name, pkg->version, spkg->version);
00076         return spkg;
00077     }
00078     /* spkg is not an upgrade */
00079     return NULL;
00080 }
00081 
00082 static int check_literal(alpm_handle_t *handle, alpm_pkg_t *lpkg,
00083         alpm_pkg_t *spkg, int enable_downgrade)
00084 {
00085     /* 1. literal was found in sdb */
00086     int cmp = _alpm_pkg_compare_versions(spkg, lpkg);
00087     if(cmp > 0) {
00088         _alpm_log(handle, ALPM_LOG_DEBUG, "new version of '%s' found (%s => %s)\n",
00089                 lpkg->name, lpkg->version, spkg->version);
00090         /* check IgnorePkg/IgnoreGroup */
00091         if(_alpm_pkg_should_ignore(handle, spkg)
00092                 || _alpm_pkg_should_ignore(handle, lpkg)) {
00093             _alpm_log(handle, ALPM_LOG_WARNING, _("%s: ignoring package upgrade (%s => %s)\n"),
00094                     lpkg->name, lpkg->version, spkg->version);
00095         } else {
00096             _alpm_log(handle, ALPM_LOG_DEBUG, "adding package %s-%s to the transaction targets\n",
00097                     spkg->name, spkg->version);
00098             return 1;
00099         }
00100     } else if(cmp < 0) {
00101         if(enable_downgrade) {
00102             /* check IgnorePkg/IgnoreGroup */
00103             if(_alpm_pkg_should_ignore(handle, spkg)
00104                     || _alpm_pkg_should_ignore(handle, lpkg)) {
00105                 _alpm_log(handle, ALPM_LOG_WARNING, _("%s: ignoring package downgrade (%s => %s)\n"),
00106                         lpkg->name, lpkg->version, spkg->version);
00107             } else {
00108                 _alpm_log(handle, ALPM_LOG_WARNING, _("%s: downgrading from version %s to version %s\n"),
00109                         lpkg->name, lpkg->version, spkg->version);
00110                 return 1;
00111             }
00112         } else {
00113             alpm_db_t *sdb = alpm_pkg_get_db(spkg);
00114             _alpm_log(handle, ALPM_LOG_WARNING, _("%s: local (%s) is newer than %s (%s)\n"),
00115                     lpkg->name, lpkg->version, sdb->treename, spkg->version);
00116         }
00117     }
00118     return 0;
00119 }
00120 
00121 static alpm_list_t *check_replacers(alpm_handle_t *handle, alpm_pkg_t *lpkg,
00122         alpm_db_t *sdb)
00123 {
00124     /* 2. search for replacers in sdb */
00125     alpm_list_t *replacers = NULL;
00126     alpm_list_t *k;
00127     _alpm_log(handle, ALPM_LOG_DEBUG,
00128             "searching for replacements for %s\n", lpkg->name);
00129     for(k = _alpm_db_get_pkgcache(sdb); k; k = k->next) {
00130         int found = 0;
00131         alpm_pkg_t *spkg = k->data;
00132         alpm_list_t *l;
00133         for(l = alpm_pkg_get_replaces(spkg); l; l = l->next) {
00134             alpm_depend_t *replace = l->data;
00135             /* we only want to consider literal matches at this point. */
00136             if(_alpm_depcmp_literal(lpkg, replace)) {
00137                 found = 1;
00138                 break;
00139             }
00140         }
00141         if(found) {
00142             int doreplace = 0;
00143             alpm_pkg_t *tpkg;
00144             /* check IgnorePkg/IgnoreGroup */
00145             if(_alpm_pkg_should_ignore(handle, spkg)
00146                     || _alpm_pkg_should_ignore(handle, lpkg)) {
00147                 _alpm_log(handle, ALPM_LOG_WARNING,
00148                         _("ignoring package replacement (%s-%s => %s-%s)\n"),
00149                         lpkg->name, lpkg->version, spkg->name, spkg->version);
00150                 continue;
00151             }
00152 
00153             QUESTION(handle, ALPM_QUESTION_REPLACE_PKG, lpkg, spkg,
00154                     sdb->treename, &doreplace);
00155             if(!doreplace) {
00156                 continue;
00157             }
00158 
00159             /* If spkg is already in the target list, we append lpkg to spkg's
00160              * removes list */
00161             tpkg = _alpm_pkg_find(handle->trans->add, spkg->name);
00162             if(tpkg) {
00163                 /* sanity check, multiple repos can contain spkg->name */
00164                 if(tpkg->origin_data.db != sdb) {
00165                     _alpm_log(handle, ALPM_LOG_WARNING, _("cannot replace %s by %s\n"),
00166                             lpkg->name, spkg->name);
00167                     continue;
00168                 }
00169                 _alpm_log(handle, ALPM_LOG_DEBUG, "appending %s to the removes list of %s\n",
00170                         lpkg->name, tpkg->name);
00171                 tpkg->removes = alpm_list_add(tpkg->removes, lpkg);
00172                 /* check the to-be-replaced package's reason field */
00173                 if(alpm_pkg_get_reason(lpkg) == ALPM_PKG_REASON_EXPLICIT) {
00174                     tpkg->reason = ALPM_PKG_REASON_EXPLICIT;
00175                 }
00176             } else {
00177                 /* add spkg to the target list */
00178                 /* copy over reason */
00179                 spkg->reason = alpm_pkg_get_reason(lpkg);
00180                 spkg->removes = alpm_list_add(NULL, lpkg);
00181                 _alpm_log(handle, ALPM_LOG_DEBUG,
00182                         "adding package %s-%s to the transaction targets\n",
00183                         spkg->name, spkg->version);
00184                 replacers = alpm_list_add(replacers, spkg);
00185             }
00186         }
00187     }
00188     return replacers;
00189 }
00190 
00191 /** Search for packages to upgrade and add them to the transaction. */
00192 int SYMEXPORT alpm_sync_sysupgrade(alpm_handle_t *handle, int enable_downgrade)
00193 {
00194     alpm_list_t *i, *j;
00195     alpm_trans_t *trans;
00196 
00197     CHECK_HANDLE(handle, return -1);
00198     trans = handle->trans;
00199     ASSERT(trans != NULL, RET_ERR(handle, ALPM_ERR_TRANS_NULL, -1));
00200     ASSERT(trans->state == STATE_INITIALIZED, RET_ERR(handle, ALPM_ERR_TRANS_NOT_INITIALIZED, -1));
00201 
00202     _alpm_log(handle, ALPM_LOG_DEBUG, "checking for package upgrades\n");
00203     for(i = _alpm_db_get_pkgcache(handle->db_local); i; i = i->next) {
00204         alpm_pkg_t *lpkg = i->data;
00205 
00206         if(_alpm_pkg_find(trans->add, lpkg->name)) {
00207             _alpm_log(handle, ALPM_LOG_DEBUG, "%s is already in the target list -- skipping\n", lpkg->name);
00208             continue;
00209         }
00210 
00211         /* Search for literal then replacers in each sync database. */
00212         for(j = handle->dbs_sync; j; j = j->next) {
00213             alpm_db_t *sdb = j->data;
00214             /* Check sdb */
00215             alpm_pkg_t *spkg = _alpm_db_get_pkgfromcache(sdb, lpkg->name);
00216             int literal_upgrade = 0;
00217             if(spkg) {
00218                 literal_upgrade = check_literal(handle, lpkg, spkg, enable_downgrade);
00219                 if(literal_upgrade) {
00220                     trans->add = alpm_list_add(trans->add, spkg);
00221                 }
00222                 /* jump to next local package */
00223                 break;
00224             } else {
00225                 alpm_list_t *replacers;
00226                 replacers = check_replacers(handle, lpkg, sdb);
00227                 if(replacers) {
00228                     trans->add = alpm_list_join(trans->add, replacers);
00229                 }
00230             }
00231         }
00232     }
00233 
00234     return 0;
00235 }
00236 
00237 /** Find group members across a list of databases.
00238  * If a member exists in several databases, only the first database is used.
00239  * IgnorePkg is also handled.
00240  * @param dbs the list of alpm_db_t *
00241  * @param name the name of the group
00242  * @return the list of alpm_pkg_t * (caller is responsible for alpm_list_free)
00243  */
00244 alpm_list_t SYMEXPORT *alpm_find_group_pkgs(alpm_list_t *dbs,
00245         const char *name)
00246 {
00247     alpm_list_t *i, *j, *pkgs = NULL, *ignorelist = NULL;
00248 
00249     for(i = dbs; i; i = i->next) {
00250         alpm_db_t *db = i->data;
00251         alpm_group_t *grp = alpm_db_readgroup(db, name);
00252 
00253         if(!grp)
00254             continue;
00255 
00256         for(j = grp->packages; j; j = j->next) {
00257             alpm_pkg_t *pkg = j->data;
00258 
00259             if(_alpm_pkg_find(ignorelist, pkg->name)) {
00260                 continue;
00261             }
00262             if(_alpm_pkg_should_ignore(db->handle, pkg)) {
00263                 ignorelist = alpm_list_add(ignorelist, pkg);
00264                 int install = 0;
00265                 QUESTION(db->handle, ALPM_QUESTION_INSTALL_IGNOREPKG, pkg,
00266                         NULL, NULL, &install);
00267                 if(!install)
00268                     continue;
00269             }
00270             if(!_alpm_pkg_find(pkgs, pkg->name)) {
00271                 pkgs = alpm_list_add(pkgs, pkg);
00272             }
00273         }
00274     }
00275     alpm_list_free(ignorelist);
00276     return pkgs;
00277 }
00278 
00279 /** Compute the size of the files that will be downloaded to install a
00280  * package.
00281  * @param newpkg the new package to upgrade to
00282  */
00283 static int compute_download_size(alpm_pkg_t *newpkg)
00284 {
00285     const char *fname;
00286     char *fpath, *fnamepart = NULL;
00287     off_t size = 0;
00288     alpm_handle_t *handle = newpkg->handle;
00289     int ret = 0;
00290 
00291     if(newpkg->origin != PKG_FROM_SYNCDB) {
00292         newpkg->infolevel |= INFRQ_DSIZE;
00293         newpkg->download_size = 0;
00294         return 0;
00295     }
00296 
00297     ASSERT(newpkg->filename != NULL, RET_ERR(handle, ALPM_ERR_PKG_INVALID_NAME, -1));
00298     fname = newpkg->filename;
00299     fpath = _alpm_filecache_find(handle, fname);
00300 
00301     /* downloaded file exists, so there's nothing to grab */
00302     if(fpath) {
00303         size = 0;
00304         goto finish;
00305     }
00306 
00307     CALLOC(fnamepart, strlen(fname) + 6, sizeof(char), return -1);
00308     sprintf(fnamepart, "%s.part", fname);
00309     fpath = _alpm_filecache_find(handle, fnamepart);
00310     if(fpath) {
00311         struct stat st;
00312         if(stat(fpath, &st) == 0) {
00313             /* subtract the size of the .part file */
00314             _alpm_log(handle, ALPM_LOG_DEBUG, "using (package - .part) size\n");
00315             size = newpkg->size - st.st_size;
00316             size = size < 0 ? 0 : size;
00317         }
00318 
00319         /* tell the caller that we have a partial */
00320         ret = 1;
00321     } else if(handle->usedelta) {
00322         off_t dltsize;
00323 
00324         dltsize = _alpm_shortest_delta_path(handle, newpkg->deltas,
00325                 newpkg->filename, &newpkg->delta_path);
00326 
00327         if(newpkg->delta_path && (dltsize < newpkg->size * MAX_DELTA_RATIO)) {
00328             _alpm_log(handle, ALPM_LOG_DEBUG, "using delta size\n");
00329             size = dltsize;
00330         } else {
00331             _alpm_log(handle, ALPM_LOG_DEBUG, "using package size\n");
00332             size = newpkg->size;
00333             alpm_list_free(newpkg->delta_path);
00334             newpkg->delta_path = NULL;
00335         }
00336     } else {
00337         size = newpkg->size;
00338     }
00339 
00340 finish:
00341     _alpm_log(handle, ALPM_LOG_DEBUG, "setting download size %jd for pkg %s\n",
00342             (intmax_t)size, newpkg->name);
00343 
00344     newpkg->infolevel |= INFRQ_DSIZE;
00345     newpkg->download_size = size;
00346 
00347     FREE(fpath);
00348     FREE(fnamepart);
00349 
00350     return ret;
00351 }
00352 
00353 int _alpm_sync_prepare(alpm_handle_t *handle, alpm_list_t **data)
00354 {
00355     alpm_list_t *i, *j;
00356     alpm_list_t *deps = NULL;
00357     alpm_list_t *unresolvable = NULL;
00358     size_t from_sync = 0;
00359     int ret = 0;
00360     alpm_trans_t *trans = handle->trans;
00361 
00362     if(data) {
00363         *data = NULL;
00364     }
00365 
00366     for(i = trans->add; i; i = i->next) {
00367         alpm_pkg_t *spkg = i->data;
00368         from_sync += (spkg->origin == PKG_FROM_SYNCDB);
00369     }
00370 
00371     /* ensure all sync database are valid if we will be using them */
00372     for(i = handle->dbs_sync; i; i = i->next) {
00373         const alpm_db_t *db = i->data;
00374         if(db->status & DB_STATUS_INVALID) {
00375             RET_ERR(handle, ALPM_ERR_DB_INVALID, -1);
00376         }
00377         /* missing databases are not allowed if we have sync targets */
00378         if(from_sync && db->status & DB_STATUS_MISSING) {
00379             RET_ERR(handle, ALPM_ERR_DB_NOT_FOUND, -1);
00380         }
00381     }
00382 
00383     if(!(trans->flags & ALPM_TRANS_FLAG_NODEPS)) {
00384         alpm_list_t *resolved = NULL;
00385         alpm_list_t *remove = NULL;
00386         alpm_list_t *localpkgs;
00387 
00388         /* Build up list by repeatedly resolving each transaction package */
00389         /* Resolve targets dependencies */
00390         EVENT(handle, ALPM_EVENT_RESOLVEDEPS_START, NULL, NULL);
00391         _alpm_log(handle, ALPM_LOG_DEBUG, "resolving target's dependencies\n");
00392 
00393         /* build remove list for resolvedeps */
00394         for(i = trans->add; i; i = i->next) {
00395             alpm_pkg_t *spkg = i->data;
00396             for(j = spkg->removes; j; j = j->next) {
00397                 remove = alpm_list_add(remove, j->data);
00398             }
00399         }
00400 
00401         /* Compute the fake local database for resolvedeps (partial fix for the
00402          * phonon/qt issue) */
00403         localpkgs = alpm_list_diff(_alpm_db_get_pkgcache(handle->db_local),
00404                 trans->add, _alpm_pkg_cmp);
00405 
00406         /* Resolve packages in the transaction one at a time, in addition
00407            building up a list of packages which could not be resolved. */
00408         for(i = trans->add; i; i = i->next) {
00409             alpm_pkg_t *pkg = i->data;
00410             if(_alpm_resolvedeps(handle, localpkgs, pkg, trans->add,
00411                         &resolved, remove, data) == -1) {
00412                 unresolvable = alpm_list_add(unresolvable, pkg);
00413             }
00414             /* Else, [resolved] now additionally contains [pkg] and all of its
00415                dependencies not already on the list */
00416         }
00417         alpm_list_free(localpkgs);
00418         alpm_list_free(remove);
00419 
00420         /* If there were unresolvable top-level packages, prompt the user to
00421            see if they'd like to ignore them rather than failing the sync */
00422         if(unresolvable != NULL) {
00423             int remove_unresolvable = 0;
00424             alpm_errno_t saved_err = handle->pm_errno;
00425             QUESTION(handle, ALPM_QUESTION_REMOVE_PKGS, unresolvable,
00426                     NULL, NULL, &remove_unresolvable);
00427             if(remove_unresolvable) {
00428                 /* User wants to remove the unresolvable packages from the
00429                    transaction. The packages will be removed from the actual
00430                    transaction when the transaction packages are replaced with a
00431                    dependency-reordered list below */
00432                 handle->pm_errno = 0;
00433                 if(data) {
00434                     alpm_list_free_inner(*data, (alpm_list_fn_free)_alpm_depmiss_free);
00435                     alpm_list_free(*data);
00436                     *data = NULL;
00437                 }
00438             } else {
00439                 /* pm_errno was set by resolvedeps, callback may have overwrote it */
00440                 handle->pm_errno = saved_err;
00441                 alpm_list_free(resolved);
00442                 ret = -1;
00443                 goto cleanup;
00444             }
00445         }
00446 
00447         /* Set DEPEND reason for pulled packages */
00448         for(i = resolved; i; i = i->next) {
00449             alpm_pkg_t *pkg = i->data;
00450             if(!_alpm_pkg_find(trans->add, pkg->name)) {
00451                 pkg->reason = ALPM_PKG_REASON_DEPEND;
00452             }
00453         }
00454 
00455         /* Unresolvable packages will be removed from the target list; set these
00456          * aside in the transaction as a list we won't operate on. If we free them
00457          * before the end of the transaction, we may kill pointers the frontend
00458          * holds to package objects. */
00459         trans->unresolvable = unresolvable;
00460 
00461         /* re-order w.r.t. dependencies */
00462         alpm_list_free(trans->add);
00463         trans->add = _alpm_sortbydeps(handle, resolved, 0);
00464         alpm_list_free(resolved);
00465 
00466         EVENT(handle, ALPM_EVENT_RESOLVEDEPS_DONE, NULL, NULL);
00467     }
00468 
00469     if(!(trans->flags & ALPM_TRANS_FLAG_NOCONFLICTS)) {
00470         /* check for inter-conflicts and whatnot */
00471         EVENT(handle, ALPM_EVENT_INTERCONFLICTS_START, NULL, NULL);
00472 
00473         _alpm_log(handle, ALPM_LOG_DEBUG, "looking for conflicts\n");
00474 
00475         /* 1. check for conflicts in the target list */
00476         _alpm_log(handle, ALPM_LOG_DEBUG, "check targets vs targets\n");
00477         deps = _alpm_innerconflicts(handle, trans->add);
00478 
00479         for(i = deps; i; i = i->next) {
00480             alpm_conflict_t *conflict = i->data;
00481             alpm_pkg_t *rsync, *sync, *sync1, *sync2;
00482 
00483             /* have we already removed one of the conflicting targets? */
00484             sync1 = _alpm_pkg_find(trans->add, conflict->package1);
00485             sync2 = _alpm_pkg_find(trans->add, conflict->package2);
00486             if(!sync1 || !sync2) {
00487                 continue;
00488             }
00489 
00490             _alpm_log(handle, ALPM_LOG_DEBUG, "conflicting packages in the sync list: '%s' <-> '%s'\n",
00491                     conflict->package1, conflict->package2);
00492 
00493             /* if sync1 provides sync2, we remove sync2 from the targets, and vice versa */
00494             alpm_depend_t *dep1 = _alpm_splitdep(conflict->package1);
00495             alpm_depend_t *dep2 = _alpm_splitdep(conflict->package2);
00496             if(_alpm_depcmp(sync1, dep2)) {
00497                 rsync = sync2;
00498                 sync = sync1;
00499             } else if(_alpm_depcmp(sync2, dep1)) {
00500                 rsync = sync1;
00501                 sync = sync2;
00502             } else {
00503                 _alpm_log(handle, ALPM_LOG_ERROR, _("unresolvable package conflicts detected\n"));
00504                 handle->pm_errno = ALPM_ERR_CONFLICTING_DEPS;
00505                 ret = -1;
00506                 if(data) {
00507                     alpm_conflict_t *newconflict = _alpm_conflict_dup(conflict);
00508                     if(newconflict) {
00509                         *data = alpm_list_add(*data, newconflict);
00510                     }
00511                 }
00512                 alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_conflict_free);
00513                 alpm_list_free(deps);
00514                 _alpm_dep_free(dep1);
00515                 _alpm_dep_free(dep2);
00516                 goto cleanup;
00517             }
00518             _alpm_dep_free(dep1);
00519             _alpm_dep_free(dep2);
00520 
00521             /* Prints warning */
00522             _alpm_log(handle, ALPM_LOG_WARNING,
00523                     _("removing '%s' from target list because it conflicts with '%s'\n"),
00524                     rsync->name, sync->name);
00525             trans->add = alpm_list_remove(trans->add, rsync, _alpm_pkg_cmp, NULL);
00526             /* rsync is not a transaction target anymore */
00527             trans->unresolvable = alpm_list_add(trans->unresolvable, rsync);
00528             continue;
00529         }
00530 
00531         alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_conflict_free);
00532         alpm_list_free(deps);
00533         deps = NULL;
00534 
00535         /* 2. we check for target vs db conflicts (and resolve)*/
00536         _alpm_log(handle, ALPM_LOG_DEBUG, "check targets vs db and db vs targets\n");
00537         deps = _alpm_outerconflicts(handle->db_local, trans->add);
00538 
00539         for(i = deps; i; i = i->next) {
00540             alpm_conflict_t *conflict = i->data;
00541 
00542             /* if conflict->package2 (the local package) is not elected for removal,
00543                we ask the user */
00544             int found = 0;
00545             for(j = trans->add; j && !found; j = j->next) {
00546                 alpm_pkg_t *spkg = j->data;
00547                 if(_alpm_pkg_find(spkg->removes, conflict->package2)) {
00548                     found = 1;
00549                 }
00550             }
00551             if(found) {
00552                 continue;
00553             }
00554 
00555             _alpm_log(handle, ALPM_LOG_DEBUG, "package '%s' conflicts with '%s'\n",
00556                     conflict->package1, conflict->package2);
00557 
00558             alpm_pkg_t *sync = _alpm_pkg_find(trans->add, conflict->package1);
00559             alpm_pkg_t *local = _alpm_db_get_pkgfromcache(handle->db_local, conflict->package2);
00560             int doremove = 0;
00561             QUESTION(handle, ALPM_QUESTION_CONFLICT_PKG, conflict->package1,
00562                             conflict->package2, conflict->reason->name, &doremove);
00563             if(doremove) {
00564                 /* append to the removes list */
00565                 _alpm_log(handle, ALPM_LOG_DEBUG, "electing '%s' for removal\n", conflict->package2);
00566                 sync->removes = alpm_list_add(sync->removes, local);
00567             } else { /* abort */
00568                 _alpm_log(handle, ALPM_LOG_ERROR, _("unresolvable package conflicts detected\n"));
00569                 handle->pm_errno = ALPM_ERR_CONFLICTING_DEPS;
00570                 ret = -1;
00571                 if(data) {
00572                     alpm_conflict_t *newconflict = _alpm_conflict_dup(conflict);
00573                     if(newconflict) {
00574                         *data = alpm_list_add(*data, newconflict);
00575                     }
00576                 }
00577                 alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_conflict_free);
00578                 alpm_list_free(deps);
00579                 goto cleanup;
00580             }
00581         }
00582         EVENT(handle, ALPM_EVENT_INTERCONFLICTS_DONE, NULL, NULL);
00583         alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_conflict_free);
00584         alpm_list_free(deps);
00585     }
00586 
00587     /* Build trans->remove list */
00588     for(i = trans->add; i; i = i->next) {
00589         alpm_pkg_t *spkg = i->data;
00590         for(j = spkg->removes; j; j = j->next) {
00591             alpm_pkg_t *rpkg = j->data;
00592             if(!_alpm_pkg_find(trans->remove, rpkg->name)) {
00593                 alpm_pkg_t *copy;
00594                 _alpm_log(handle, ALPM_LOG_DEBUG, "adding '%s' to remove list\n", rpkg->name);
00595                 if(_alpm_pkg_dup(rpkg, &copy) == -1) {
00596                     return -1;
00597                 }
00598                 trans->remove = alpm_list_add(trans->remove, copy);
00599             }
00600         }
00601     }
00602 
00603     if(!(trans->flags & ALPM_TRANS_FLAG_NODEPS)) {
00604         _alpm_log(handle, ALPM_LOG_DEBUG, "checking dependencies\n");
00605         deps = alpm_checkdeps(handle, _alpm_db_get_pkgcache(handle->db_local),
00606                 trans->remove, trans->add, 1);
00607         if(deps) {
00608             handle->pm_errno = ALPM_ERR_UNSATISFIED_DEPS;
00609             ret = -1;
00610             if(data) {
00611                 *data = deps;
00612             } else {
00613                 alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_depmiss_free);
00614                 alpm_list_free(deps);
00615             }
00616             goto cleanup;
00617         }
00618     }
00619     for(i = trans->add; i; i = i->next) {
00620         /* update download size field */
00621         alpm_pkg_t *spkg = i->data;
00622         if(compute_download_size(spkg) < 0) {
00623             ret = -1;
00624             goto cleanup;
00625         }
00626     }
00627 
00628 cleanup:
00629     return ret;
00630 }
00631 
00632 /** Returns the size of the files that will be downloaded to install a
00633  * package.
00634  * @param newpkg the new package to upgrade to
00635  * @return the size of the download
00636  */
00637 off_t SYMEXPORT alpm_pkg_download_size(alpm_pkg_t *newpkg)
00638 {
00639     if(!(newpkg->infolevel & INFRQ_DSIZE)) {
00640         compute_download_size(newpkg);
00641     }
00642     return newpkg->download_size;
00643 }
00644 
00645 static int endswith(const char *filename, const char *extension)
00646 {
00647     const char *s = filename + strlen(filename) - strlen(extension);
00648     return strcmp(s, extension) == 0;
00649 }
00650 
00651 /** Applies delta files to create an upgraded package file.
00652  *
00653  * All intermediate files are deleted, leaving only the starting and
00654  * ending package files.
00655  *
00656  * @param handle the context handle
00657  *
00658  * @return 0 if all delta files were able to be applied, 1 otherwise.
00659  */
00660 static int apply_deltas(alpm_handle_t *handle)
00661 {
00662     alpm_list_t *i;
00663     int deltas_found = 0, ret = 0;
00664     const char *cachedir = _alpm_filecache_setup(handle);
00665     alpm_trans_t *trans = handle->trans;
00666 
00667     for(i = trans->add; i; i = i->next) {
00668         alpm_pkg_t *spkg = i->data;
00669         alpm_list_t *delta_path = spkg->delta_path;
00670         alpm_list_t *dlts = NULL;
00671 
00672         if(!delta_path) {
00673             continue;
00674         }
00675 
00676         if(!deltas_found) {
00677             /* only show this if we actually have deltas to apply, and it is before
00678              * the very first one */
00679             EVENT(handle, ALPM_EVENT_DELTA_PATCHES_START, NULL, NULL);
00680             deltas_found = 1;
00681         }
00682 
00683         for(dlts = delta_path; dlts; dlts = dlts->next) {
00684             alpm_delta_t *d = dlts->data;
00685             char *delta, *from, *to;
00686             char command[PATH_MAX];
00687             size_t len = 0;
00688 
00689             delta = _alpm_filecache_find(handle, d->delta);
00690             /* the initial package might be in a different cachedir */
00691             if(dlts == delta_path) {
00692                 from = _alpm_filecache_find(handle, d->from);
00693             } else {
00694                 /* len = cachedir len + from len + '/' + null */
00695                 len = strlen(cachedir) + strlen(d->from) + 2;
00696                 MALLOC(from, len, RET_ERR(handle, ALPM_ERR_MEMORY, 1));
00697                 snprintf(from, len, "%s/%s", cachedir, d->from);
00698             }
00699             len = strlen(cachedir) + strlen(d->to) + 2;
00700             MALLOC(to, len, RET_ERR(handle, ALPM_ERR_MEMORY, 1));
00701             snprintf(to, len, "%s/%s", cachedir, d->to);
00702 
00703             /* build the patch command */
00704             if(endswith(to, ".gz")) {
00705                 /* special handling for gzip : we disable timestamp with -n option */
00706                 snprintf(command, PATH_MAX, "xdelta3 -d -q -R -c -s %s %s | gzip -n > %s", from, delta, to);
00707             } else {
00708                 snprintf(command, PATH_MAX, "xdelta3 -d -q -s %s %s %s", from, delta, to);
00709             }
00710 
00711             _alpm_log(handle, ALPM_LOG_DEBUG, "command: %s\n", command);
00712 
00713             EVENT(handle, ALPM_EVENT_DELTA_PATCH_START, d->to, d->delta);
00714 
00715             int retval = system(command);
00716             if(retval == 0) {
00717                 EVENT(handle, ALPM_EVENT_DELTA_PATCH_DONE, NULL, NULL);
00718 
00719                 /* delete the delta file */
00720                 unlink(delta);
00721 
00722                 /* Delete the 'from' package but only if it is an intermediate
00723                  * package. The starting 'from' package should be kept, just
00724                  * as if deltas were not used. */
00725                 if(dlts != delta_path) {
00726                     unlink(from);
00727                 }
00728             }
00729             FREE(from);
00730             FREE(to);
00731             FREE(delta);
00732 
00733             if(retval != 0) {
00734                 /* one delta failed for this package, cancel the remaining ones */
00735                 EVENT(handle, ALPM_EVENT_DELTA_PATCH_FAILED, NULL, NULL);
00736                 handle->pm_errno = ALPM_ERR_DLT_PATCHFAILED;
00737                 ret = 1;
00738                 break;
00739             }
00740         }
00741     }
00742     if(deltas_found) {
00743         EVENT(handle, ALPM_EVENT_DELTA_PATCHES_DONE, NULL, NULL);
00744     }
00745 
00746     return ret;
00747 }
00748 
00749 /**
00750  * Prompts to delete the file now that we know it is invalid.
00751  * @param handle the context handle
00752  * @param filename the absolute path of the file to test
00753  * @param reason an error code indicating the reason for package invalidity
00754  *
00755  * @return 1 if file was removed, 0 otherwise
00756  */
00757 static int prompt_to_delete(alpm_handle_t *handle, const char *filepath,
00758         alpm_errno_t reason)
00759 {
00760     int doremove = 0;
00761     QUESTION(handle, ALPM_QUESTION_CORRUPTED_PKG, (char *)filepath,
00762             &reason, NULL, &doremove);
00763     if(doremove) {
00764         unlink(filepath);
00765     }
00766     return doremove;
00767 }
00768 
00769 static int validate_deltas(alpm_handle_t *handle, alpm_list_t *deltas)
00770 {
00771     alpm_list_t *i, *errors = NULL;
00772 
00773     if(!deltas) {
00774         return 0;
00775     }
00776 
00777     /* Check integrity of deltas */
00778     EVENT(handle, ALPM_EVENT_DELTA_INTEGRITY_START, NULL, NULL);
00779     for(i = deltas; i; i = i->next) {
00780         alpm_delta_t *d = i->data;
00781         char *filepath = _alpm_filecache_find(handle, d->delta);
00782 
00783         if(_alpm_test_checksum(filepath, d->delta_md5, ALPM_CSUM_MD5)) {
00784             errors = alpm_list_add(errors, filepath);
00785         } else {
00786             FREE(filepath);
00787         }
00788     }
00789     EVENT(handle, ALPM_EVENT_DELTA_INTEGRITY_DONE, NULL, NULL);
00790 
00791     if(errors) {
00792         for(i = errors; i; i = i->next) {
00793             char *filepath = i->data;
00794             prompt_to_delete(handle, filepath, ALPM_ERR_DLT_INVALID);
00795             FREE(filepath);
00796         }
00797         alpm_list_free(errors);
00798         handle->pm_errno = ALPM_ERR_DLT_INVALID;
00799         return -1;
00800     }
00801     return 0;
00802 }
00803 
00804 static struct dload_payload *build_payload(alpm_handle_t *handle,
00805         const char *filename, size_t size, alpm_list_t *servers)
00806 {
00807         struct dload_payload *payload;
00808 
00809         CALLOC(payload, 1, sizeof(*payload), RET_ERR(handle, ALPM_ERR_MEMORY, NULL));
00810         STRDUP(payload->remote_name, filename, RET_ERR(handle, ALPM_ERR_MEMORY, NULL));
00811         payload->max_size = size;
00812         payload->servers = servers;
00813         return payload;
00814 }
00815 
00816 static int find_dl_candidates(alpm_db_t *repo, alpm_list_t **files, alpm_list_t **deltas)
00817 {
00818     alpm_list_t *i;
00819     alpm_handle_t *handle = repo->handle;
00820 
00821     for(i = handle->trans->add; i; i = i->next) {
00822         alpm_pkg_t *spkg = i->data;
00823 
00824         if(spkg->origin != PKG_FROM_FILE && repo == spkg->origin_data.db) {
00825             alpm_list_t *delta_path = spkg->delta_path;
00826 
00827             if(!repo->servers) {
00828                 handle->pm_errno = ALPM_ERR_SERVER_NONE;
00829                 _alpm_log(handle, ALPM_LOG_ERROR, "%s: %s\n",
00830                         alpm_strerror(handle->pm_errno), repo->treename);
00831                 return 1;
00832             }
00833 
00834             if(delta_path) {
00835                 /* using deltas */
00836                 alpm_list_t *dlts;
00837                 for(dlts = delta_path; dlts; dlts = dlts->next) {
00838                     alpm_delta_t *delta = dlts->data;
00839                     if(delta->download_size != 0) {
00840                         struct dload_payload *payload = build_payload(
00841                                 handle, delta->delta, delta->download_size, repo->servers);
00842                         ASSERT(payload, return -1);
00843                         *files = alpm_list_add(*files, payload);
00844                     }
00845                     /* keep a list of all the delta files for md5sums */
00846                     *deltas = alpm_list_add(*deltas, delta);
00847                 }
00848 
00849             } else if(spkg->download_size != 0) {
00850                 struct dload_payload *payload;
00851                 ASSERT(spkg->filename != NULL, RET_ERR(handle, ALPM_ERR_PKG_INVALID_NAME, -1));
00852                 payload = build_payload(handle, spkg->filename, spkg->size, repo->servers);
00853                 ASSERT(payload, return -1);
00854                 *files = alpm_list_add(*files, payload);
00855             }
00856         }
00857     }
00858 
00859     return 0;
00860 }
00861 
00862 static int download_single_file(alpm_handle_t *handle, struct dload_payload *payload,
00863         const char *cachedir)
00864 {
00865     const alpm_list_t *server;
00866 
00867     for(server = payload->servers; server; server = server->next) {
00868         const char *server_url = server->data;
00869         size_t len;
00870 
00871         /* print server + filename into a buffer */
00872         len = strlen(server_url) + strlen(payload->remote_name) + 2;
00873         MALLOC(payload->fileurl, len, RET_ERR(handle, ALPM_ERR_MEMORY, -1));
00874         snprintf(payload->fileurl, len, "%s/%s", server_url, payload->remote_name);
00875         payload->handle = handle;
00876         payload->allow_resume = 1;
00877 
00878         if(_alpm_download(payload, cachedir, NULL) != -1) {
00879             return 0;
00880         }
00881     }
00882 
00883     return -1;
00884 }
00885 
00886 static int download_files(alpm_handle_t *handle, alpm_list_t **deltas)
00887 {
00888     const char *cachedir;
00889     alpm_list_t *i, *files = NULL;
00890     int errors = 0;
00891 
00892     cachedir = _alpm_filecache_setup(handle);
00893     handle->trans->state = STATE_DOWNLOADING;
00894 
00895     /* Total progress - figure out the total download size if required to
00896      * pass to the callback. This function is called once, and it is up to the
00897      * frontend to compute incremental progress. */
00898     if(handle->totaldlcb) {
00899         off_t total_size = (off_t)0;
00900         /* sum up the download size for each package and store total */
00901         for(i = handle->trans->add; i; i = i->next) {
00902             alpm_pkg_t *spkg = i->data;
00903             total_size += spkg->download_size;
00904         }
00905         handle->totaldlcb(total_size);
00906     }
00907 
00908     for(i = handle->dbs_sync; i; i = i->next) {
00909         errors += find_dl_candidates(i->data, &files, deltas);
00910     }
00911 
00912     if(files) {
00913         /* check for necessary disk space for download */
00914         if(handle->checkspace) {
00915             off_t *file_sizes;
00916             size_t idx, num_files;
00917             int ret;
00918 
00919             _alpm_log(handle, ALPM_LOG_DEBUG, "checking available disk space for download\n");
00920 
00921             num_files = alpm_list_count(files);
00922             CALLOC(file_sizes, num_files, sizeof(off_t), goto finish);
00923 
00924             for(i = files, idx = 0; i; i = i->next, idx++) {
00925                 const struct dload_payload *payload = i->data;
00926                 file_sizes[idx] = payload->max_size;
00927             }
00928 
00929             ret = _alpm_check_downloadspace(handle, cachedir, num_files, file_sizes);
00930             free(file_sizes);
00931 
00932             if(ret != 0) {
00933                 errors++;
00934                 goto finish;
00935             }
00936         }
00937 
00938         EVENT(handle, ALPM_EVENT_RETRIEVE_START, NULL, NULL);
00939         for(i = files; i; i = i->next) {
00940             if(download_single_file(handle, i->data, cachedir) == -1) {
00941                 errors++;
00942                 _alpm_log(handle, ALPM_LOG_WARNING, _("failed to retrieve some files\n"));
00943             }
00944         }
00945     }
00946 
00947 finish:
00948     if(files) {
00949         alpm_list_free_inner(files, (alpm_list_fn_free)_alpm_dload_payload_reset);
00950         FREELIST(files);
00951     }
00952 
00953     for(i = handle->trans->add; i; i = i->next) {
00954         alpm_pkg_t *pkg = i->data;
00955         pkg->infolevel &= ~INFRQ_DSIZE;
00956         pkg->download_size = 0;
00957     }
00958 
00959     /* clear out value to let callback know we are done */
00960     if(handle->totaldlcb) {
00961         handle->totaldlcb(0);
00962     }
00963 
00964     return errors;
00965 }
00966 
00967 static int check_validity(alpm_handle_t *handle,
00968         size_t total, size_t total_bytes)
00969 {
00970     struct validity {
00971         alpm_pkg_t *pkg;
00972         char *path;
00973         alpm_siglist_t *siglist;
00974         alpm_siglevel_t level;
00975         alpm_errno_t error;
00976     };
00977     size_t current = 0, current_bytes = 0;
00978     alpm_list_t *i, *errors = NULL;
00979 
00980     /* Check integrity of packages */
00981     EVENT(handle, ALPM_EVENT_INTEGRITY_START, NULL, NULL);
00982 
00983     for(i = handle->trans->add; i; i = i->next, current++) {
00984         struct validity v = { i->data, NULL, NULL, 0, 0 };
00985         int percent = (int)(((double)current_bytes / total_bytes) * 100);
00986 
00987         PROGRESS(handle, ALPM_PROGRESS_INTEGRITY_START, "", percent,
00988                 total, current);
00989         if(v.pkg->origin == PKG_FROM_FILE) {
00990             continue; /* pkg_load() has been already called, this package is valid */
00991         }
00992 
00993         current_bytes += v.pkg->size;
00994         v.path = _alpm_filecache_find(handle, v.pkg->filename);
00995         v.level = alpm_db_get_siglevel(alpm_pkg_get_db(v.pkg));
00996 
00997         if(_alpm_pkg_validate_internal(handle, v.path, v.pkg,
00998                     v.level, &v.siglist) == -1) {
00999             v.error = handle->pm_errno;
01000             struct validity *invalid = malloc(sizeof(struct validity));
01001             memcpy(invalid, &v, sizeof(struct validity));
01002             errors = alpm_list_add(errors, invalid);
01003         } else {
01004             alpm_siglist_cleanup(v.siglist);
01005             free(v.siglist);
01006             free(v.path);
01007         }
01008     }
01009 
01010     PROGRESS(handle, ALPM_PROGRESS_INTEGRITY_START, "", 100,
01011             total, current);
01012     EVENT(handle, ALPM_EVENT_INTEGRITY_DONE, NULL, NULL);
01013 
01014     if(errors) {
01015         int tryagain = 0;
01016         for(i = errors; i; i = i->next) {
01017             struct validity *v = i->data;
01018             if(v->error == ALPM_ERR_PKG_INVALID_SIG) {
01019                 int retry = _alpm_process_siglist(handle, v->pkg->name, v->siglist,
01020                         v->level & ALPM_SIG_PACKAGE_OPTIONAL,
01021                         v->level & ALPM_SIG_PACKAGE_MARGINAL_OK,
01022                         v->level & ALPM_SIG_PACKAGE_UNKNOWN_OK);
01023                 tryagain += retry;
01024             } else if(v->error == ALPM_ERR_PKG_INVALID_CHECKSUM) {
01025                 prompt_to_delete(handle, v->path, v->error);
01026             }
01027             alpm_siglist_cleanup(v->siglist);
01028             free(v->siglist);
01029             free(v->path);
01030             free(v);
01031         }
01032         alpm_list_free(errors);
01033 
01034         if(tryagain == 0) {
01035             if(!handle->pm_errno) {
01036                 RET_ERR(handle, ALPM_ERR_PKG_INVALID, -1);
01037             }
01038             return -1;
01039         }
01040         /* we were told at least once we can try again */
01041         return 1;
01042     }
01043 
01044     return 0;
01045 }
01046 
01047 static int load_packages(alpm_handle_t *handle, alpm_list_t **data,
01048         size_t total, size_t total_bytes)
01049 {
01050     size_t current = 0, current_bytes = 0;
01051     int errors = 0;
01052     alpm_list_t *i;
01053 
01054     /* load packages from disk now that they are known-valid */
01055     EVENT(handle, ALPM_EVENT_LOAD_START, NULL, NULL);
01056 
01057     for(i = handle->trans->add; i; i = i->next, current++) {
01058         alpm_pkg_t *spkg = i->data;
01059         char *filepath;
01060         int percent = (int)(((double)current_bytes / total_bytes) * 100);
01061 
01062         PROGRESS(handle, ALPM_PROGRESS_LOAD_START, "", percent,
01063                 total, current);
01064         if(spkg->origin == PKG_FROM_FILE) {
01065             continue; /* pkg_load() has been already called, this package is valid */
01066         }
01067 
01068         current_bytes += spkg->size;
01069         filepath = _alpm_filecache_find(handle, spkg->filename);
01070 
01071         /* load the package file and replace pkgcache entry with it in the target list */
01072         /* TODO: alpm_pkg_get_db() will not work on this target anymore */
01073         _alpm_log(handle, ALPM_LOG_DEBUG,
01074                 "replacing pkgcache entry with package file for target %s\n",
01075                 spkg->name);
01076         alpm_pkg_t *pkgfile =_alpm_pkg_load_internal(handle, filepath, 1);
01077         if(!pkgfile) {
01078             errors++;
01079             *data = alpm_list_add(*data, strdup(spkg->filename));
01080             free(filepath);
01081             continue;
01082         }
01083         free(filepath);
01084         /* copy over the install reason */
01085         pkgfile->reason = spkg->reason;
01086         i->data = pkgfile;
01087         /* spkg has been removed from the target list, so we can free the
01088          * sync-specific fields */
01089         _alpm_pkg_free_trans(spkg);
01090     }
01091 
01092     PROGRESS(handle, ALPM_PROGRESS_LOAD_START, "", 100,
01093             total, current);
01094     EVENT(handle, ALPM_EVENT_LOAD_DONE, NULL, NULL);
01095 
01096     if(errors) {
01097         if(!handle->pm_errno) {
01098             RET_ERR(handle, ALPM_ERR_PKG_INVALID, -1);
01099         }
01100         return -1;
01101     }
01102 
01103     return 0;
01104 }
01105 
01106 int _alpm_sync_commit(alpm_handle_t *handle, alpm_list_t **data)
01107 {
01108     alpm_list_t *i, *deltas = NULL;
01109     size_t total = 0, total_bytes = 0;
01110     alpm_trans_t *trans = handle->trans;
01111 
01112     if(download_files(handle, &deltas)) {
01113         alpm_list_free(deltas);
01114         return -1;
01115     }
01116 
01117     if(validate_deltas(handle, deltas)) {
01118         alpm_list_free(deltas);
01119         return -1;
01120     }
01121     alpm_list_free(deltas);
01122 
01123     /* Use the deltas to generate the packages */
01124     if(apply_deltas(handle)) {
01125         return -1;
01126     }
01127 
01128     /* get the total size of all packages so we can adjust the progress bar more
01129      * realistically if there are small and huge packages involved */
01130     for(i = trans->add; i; i = i->next) {
01131         alpm_pkg_t *spkg = i->data;
01132         if(spkg->origin != PKG_FROM_FILE) {
01133             total_bytes += spkg->size;
01134         }
01135         total++;
01136     }
01137     /* this can only happen maliciously */
01138     total_bytes = total_bytes ? total_bytes : 1;
01139 
01140     /* this one is special: -1 is failure, 1 is retry, 0 is success */
01141     while(1) {
01142         int ret = check_validity(handle, total, total_bytes);
01143         if(ret == 0) {
01144             break;
01145         } else if(ret < 0) {
01146             return -1;
01147         }
01148     }
01149 
01150     if(trans->flags & ALPM_TRANS_FLAG_DOWNLOADONLY) {
01151         return 0;
01152     }
01153 
01154     if(load_packages(handle, data, total, total_bytes)) {
01155         return -1;
01156     }
01157 
01158     trans->state = STATE_COMMITING;
01159 
01160     /* fileconflict check */
01161     if(!(trans->flags & (ALPM_TRANS_FLAG_FORCE|ALPM_TRANS_FLAG_DBONLY))) {
01162         EVENT(handle, ALPM_EVENT_FILECONFLICTS_START, NULL, NULL);
01163 
01164         _alpm_log(handle, ALPM_LOG_DEBUG, "looking for file conflicts\n");
01165         alpm_list_t *conflict = _alpm_db_find_fileconflicts(handle,
01166                 trans->add, trans->remove);
01167         if(conflict) {
01168             if(data) {
01169                 *data = conflict;
01170             } else {
01171                 alpm_list_free_inner(conflict, (alpm_list_fn_free)_alpm_fileconflict_free);
01172                 alpm_list_free(conflict);
01173             }
01174             RET_ERR(handle, ALPM_ERR_FILE_CONFLICTS, -1);
01175         }
01176 
01177         EVENT(handle, ALPM_EVENT_FILECONFLICTS_DONE, NULL, NULL);
01178     }
01179 
01180     /* check available disk space */
01181     if(handle->checkspace && !(trans->flags & ALPM_TRANS_FLAG_DBONLY)) {
01182         EVENT(handle, ALPM_EVENT_DISKSPACE_START, NULL, NULL);
01183 
01184         _alpm_log(handle, ALPM_LOG_DEBUG, "checking available disk space\n");
01185         if(_alpm_check_diskspace(handle) == -1) {
01186             _alpm_log(handle, ALPM_LOG_ERROR, "%s\n", _("not enough free disk space"));
01187             return -1;
01188         }
01189 
01190         EVENT(handle, ALPM_EVENT_DISKSPACE_DONE, NULL, NULL);
01191     }
01192 
01193     /* remove conflicting and to-be-replaced packages */
01194     if(trans->remove) {
01195         _alpm_log(handle, ALPM_LOG_DEBUG, "removing conflicting and to-be-replaced packages\n");
01196         /* we want the frontend to be aware of commit details */
01197         if(_alpm_remove_packages(handle, 0) == -1) {
01198             _alpm_log(handle, ALPM_LOG_ERROR, _("could not commit removal transaction\n"));
01199             return -1;
01200         }
01201     }
01202 
01203     /* install targets */
01204     _alpm_log(handle, ALPM_LOG_DEBUG, "installing packages\n");
01205     if(_alpm_upgrade_packages(handle) == -1) {
01206         _alpm_log(handle, ALPM_LOG_ERROR, _("could not commit transaction\n"));
01207         return -1;
01208     }
01209 
01210     return 0;
01211 }
01212 
01213 /* vim: set ts=2 sw=2 noet: */