sync.c

Go to the documentation of this file.
00001 /*
00002  *  sync.c
00003  *
00004  *  Copyright (c) 2002-2007 by Judd Vinet <jvinet@zeroflux.org>
00005  *  Copyright (c) 2005 by Aurelien Foret <orelien@chez.com>
00006  *  Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu>
00007  *  Copyright (c) 2005, 2006 by Miklos Vajna <vmiklos@frugalware.org>
00008  *
00009  *  This program is free software; you can redistribute it and/or modify
00010  *  it under the terms of the GNU General Public License as published by
00011  *  the Free Software Foundation; either version 2 of the License, or
00012  *  (at your option) any later version.
00013  *
00014  *  This program is distributed in the hope that it will be useful,
00015  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  *  GNU General Public License for more details.
00018  *
00019  *  You should have received a copy of the GNU General Public License
00020  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00021  */
00022 
00023 #include "config.h"
00024 
00025 #include <stdlib.h>
00026 #include <stdio.h>
00027 #include <fcntl.h>
00028 #include <string.h>
00029 #include <unistd.h>
00030 #include <time.h>
00031 #include <dirent.h>
00032 
00033 /* libalpm */
00034 #include "sync.h"
00035 #include "alpm_list.h"
00036 #include "log.h"
00037 #include "error.h"
00038 #include "package.h"
00039 #include "db.h"
00040 #include "cache.h"
00041 #include "deps.h"
00042 #include "conflict.h"
00043 #include "trans.h"
00044 #include "util.h"
00045 #include "handle.h"
00046 #include "alpm.h"
00047 #include "server.h"
00048 #include "delta.h"
00049 
00050 pmsyncpkg_t *_alpm_sync_new(int type, pmpkg_t *spkg, void *data)
00051 {
00052     pmsyncpkg_t *sync;
00053 
00054     ALPM_LOG_FUNC;
00055 
00056     CALLOC(sync, 1, sizeof(pmsyncpkg_t), RET_ERR(PM_ERR_MEMORY, NULL));
00057 
00058     sync->type = type;
00059     sync->pkg = spkg;
00060     sync->data = data;
00061 
00062     return(sync);
00063 }
00064 
00065 void _alpm_sync_free(pmsyncpkg_t *sync)
00066 {
00067     ALPM_LOG_FUNC;
00068 
00069     if(sync == NULL) {
00070         return;
00071     }
00072 
00073     /* TODO wow this is ugly */
00074     if(sync->type == PM_SYNC_TYPE_REPLACE) {
00075         alpm_list_free(sync->data);
00076     }
00077     sync->data = NULL;
00078     FREE(sync);
00079 }
00080 
00081 static void synclist_free(alpm_list_t *syncpkgs)
00082 {
00083     if(syncpkgs) {
00084         alpm_list_t *tmp;
00085         for(tmp = syncpkgs; tmp; tmp = alpm_list_next(tmp)) {
00086             if(tmp->data) {
00087                 _alpm_sync_free(tmp->data);
00088             }
00089         }
00090         alpm_list_free(syncpkgs);
00091     }
00092 
00093 }
00094 
00095 /* Find recommended replacements for packages during a sync.
00096  */
00097 static int find_replacements(pmtrans_t *trans, pmdb_t *db_local,
00098         alpm_list_t *dbs_sync, alpm_list_t **syncpkgs)
00099 {
00100     alpm_list_t *i, *j, *k; /* wow */
00101 
00102     ALPM_LOG_FUNC;
00103 
00104     if(syncpkgs == NULL) {
00105         return(-1);
00106     }
00107 
00108     /* check for "recommended" package replacements */
00109     _alpm_log(PM_LOG_DEBUG, "checking for package replacements\n");
00110     for(i = dbs_sync; i; i = i->next) {
00111         pmdb_t *db = i->data;
00112 
00113         /* for each db, check each package's REPLACES list */
00114         for(j = _alpm_db_get_pkgcache(db); j; j = j->next) {
00115             pmpkg_t *spkg = j->data;
00116 
00117             for(k = alpm_pkg_get_replaces(spkg); k; k = k->next) {
00118                 const char *replacement = k->data;
00119 
00120                 pmpkg_t *lpkg = _alpm_db_get_pkgfromcache(db_local, replacement);
00121                 if(!lpkg) {
00122                     continue;
00123                 }
00124 
00125                 _alpm_log(PM_LOG_DEBUG, "checking replacement '%s' for package '%s'\n",
00126                         replacement, spkg->name);
00127                 /* ignore if EITHER the local or replacement package are to be ignored */
00128                 if(_alpm_pkg_should_ignore(spkg) || _alpm_pkg_should_ignore(lpkg)) {
00129                     _alpm_log(PM_LOG_WARNING, _("%s-%s: ignoring package upgrade (to be replaced by %s-%s)\n"),
00130                                         alpm_pkg_get_name(lpkg), alpm_pkg_get_version(lpkg),
00131                                         alpm_pkg_get_name(spkg), alpm_pkg_get_version(spkg));
00132                 } else {
00133                     /* get confirmation for the replacement */
00134                     if(trans) {
00135                         int doreplace = 0;
00136                         QUESTION(trans, PM_TRANS_CONV_REPLACE_PKG, lpkg, spkg, db->treename, &doreplace);
00137                         if(!doreplace) {
00138                             continue;
00139                         }
00140                     }
00141 
00142                     /* if confirmed, add this to the 'final' list, designating 'lpkg' as
00143                      * the package to replace.
00144                      */
00145                     pmsyncpkg_t *sync;
00146 
00147                     /* check if spkg->name is already in the packages list. */
00148                     sync = _alpm_sync_find(*syncpkgs, alpm_pkg_get_name(spkg));
00149                     if(sync) {
00150                         /* found it -- just append to the replaces list */
00151                         sync->data = alpm_list_add(sync->data, lpkg);
00152                     } else {
00153                         /* none found -- enter pkg into the final sync list */
00154                         sync = _alpm_sync_new(PM_SYNC_TYPE_REPLACE, spkg, NULL);
00155                         if(sync == NULL) {
00156                             pm_errno = PM_ERR_MEMORY;
00157                             synclist_free(*syncpkgs);
00158                             return(-1);
00159                         }
00160                         sync->data = alpm_list_add(NULL, lpkg);
00161                         *syncpkgs = alpm_list_add(*syncpkgs, sync);
00162                     }
00163                     _alpm_log(PM_LOG_DEBUG, "%s-%s elected for upgrade (to be replaced by %s-%s)\n",
00164                             alpm_pkg_get_name(lpkg), alpm_pkg_get_version(lpkg),
00165                             alpm_pkg_get_name(spkg), alpm_pkg_get_version(spkg));
00166                 }
00167             }
00168         }
00169     }
00170     return(0);
00171 }
00172 
00173 /** Get a list of upgradable packages on the current system
00174  * Adds out of date packages to *list.
00175  * @arg list pointer to a list of pmsyncpkg_t.
00176  */
00177 int SYMEXPORT alpm_sync_sysupgrade(pmdb_t *db_local,
00178         alpm_list_t *dbs_sync, alpm_list_t **syncpkgs)
00179 {
00180     return(_alpm_sync_sysupgrade(NULL, db_local, dbs_sync, syncpkgs));
00181 }
00182 
00183 int _alpm_sync_sysupgrade(pmtrans_t *trans,
00184         pmdb_t *db_local, alpm_list_t *dbs_sync, alpm_list_t **syncpkgs)
00185 {
00186     alpm_list_t *i, *j;
00187 
00188     ALPM_LOG_FUNC;
00189 
00190     if(syncpkgs == NULL) {
00191         return(-1);
00192     }
00193     /* check for "recommended" package replacements */
00194     if(find_replacements(trans, db_local, dbs_sync, syncpkgs)) {
00195         return(-1);
00196     }
00197 
00198     /* match installed packages with the sync dbs and compare versions */
00199     _alpm_log(PM_LOG_DEBUG, "checking for package upgrades\n");
00200     for(i = _alpm_db_get_pkgcache(db_local); i; i = i->next) {
00201         int replace = 0;
00202         pmpkg_t *local = i->data;
00203         pmpkg_t *spkg = NULL;
00204         pmsyncpkg_t *sync;
00205 
00206         for(j = dbs_sync; !spkg && j; j = j->next) {
00207             spkg = _alpm_db_get_pkgfromcache(j->data, alpm_pkg_get_name(local));
00208         }
00209         if(spkg == NULL) {
00210             _alpm_log(PM_LOG_DEBUG, "'%s' not found in sync db -- skipping\n",
00211                     alpm_pkg_get_name(local));
00212             continue;
00213         }
00214 
00215         /* we don't care about a to-be-replaced package's newer version */
00216         for(j = *syncpkgs; j && !replace; j=j->next) {
00217             sync = j->data;
00218             if(sync->type == PM_SYNC_TYPE_REPLACE) {
00219                 if(_alpm_pkg_find(alpm_pkg_get_name(spkg), sync->data)) {
00220                     replace = 1;
00221                 }
00222             }
00223         }
00224         if(replace) {
00225             _alpm_log(PM_LOG_DEBUG, "'%s' is already elected for removal -- skipping\n",
00226                     alpm_pkg_get_name(local));
00227             continue;
00228         }
00229 
00230         /* compare versions and see if we need to upgrade */
00231         if(_alpm_pkg_compare_versions(local, spkg)) {
00232             _alpm_log(PM_LOG_DEBUG, "%s elected for upgrade (%s => %s)\n",
00233                     alpm_pkg_get_name(local), alpm_pkg_get_version(local),
00234                     alpm_pkg_get_version(spkg));
00235             if(!_alpm_sync_find(*syncpkgs, alpm_pkg_get_name(spkg))) {
00236                 /* If package is in the ignorepkg list, skip it */
00237                 if(_alpm_pkg_should_ignore(spkg)) {
00238                     _alpm_log(PM_LOG_WARNING, _("%s: ignoring package upgrade (%s => %s)\n"),
00239                             alpm_pkg_get_name(local), alpm_pkg_get_version(local),
00240                             alpm_pkg_get_version(spkg));
00241                     continue;
00242                 }
00243 
00244                 sync = _alpm_sync_new(PM_SYNC_TYPE_UPGRADE, spkg, local);
00245                 if(sync == NULL) {
00246                     pm_errno = PM_ERR_MEMORY;
00247                     synclist_free(*syncpkgs);
00248                     return(-1);
00249                 }
00250                 *syncpkgs = alpm_list_add(*syncpkgs, sync);
00251             }
00252         }
00253     }
00254 
00255     return(0);
00256 }
00257 
00258 int _alpm_sync_addtarget(pmtrans_t *trans, pmdb_t *db_local, alpm_list_t *dbs_sync, char *name)
00259 {
00260     char *targline;
00261     char *targ;
00262     alpm_list_t *j;
00263     pmpkg_t *local;
00264     pmpkg_t *spkg = NULL;
00265     pmsyncpkg_t *sync;
00266     int repo_found = 0;
00267 
00268     ALPM_LOG_FUNC;
00269 
00270     ASSERT(db_local != NULL, RET_ERR(PM_ERR_DB_NULL, -1));
00271     ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
00272     ASSERT(name != NULL, RET_ERR(PM_ERR_WRONG_ARGS, -1));
00273     STRDUP(targline, name, RET_ERR(PM_ERR_MEMORY, -1));
00274 
00275     targ = strchr(targline, '/');
00276     if(targ) {
00277         /* we are looking for a package in a specific database */
00278         *targ = '\0';
00279         targ++;
00280         _alpm_log(PM_LOG_DEBUG, "searching for target '%s' in repo\n", targ);
00281         for(j = dbs_sync; j && !spkg; j = j->next) {
00282             pmdb_t *db = j->data;
00283             if(strcmp(db->treename, targline) == 0) {
00284                 repo_found = 1;
00285                 spkg = _alpm_db_get_pkgfromcache(db, targ);
00286                 if(spkg == NULL) {
00287                     pm_errno = PM_ERR_PKG_NOT_FOUND;
00288                     goto error;
00289                 }
00290             }
00291         }
00292         if(!repo_found) {
00293             _alpm_log(PM_LOG_ERROR, _("repository '%s' not found\n"), targline);
00294             pm_errno = PM_ERR_PKG_REPO_NOT_FOUND;
00295             goto error;
00296         }
00297     } else {
00298         targ = targline;
00299         for(j = dbs_sync; j && !spkg; j = j->next) {
00300             pmdb_t *db = j->data;
00301             spkg = _alpm_db_get_pkgfromcache(db, targ);
00302         }
00303         if(spkg == NULL) {
00304             pm_errno = PM_ERR_PKG_NOT_FOUND;
00305             goto error;
00306         }
00307     }
00308 
00309     if(_alpm_pkg_should_ignore(spkg)) {
00310         int resp;
00311         QUESTION(trans, PM_TRANS_CONV_INSTALL_IGNOREPKG, spkg, NULL, NULL, &resp);
00312         if (!resp) {
00313             return(0);
00314         }
00315     }
00316 
00317     local = _alpm_db_get_pkgfromcache(db_local, alpm_pkg_get_name(spkg));
00318     if(local) {
00319         if(_alpm_pkg_compare_versions(local, spkg) == 0) {
00320             /* spkg is NOT an upgrade */
00321             if(trans->flags & PM_TRANS_FLAG_NEEDED) {
00322                 _alpm_log(PM_LOG_WARNING, _("%s-%s is up to date -- skipping\n"),
00323                         alpm_pkg_get_name(local), alpm_pkg_get_version(local));
00324                 return(0);
00325             } else {
00326                 _alpm_log(PM_LOG_WARNING, _("%s-%s is up to date -- reinstalling\n"),
00327                         alpm_pkg_get_name(local), alpm_pkg_get_version(local));
00328             }
00329         }
00330     }
00331 
00332     /* add the package to the transaction */
00333     if(!_alpm_sync_find(trans->packages, alpm_pkg_get_name(spkg))) {
00334         sync = _alpm_sync_new(PM_SYNC_TYPE_UPGRADE, spkg, local);
00335         if(sync == NULL) {
00336             pm_errno = PM_ERR_MEMORY;
00337             goto error;
00338         }
00339         _alpm_log(PM_LOG_DEBUG, "adding target '%s' to the transaction set\n",
00340                             alpm_pkg_get_name(spkg));
00341         trans->packages = alpm_list_add(trans->packages, sync);
00342     }
00343 
00344     FREE(targline);
00345     return(0);
00346 
00347 error:
00348     if(targline) {
00349         FREE(targline);
00350     }
00351     return(-1);
00352 }
00353 
00354 /* Helper functions for alpm_list_remove
00355  */
00356 static int syncpkg_cmp(const void *s1, const void *s2)
00357 {
00358     const pmsyncpkg_t *sp1 = s1;
00359     const pmsyncpkg_t *sp2 = s2;
00360     pmpkg_t *p1, *p2;
00361 
00362   p1 = alpm_sync_get_pkg(sp1);
00363   p2 = alpm_sync_get_pkg(sp2);
00364 
00365     return(strcmp(alpm_pkg_get_name(p1), alpm_pkg_get_name(p2)));
00366 }
00367 
00368 int _alpm_sync_prepare(pmtrans_t *trans, pmdb_t *db_local, alpm_list_t *dbs_sync, alpm_list_t **data)
00369 {
00370     alpm_list_t *deps = NULL;
00371     alpm_list_t *list = NULL, *remove = NULL; /* allow checkdeps usage with trans->packages */
00372     alpm_list_t *i, *j;
00373     int ret = 0;
00374 
00375     ALPM_LOG_FUNC;
00376 
00377     ASSERT(db_local != NULL, RET_ERR(PM_ERR_DB_NULL, -1));
00378     ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
00379 
00380     if(data) {
00381         *data = NULL;
00382     }
00383 
00384     if(!(trans->flags & PM_TRANS_FLAG_DEPENDSONLY)) {
00385         for(i = trans->packages; i; i = i->next) {
00386             pmsyncpkg_t *sync = i->data;
00387             list = alpm_list_add(list, sync->pkg);
00388         }
00389     }
00390 
00391     if(!(trans->flags & PM_TRANS_FLAG_NODEPS)) {
00392         /* Resolve targets dependencies */
00393         EVENT(trans, PM_TRANS_EVT_RESOLVEDEPS_START, NULL, NULL);
00394         _alpm_log(PM_LOG_DEBUG, "resolving target's dependencies\n");
00395 
00396         /* build remove list for resolvedeps */
00397         for(i = trans->packages; i; i = i->next) {
00398             pmsyncpkg_t *sync = i->data;
00399             if(sync->type == PM_SYNC_TYPE_REPLACE) {
00400                 for(j = sync->data; j; j = j->next) {
00401                     remove = alpm_list_add(remove, j->data);
00402                 }
00403             }
00404         }
00405 
00406         for(i = trans->packages; i; i = i->next) {
00407             pmpkg_t *spkg = ((pmsyncpkg_t *)i->data)->pkg;
00408             if(_alpm_resolvedeps(db_local, dbs_sync, spkg, &list,
00409                         remove, trans, data) == -1) {
00410                 /* pm_errno is set by resolvedeps */
00411                 ret = -1;
00412                 goto cleanup;
00413             }
00414         }
00415 
00416         if((trans->flags & PM_TRANS_FLAG_DEPENDSONLY)) {
00417             FREELIST(trans->packages);
00418         }
00419 
00420         for(i = list; i; i = i->next) {
00421             /* add the dependencies found by resolvedeps to the transaction set */
00422             pmpkg_t *spkg = i->data;
00423             if(!_alpm_sync_find(trans->packages, alpm_pkg_get_name(spkg))) {
00424                 pmsyncpkg_t *sync = _alpm_sync_new(PM_SYNC_TYPE_DEPEND, spkg, NULL);
00425                 if(sync == NULL) {
00426                     ret = -1;
00427                     goto cleanup;
00428                 }
00429                 trans->packages = alpm_list_add(trans->packages, sync);
00430                 _alpm_log(PM_LOG_DEBUG, "adding package %s-%s to the transaction targets\n",
00431                                     alpm_pkg_get_name(spkg), alpm_pkg_get_version(spkg));
00432             }
00433         }
00434 
00435         /* re-order w.r.t. dependencies */
00436         alpm_list_t *sortlist = _alpm_sortbydeps(list, PM_TRANS_TYPE_ADD);
00437         alpm_list_t *newpkgs = NULL;
00438         for(i = sortlist; i; i = i->next) {
00439             for(j = trans->packages; j; j = j->next) {
00440                 pmsyncpkg_t *s = j->data;
00441                 if(s->pkg == i->data) {
00442                     newpkgs = alpm_list_add(newpkgs, s);
00443                 }
00444             }
00445         }
00446         alpm_list_free(sortlist);
00447         alpm_list_free(trans->packages);
00448         trans->packages = newpkgs;
00449 
00450         EVENT(trans, PM_TRANS_EVT_RESOLVEDEPS_DONE, NULL, NULL);
00451     }
00452 
00453     /* We don't care about conflicts if we're just printing uris */
00454     if(!(trans->flags & (PM_TRANS_FLAG_NOCONFLICTS | PM_TRANS_FLAG_PRINTURIS))) {
00455         /* check for inter-conflicts and whatnot */
00456         EVENT(trans, PM_TRANS_EVT_INTERCONFLICTS_START, NULL, NULL);
00457 
00458         _alpm_log(PM_LOG_DEBUG, "looking for conflicts\n");
00459         deps = _alpm_checkconflicts(db_local, list);
00460         if(deps) {
00461             int errorout = 0;
00462             alpm_list_t *asked = NULL;
00463             pmconflict_t *conflict = NULL;
00464 
00465             for(i = deps; i && !errorout; i = i->next) {
00466                 pmsyncpkg_t *sync;
00467                 pmpkg_t *found = NULL;
00468 
00469                 conflict = i->data;
00470                 _alpm_log(PM_LOG_DEBUG, "package '%s' conflicts with '%s'\n",
00471                         conflict->package1, conflict->package2);
00472                 /* check if the conflicting package is about to be removed/replaced.
00473                  * if so, then just ignore it. */
00474                 for(j = trans->packages; j && !found; j = j->next) {
00475                     sync = j->data;
00476                     if(sync->type == PM_SYNC_TYPE_REPLACE) {
00477                         found = _alpm_pkg_find(conflict->package2, sync->data);
00478                     }
00479                 }
00480                 if(found) {
00481                     _alpm_log(PM_LOG_DEBUG, "'%s' is already elected for removal -- skipping\n",
00482                             alpm_pkg_get_name(found));
00483                     continue;
00484                 }
00485 
00486                 sync = _alpm_sync_find(trans->packages, conflict->package1);
00487                 if(sync == NULL) {
00488                     _alpm_log(PM_LOG_DEBUG, "'%s' not found in transaction set -- skipping\n",
00489                               conflict->package1);
00490                     continue;
00491                 }
00492                 pmpkg_t *local = _alpm_db_get_pkgfromcache(db_local, conflict->package2);
00493                 /* check if this package provides the package it's conflicting with */
00494                 if(alpm_list_find(alpm_pkg_get_provides(sync->pkg),
00495                             conflict->package2, _alpm_prov_cmp)) {
00496                     /* treat like a replaces item so requiredby fields are
00497                      * inherited properly. */
00498                     _alpm_log(PM_LOG_DEBUG, "package '%s' provides its own conflict\n",
00499                             conflict->package1);
00500                     if(!local) {
00501                         char *rmpkg = NULL;
00502                         void *target, *depend;
00503                         /* hmmm, package2 isn't installed, so it must be conflicting
00504                          * with another package in our final list.  For example:
00505                          *
00506                          *     pacman -S blackbox xfree86
00507                          *
00508                          * If no x-servers are installed and blackbox pulls in xorg, then
00509                          * xorg and xfree86 will conflict with each other.  In this case,
00510                          * we should follow the user's preference and rip xorg out of final,
00511                          * opting for xfree86 instead.
00512                          */
00513 
00514                         /* figure out which one was requested in targets. If they both
00515                          * were, then it's still an unresolvable conflict. */
00516                         target = alpm_list_find_str(trans->targets, conflict->package1);
00517                         depend = alpm_list_find_str(trans->targets, conflict->package2);
00518                         if(depend && !target) {
00519                             _alpm_log(PM_LOG_DEBUG, "'%s' is in the target list -- keeping it\n",
00520                                 conflict->package2);
00521                             /* remove conflict->package1 */
00522                             rmpkg = conflict->package1;
00523                         } else if(target && !depend) {
00524                             _alpm_log(PM_LOG_DEBUG, "'%s' is in the target list -- keeping it\n",
00525                                 conflict->package1);
00526                             /* remove conflict->package2 */
00527                             rmpkg = conflict->package2;
00528                         } else {
00529                             /* miss->target2 is not needed, miss->target already provides
00530                              * it, let's resolve the conflict */
00531                             rmpkg = conflict->package2;
00532                         }
00533                         if(rmpkg) {
00534                             pmsyncpkg_t *rsync = _alpm_sync_find(trans->packages, rmpkg);
00535                             if(rsync) {
00536                                 void *vpkg;
00537                                 _alpm_log(PM_LOG_DEBUG, "removing '%s' from target list\n",
00538                                         rsync->pkg->name);
00539                                 trans->packages = alpm_list_remove(trans->packages, rsync,
00540                                         syncpkg_cmp, &vpkg);
00541                                 _alpm_sync_free(vpkg);
00542                             }
00543                             continue;
00544                         }
00545                     }
00546                 }
00547                 /* It's a conflict -- see if they want to remove it */
00548                 _alpm_log(PM_LOG_DEBUG, "resolving package '%s' conflict\n",
00549                         conflict->package1);
00550                 if(local) {
00551                     int doremove = 0;
00552                     if(!alpm_list_find_str(asked, conflict->package2)) {
00553                         QUESTION(trans, PM_TRANS_CONV_CONFLICT_PKG, conflict->package1,
00554                                 conflict->package2, NULL, &doremove);
00555                         asked = alpm_list_add(asked, strdup(conflict->package2));
00556                         if(doremove) {
00557                             if(sync->type != PM_SYNC_TYPE_REPLACE) {
00558                                 /* switch this sync type to REPLACE */
00559                                 sync->type = PM_SYNC_TYPE_REPLACE;
00560                                 sync->data = NULL;
00561                             }
00562                             /* append to the replaces list */
00563                             _alpm_log(PM_LOG_DEBUG, "electing '%s' for removal\n",
00564                                     conflict->package2);
00565                             sync->data = alpm_list_add(sync->data, local);
00566                             /* see if the package is in the current target list */
00567                             pmsyncpkg_t *rsync = _alpm_sync_find(trans->packages,
00568                                     conflict->package2);
00569                             if(rsync) {
00570                                 /* remove it from the target list */
00571                                 void *vpkg;
00572                                 _alpm_log(PM_LOG_DEBUG, "removing '%s' from target list\n",
00573                                         conflict->package2);
00574                                 trans->packages = alpm_list_remove(trans->packages, rsync,
00575                                         syncpkg_cmp, &vpkg);
00576                                 _alpm_sync_free(vpkg);
00577                             }
00578                         } else {
00579                             /* abort */
00580                             _alpm_log(PM_LOG_ERROR, _("unresolvable package conflicts detected\n"));
00581                             errorout = 1;
00582                         }
00583                     }
00584                 } else {
00585                     _alpm_log(PM_LOG_ERROR, _("unresolvable package conflicts detected\n"));
00586                     errorout = 1;
00587                 }
00588             }
00589             if(errorout) {
00590                 /* The last conflict was unresolvable, so we duplicate it and add it to *data */
00591                 pm_errno = PM_ERR_CONFLICTING_DEPS;
00592                 if(data) {
00593                     pmconflict_t *lastconflict = conflict;
00594                     if((conflict = malloc(sizeof(pmconflict_t))) == NULL) {
00595                         _alpm_log(PM_LOG_ERROR, _("malloc failure: could not allocate %zd bytes\n"),
00596                                 sizeof(pmconflict_t));
00597                         FREELIST(*data);
00598                         pm_errno = PM_ERR_MEMORY;
00599                     } else {
00600                         *conflict = *lastconflict;
00601                         *data = alpm_list_add(*data, conflict);
00602                     }
00603                 }
00604                 FREELIST(asked);
00605                 alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_conflict_free);
00606                 alpm_list_free(deps);
00607                 ret = -1;
00608                 goto cleanup;
00609             }
00610             FREELIST(asked);
00611             FREELIST(deps);
00612         }
00613         EVENT(trans, PM_TRANS_EVT_INTERCONFLICTS_DONE, NULL, NULL);
00614     }
00615 
00616     if(!(trans->flags & PM_TRANS_FLAG_NODEPS)) {
00617         /* rebuild remove and list */
00618         alpm_list_free(list);
00619         list = NULL;
00620         for(i = trans->packages; i; i = i->next) {
00621             pmsyncpkg_t *sync = i->data;
00622             list = alpm_list_add(list, sync->pkg);
00623         }
00624         alpm_list_free(remove);
00625         remove = NULL;
00626         for(i = trans->packages; i; i = i->next) {
00627             pmsyncpkg_t *sync = i->data;
00628             if(sync->type == PM_SYNC_TYPE_REPLACE) {
00629                 for(j = sync->data; j; j = j->next) {
00630                     remove = alpm_list_add(remove, j->data);
00631                 }
00632             }
00633         }
00634 
00635         _alpm_log(PM_LOG_DEBUG, "checking dependencies\n");
00636         deps = alpm_checkdeps(db_local, 1, remove, list);
00637         if(deps) {
00638             pm_errno = PM_ERR_UNSATISFIED_DEPS;
00639             ret = -1;
00640             if(data) {
00641                 *data = deps;
00642             } else {
00643                 FREELIST(deps);
00644             }
00645             goto cleanup;
00646         }
00647     }
00648 
00649 cleanup:
00650     alpm_list_free(list);
00651     alpm_list_free(remove);
00652 
00653     return(ret);
00654 }
00655 
00656 /** Returns a list of deltas that should be downloaded instead of the
00657  * package.
00658  *
00659  * It first tests if a delta path exists between the currently installed
00660  * version (if any) and the version to upgrade to. If so, the delta path
00661  * is used if its size is below a set percentage (MAX_DELTA_RATIO) of
00662  * the package size, Otherwise, an empty list is returned.
00663  *
00664  * @param newpkg the new package to upgrade to
00665  * @param db_local the local database
00666  *
00667  * @return the list of pmdelta_t * objects. NULL (the empty list) is
00668  * returned if the package should be downloaded instead of deltas.
00669  */
00670 static alpm_list_t *pkg_upgrade_delta_path(pmpkg_t *newpkg, pmdb_t *db_local)
00671 {
00672     pmpkg_t *oldpkg = alpm_db_get_pkg(db_local, newpkg->name);
00673     alpm_list_t *ret = NULL;
00674 
00675     if(oldpkg) {
00676         const char *oldname = alpm_pkg_get_filename(oldpkg);
00677         char *oldpath = _alpm_filecache_find(oldname);
00678 
00679         if(oldpath) {
00680             alpm_list_t *deltas = _alpm_shortest_delta_path(
00681                     alpm_pkg_get_deltas(newpkg),
00682                     alpm_pkg_get_version(oldpkg),
00683                     alpm_pkg_get_version(newpkg));
00684 
00685             if(deltas) {
00686                 unsigned long dltsize = _alpm_delta_path_size(deltas);
00687                 unsigned long pkgsize = alpm_pkg_get_size(newpkg);
00688 
00689                 if(dltsize < pkgsize * MAX_DELTA_RATIO) {
00690                     ret = deltas;
00691                 } else {
00692                     ret = NULL;
00693                     alpm_list_free(deltas);
00694                 }
00695             }
00696 
00697             FREE(oldpath);
00698         }
00699     }
00700 
00701     return(ret);
00702 }
00703 
00704 /** Returns the size of the files that will be downloaded to install a
00705  * package.
00706  *
00707  * @param newpkg the new package to upgrade to
00708  * @param db_local the local database
00709  *
00710  * @return the size of the download
00711  */
00712 unsigned long SYMEXPORT alpm_pkg_download_size(pmpkg_t *newpkg, pmdb_t *db_local)
00713 {
00714     char *fpath = _alpm_filecache_find(alpm_pkg_get_filename(newpkg));
00715     unsigned long size = 0;
00716 
00717     if(fpath) {
00718         size = 0;
00719     } else if(handle->usedelta) {
00720         alpm_list_t *deltas = pkg_upgrade_delta_path(newpkg, db_local);
00721 
00722         if(deltas) {
00723             size = _alpm_delta_path_size_uncached(deltas);
00724         } else {
00725             size = alpm_pkg_get_size(newpkg);
00726         }
00727 
00728         alpm_list_free(deltas);
00729     } else {
00730         size = alpm_pkg_get_size(newpkg);
00731     }
00732 
00733     FREE(fpath);
00734 
00735     return(size);
00736 }
00737 
00738 /** Applies delta files to create an upgraded package file.
00739  *
00740  * All intermediate files are deleted, leaving only the starting and
00741  * ending package files.
00742  *
00743  * @param trans the transaction
00744  * @param patches A list of alternating pmpkg_t * and pmdelta_t *
00745  * objects. The patch command will be built using the pmpkg_t, pmdelta_t
00746  * pair.
00747  *
00748  * @return 0 if all delta files were able to be applied, 1 otherwise.
00749  */
00750 static int apply_deltas(pmtrans_t *trans, alpm_list_t *patches)
00751 {
00752     /* keep track of the previous package in the loop to decide if a
00753      * package file should be deleted */
00754     pmpkg_t *lastpkg = NULL;
00755     int lastpkg_failed = 0;
00756     int ret = 0;
00757     const char *cachedir = _alpm_filecache_setup();
00758 
00759     alpm_list_t *p = patches;
00760     while(p) {
00761         pmpkg_t *pkg;
00762         pmdelta_t *d;
00763         char command[PATH_MAX], fname[PATH_MAX];
00764         char pkgfilename[PATH_MAX];
00765 
00766         pkg = alpm_list_getdata(p);
00767         p = alpm_list_next(p);
00768 
00769         d = alpm_list_getdata(p);
00770         p = alpm_list_next(p);
00771 
00772         /* if patching fails, ignore the rest of that package's deltas */
00773         if(lastpkg_failed) {
00774             if(pkg == lastpkg) {
00775                 continue;
00776             } else {
00777                 lastpkg_failed = 0;
00778             }
00779         }
00780 
00781         /* an example of the patch command: (using /cache for cachedir)
00782          * xdelta patch /cache/pacman_3.0.0-1_to_3.0.1-1-i686.delta \
00783          *              /cache/pacman-3.0.0-1-i686.pkg.tar.gz       \
00784          *              /cache/pacman-3.0.1-1-i686.pkg.tar.gz
00785          */
00786 
00787         /* build the patch command */
00788         snprintf(command, PATH_MAX,
00789                 "xdelta patch"         /* the command */
00790                 " %s/%s"               /* the delta */
00791                 " %s/%s-%s-%s" PKGEXT  /* the 'from' package */
00792                 " %s/%s-%s-%s" PKGEXT, /* the 'to' package */
00793                 cachedir, d->filename,
00794                 cachedir, pkg->name, d->from, pkg->arch,
00795                 cachedir, pkg->name, d->to, pkg->arch);
00796 
00797         _alpm_log(PM_LOG_DEBUG, _("command: %s\n"), command);
00798 
00799         snprintf(pkgfilename, PATH_MAX, "%s-%s-%s" PKGEXT,
00800                 pkg->name, d->to, pkg->arch);
00801 
00802         EVENT(trans, PM_TRANS_EVT_DELTA_PATCH_START, pkgfilename, d->filename);
00803 
00804         if(system(command) == 0) {
00805             EVENT(trans, PM_TRANS_EVT_DELTA_PATCH_DONE, NULL, NULL);
00806 
00807             /* delete the delta file */
00808             snprintf(fname, PATH_MAX, "%s/%s", cachedir, d->filename);
00809             unlink(fname);
00810 
00811             /* Delete the 'from' package but only if it is an intermediate
00812              * package. The starting 'from' package should be kept, just
00813              * as if deltas were not used. Delete the package file if the
00814              * previous iteration of the loop used the same package. */
00815             if(pkg == lastpkg) {
00816                 snprintf(fname, PATH_MAX, "%s/%s-%s-%s" PKGEXT,
00817                         cachedir, pkg->name, d->from, pkg->arch);
00818                 unlink(fname);
00819             } else {
00820                 lastpkg = pkg;
00821             }
00822         } else {
00823             EVENT(trans, PM_TRANS_EVT_DELTA_PATCH_FAILED, NULL, NULL);
00824             lastpkg_failed = 1;
00825             ret = 1;
00826         }
00827     }
00828 
00829     return(ret);
00830 }
00831 
00832 /** Compares the md5sum of a file to the expected value.
00833  *
00834  * If the md5sum does not match, the user is asked whether the file
00835  * should be deleted.
00836  *
00837  * @param trans the transaction
00838  * @param filename the filename of the file to test
00839  * @param md5sum the expected md5sum of the file
00840  * @param data data to write the error messages to
00841  *
00842  * @return 0 if the md5sum matched, 1 otherwise
00843  */
00844 static int test_md5sum(pmtrans_t *trans, const char *filename,
00845         const char *md5sum, alpm_list_t **data)
00846 {
00847     char *filepath;
00848     char *md5sum2;
00849     char *errormsg = NULL;
00850     int ret = 0;
00851 
00852     filepath = _alpm_filecache_find(filename);
00853     md5sum2 = alpm_get_md5sum(filepath);
00854 
00855     if(md5sum == NULL) {
00856         if(data) {
00857             /* TODO wtf is this? malloc'd strings for error messages? */
00858             if((errormsg = calloc(512, sizeof(char))) == NULL) {
00859                 RET_ERR(PM_ERR_MEMORY, -1);
00860             }
00861             snprintf(errormsg, 512, _("can't get md5 checksum for file %s\n"),
00862                     filename);
00863             *data = alpm_list_add(*data, errormsg);
00864         }
00865         ret = 1;
00866     } else if(md5sum2 == NULL) {
00867         if(data) {
00868             if((errormsg = calloc(512, sizeof(char))) == NULL) {
00869                 RET_ERR(PM_ERR_MEMORY, -1);
00870             }
00871             snprintf(errormsg, 512, _("can't get md5 checksum for file %s\n"),
00872                     filename);
00873             *data = alpm_list_add(*data, errormsg);
00874         }
00875         ret = 1;
00876     } else if(strcmp(md5sum, md5sum2) != 0) {
00877         int doremove = 0;
00878         QUESTION(trans, PM_TRANS_CONV_CORRUPTED_PKG, (char *)filename,
00879                 NULL, NULL, &doremove);
00880         if(doremove) {
00881             unlink(filepath);
00882         }
00883         if(data) {
00884             if((errormsg = calloc(512, sizeof(char))) == NULL) {
00885                 RET_ERR(PM_ERR_MEMORY, -1);
00886             }
00887             snprintf(errormsg, 512, _("file %s was corrupted (bad MD5 checksum)\n"),
00888                     filename);
00889             *data = alpm_list_add(*data, errormsg);
00890         }
00891         ret = 1;
00892     }
00893 
00894     FREE(filepath);
00895     FREE(md5sum2);
00896 
00897     return(ret);
00898 }
00899 
00900 /** Compares the md5sum of a delta to the expected value.
00901  *
00902  * @param trans the transaction
00903  * @param delta the delta to test
00904  * @param data data to write the error messages to
00905  *
00906  * @return 0 if the md5sum matched, 1 otherwise
00907  */
00908 static int test_delta_md5sum(pmtrans_t *trans, pmdelta_t *delta,
00909         alpm_list_t **data)
00910 {
00911     const char *filename;
00912     const char *md5sum;
00913     int ret = 0;
00914 
00915     filename = alpm_delta_get_filename(delta);
00916     md5sum = alpm_delta_get_md5sum(delta);
00917 
00918     ret = test_md5sum(trans, filename, md5sum, data);
00919 
00920     return(ret);
00921 }
00922 
00923 /** Compares the md5sum of a package to the expected value.
00924  *
00925  * @param trans the transaction
00926  * @param pkg the package to test
00927  * @param data data to write the error messages to
00928  *
00929  * @return 0 if the md5sum matched, 1 otherwise
00930  */
00931 static int test_pkg_md5sum(pmtrans_t *trans, pmpkg_t *pkg, alpm_list_t **data)
00932 {
00933     const char *filename;
00934     const char *md5sum;
00935     int ret = 0;
00936 
00937     filename = alpm_pkg_get_filename(pkg);
00938     md5sum = alpm_pkg_get_md5sum(pkg);
00939 
00940     ret = test_md5sum(trans, filename, md5sum, data);
00941 
00942     return(ret);
00943 }
00944 
00945 int _alpm_sync_commit(pmtrans_t *trans, pmdb_t *db_local, alpm_list_t **data)
00946 {
00947     alpm_list_t *i, *j, *files = NULL;
00948     alpm_list_t *patches = NULL, *deltas = NULL;
00949     pmtrans_t *tr = NULL;
00950     int replaces = 0, retval = 0;
00951     const char *cachedir = NULL;
00952     int dltotal = 0, dl = 0;
00953 
00954     ALPM_LOG_FUNC;
00955 
00956     ASSERT(db_local != NULL, RET_ERR(PM_ERR_DB_NULL, -1));
00957     ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
00958 
00959     cachedir = _alpm_filecache_setup();
00960     trans->state = STATE_DOWNLOADING;
00961 
00962     /* Sum up the download sizes. This has to be in its own loop because
00963      * the download loop is grouped by db. */
00964     for(j = trans->packages; j; j = j->next) {
00965         pmsyncpkg_t *sync = j->data;
00966         pmpkg_t *spkg = sync->pkg;
00967         dltotal += alpm_pkg_download_size(spkg, db_local);
00968     }
00969 
00970     /* group sync records by repository and download */
00971     for(i = handle->