Respect Ignore{Pkg,Group} for group members
[pacman-ng.git] / lib / libalpm / sync.c
blob4cbaf0cbf50bd4eab3755d4ba84d20175d8053ae
1 /*
2 * sync.c
4 * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org>
5 * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
6 * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com>
7 * Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu>
8 * Copyright (c) 2005, 2006 by Miklos Vajna <vmiklos@frugalware.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "config.h"
26 #include <sys/types.h> /* off_t */
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <fcntl.h>
30 #include <string.h>
31 #include <stdint.h> /* intmax_t */
32 #include <unistd.h>
33 #include <time.h>
34 #include <dirent.h>
36 /* libalpm */
37 #include "sync.h"
38 #include "alpm_list.h"
39 #include "log.h"
40 #include "package.h"
41 #include "db.h"
42 #include "cache.h"
43 #include "deps.h"
44 #include "conflict.h"
45 #include "trans.h"
46 #include "add.h"
47 #include "util.h"
48 #include "handle.h"
49 #include "alpm.h"
50 #include "dload.h"
51 #include "delta.h"
52 #include "remove.h"
54 /** Check for new version of pkg in sync repos
55 * (only the first occurrence is considered in sync)
57 pmpkg_t SYMEXPORT *alpm_sync_newversion(pmpkg_t *pkg, alpm_list_t *dbs_sync)
59 ASSERT(pkg != NULL, return(NULL));
61 alpm_list_t *i;
62 pmpkg_t *spkg = NULL;
64 for(i = dbs_sync; !spkg && i; i = i->next) {
65 spkg = _alpm_db_get_pkgfromcache(i->data, alpm_pkg_get_name(pkg));
68 if(spkg == NULL) {
69 _alpm_log(PM_LOG_DEBUG, "'%s' not found in sync db => no upgrade\n",
70 alpm_pkg_get_name(pkg));
71 return(NULL);
74 /* compare versions and see if spkg is an upgrade */
75 if(_alpm_pkg_compare_versions(spkg, pkg) > 0) {
76 _alpm_log(PM_LOG_DEBUG, "new version of '%s' found (%s => %s)\n",
77 alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg),
78 alpm_pkg_get_version(spkg));
79 return(spkg);
81 /* spkg is not an upgrade */
82 return(NULL);
85 /** Search for packages to upgrade and add them to the transaction.
86 * @return 0 on success, -1 on error (pm_errno is set accordingly)
88 int SYMEXPORT alpm_sync_sysupgrade(int enable_downgrade)
90 alpm_list_t *i, *j, *k;
91 pmtrans_t *trans;
92 pmdb_t *db_local;
93 alpm_list_t *dbs_sync;
95 ALPM_LOG_FUNC;
97 ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1));
98 trans = handle->trans;
99 db_local = handle->db_local;
100 dbs_sync = handle->dbs_sync;
101 ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
102 ASSERT(trans->state == STATE_INITIALIZED, RET_ERR(PM_ERR_TRANS_NOT_INITIALIZED, -1));
104 _alpm_log(PM_LOG_DEBUG, "checking for package upgrades\n");
105 for(i = _alpm_db_get_pkgcache(db_local); i; i = i->next) {
106 pmpkg_t *lpkg = i->data;
108 if(_alpm_pkg_find(trans->add, lpkg->name)) {
109 _alpm_log(PM_LOG_DEBUG, "%s is already in the target list -- skipping\n", lpkg->name);
110 continue;
113 /* Search for literal then replacers in each sync database.
114 * If found, don't check other databases */
115 for(j = dbs_sync; j; j = j->next) {
116 pmdb_t *sdb = j->data;
117 /* Check sdb */
118 pmpkg_t *spkg = _alpm_db_get_pkgfromcache(sdb, lpkg->name);
119 if(spkg) { /* 1. literal was found in sdb */
120 int cmp = _alpm_pkg_compare_versions(spkg, lpkg);
121 if(cmp > 0) {
122 _alpm_log(PM_LOG_DEBUG, "new version of '%s' found (%s => %s)\n",
123 lpkg->name, lpkg->version, spkg->version);
124 /* check IgnorePkg/IgnoreGroup */
125 if(_alpm_pkg_should_ignore(spkg) || _alpm_pkg_should_ignore(lpkg)) {
126 _alpm_log(PM_LOG_WARNING, _("%s: ignoring package upgrade (%s => %s)\n"),
127 lpkg->name, lpkg->version, spkg->version);
128 } else {
129 _alpm_log(PM_LOG_DEBUG, "adding package %s-%s to the transaction targets\n",
130 spkg->name, spkg->version);
131 trans->add = alpm_list_add(trans->add, spkg);
133 } else if(cmp < 0) {
134 if(enable_downgrade) {
135 /* check IgnorePkg/IgnoreGroup */
136 if(_alpm_pkg_should_ignore(spkg) || _alpm_pkg_should_ignore(lpkg)) {
137 _alpm_log(PM_LOG_WARNING, _("%s: ignoring package downgrade (%s => %s)\n"),
138 lpkg->name, lpkg->version, spkg->version);
139 } else {
140 _alpm_log(PM_LOG_WARNING, _("%s: downgrading from version %s to version %s\n"),
141 lpkg->name, lpkg->version, spkg->version);
142 trans->add = alpm_list_add(trans->add, spkg);
144 } else {
145 _alpm_log(PM_LOG_WARNING, _("%s: local (%s) is newer than %s (%s)\n"),
146 lpkg->name, lpkg->version, sdb->treename, spkg->version);
149 break; /* jump to next local package */
150 } else { /* 2. search for replacers in sdb */
151 int found = 0;
152 for(k = _alpm_db_get_pkgcache(sdb); k; k = k->next) {
153 spkg = k->data;
154 if(alpm_list_find_str(alpm_pkg_get_replaces(spkg), lpkg->name)) {
155 found = 1;
156 /* check IgnorePkg/IgnoreGroup */
157 if(_alpm_pkg_should_ignore(spkg) || _alpm_pkg_should_ignore(lpkg)) {
158 _alpm_log(PM_LOG_WARNING, _("ignoring package replacement (%s-%s => %s-%s)\n"),
159 lpkg->name, lpkg->version, spkg->name, spkg->version);
160 continue;
163 int doreplace = 0;
164 QUESTION(trans, PM_TRANS_CONV_REPLACE_PKG, lpkg, spkg, sdb->treename, &doreplace);
165 if(!doreplace) {
166 continue;
169 /* If spkg is already in the target list, we append lpkg to spkg's removes list */
170 pmpkg_t *tpkg = _alpm_pkg_find(trans->add, spkg->name);
171 if(tpkg) {
172 /* sanity check, multiple repos can contain spkg->name */
173 if(tpkg->origin_data.db != sdb) {
174 _alpm_log(PM_LOG_WARNING, _("cannot replace %s by %s\n"),
175 lpkg->name, spkg->name);
176 continue;
178 _alpm_log(PM_LOG_DEBUG, "appending %s to the removes list of %s\n",
179 lpkg->name, tpkg->name);
180 tpkg->removes = alpm_list_add(tpkg->removes, lpkg);
181 /* check the to-be-replaced package's reason field */
182 if(alpm_pkg_get_reason(lpkg) == PM_PKG_REASON_EXPLICIT) {
183 tpkg->reason = PM_PKG_REASON_EXPLICIT;
185 } else { /* add spkg to the target list */
186 /* copy over reason */
187 spkg->reason = alpm_pkg_get_reason(lpkg);
188 spkg->removes = alpm_list_add(NULL, lpkg);
189 _alpm_log(PM_LOG_DEBUG, "adding package %s-%s to the transaction targets\n",
190 spkg->name, spkg->version);
191 trans->add = alpm_list_add(trans->add, spkg);
195 if(found) {
196 break; /* jump to next local package */
202 return(0);
205 static int sync_pkg(pmpkg_t *spkg, alpm_list_t *pkg_list)
207 pmtrans_t *trans;
208 pmdb_t *db_local;
209 pmpkg_t *local;
211 ALPM_LOG_FUNC;
213 trans = handle->trans;
214 db_local = handle->db_local;
216 if(_alpm_pkg_find(pkg_list, alpm_pkg_get_name(spkg))) {
217 RET_ERR(PM_ERR_TRANS_DUP_TARGET, -1);
220 local = _alpm_db_get_pkgfromcache(db_local, alpm_pkg_get_name(spkg));
221 if(local) {
222 int cmp = _alpm_pkg_compare_versions(spkg, local);
223 if(cmp == 0) {
224 if(trans->flags & PM_TRANS_FLAG_NEEDED) {
225 /* with the NEEDED flag, packages up to date are not reinstalled */
226 _alpm_log(PM_LOG_WARNING, _("%s-%s is up to date -- skipping\n"),
227 alpm_pkg_get_name(local), alpm_pkg_get_version(local));
228 return(0);
229 } else {
230 _alpm_log(PM_LOG_WARNING, _("%s-%s is up to date -- reinstalling\n"),
231 alpm_pkg_get_name(local), alpm_pkg_get_version(local));
234 } else if(cmp < 0) {
235 /* local version is newer */
236 _alpm_log(PM_LOG_WARNING, _("downgrading package %s (%s => %s)\n"),
237 alpm_pkg_get_name(local), alpm_pkg_get_version(local),
238 alpm_pkg_get_version(spkg));
242 /* add the package to the transaction */
243 spkg->reason = PM_PKG_REASON_EXPLICIT;
244 _alpm_log(PM_LOG_DEBUG, "adding package %s-%s to the transaction targets\n",
245 alpm_pkg_get_name(spkg), alpm_pkg_get_version(spkg));
246 trans->add = alpm_list_add(trans->add, spkg);
248 return(0);
251 static int sync_group(alpm_list_t *dbs_sync, const char *target)
253 alpm_list_t *i, *j;
254 alpm_list_t *known_pkgs = NULL;
255 pmgrp_t *grp;
256 int found = 0;
258 ALPM_LOG_FUNC;
260 _alpm_log(PM_LOG_DEBUG, "%s package not found, searching for group...\n", target);
261 for(i = dbs_sync; i; i = i->next) {
262 pmdb_t *db = i->data;
263 grp = alpm_db_readgrp(db, target);
264 if(grp) {
265 found = 1;
266 for(j = alpm_grp_get_pkgs(grp); j; j = j->next) {
267 pmpkg_t *pkg = j->data;
269 /* check if group member is ignored */
270 if(_alpm_pkg_should_ignore(pkg)) {
271 int install = 0;
272 QUESTION(handle->trans, PM_TRANS_CONV_INSTALL_IGNOREPKG, pkg,
273 NULL, NULL, &install);
274 if(install == 0) {
275 _alpm_log(PM_LOG_WARNING, _("skipping target: %s\n"), alpm_pkg_get_name(pkg));
276 continue;
280 if(sync_pkg(pkg, known_pkgs) == -1) {
281 if(pm_errno == PM_ERR_TRANS_DUP_TARGET || pm_errno == PM_ERR_PKG_IGNORED) {
282 /* just skip duplicate or ignored targets */
283 continue;
284 } else {
285 alpm_list_free(known_pkgs);
286 return(-1);
289 known_pkgs = alpm_list_add(known_pkgs, pkg);
293 alpm_list_free(known_pkgs);
295 if(!found) {
296 /* pass through any 'found but ignored' errors */
297 if(pm_errno != PM_ERR_PKG_IGNORED) {
298 pm_errno = PM_ERR_PKG_NOT_FOUND;
300 return(-1);
303 return(0);
306 static int sync_target(alpm_list_t *dbs_sync, const char *target)
308 pmpkg_t *spkg;
309 pmdepend_t *dep; /* provisions and dependencies are also allowed */
311 ALPM_LOG_FUNC;
313 /* Sanity checks */
314 ASSERT(target != NULL && strlen(target) != 0, RET_ERR(PM_ERR_WRONG_ARGS, -1));
315 ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1));
317 dep = _alpm_splitdep(target);
318 spkg = _alpm_resolvedep(dep, dbs_sync, NULL, 1);
319 _alpm_dep_free(dep);
321 if(spkg != NULL) {
322 return(sync_pkg(spkg, handle->trans->add));
325 return(sync_group(dbs_sync, target));
328 /** Add a sync target to the transaction.
329 * @param target the name of the sync target to add
330 * @return 0 on success, -1 on error (pm_errno is set accordingly)
332 int SYMEXPORT alpm_sync_dbtarget(char *dbname, char *target)
334 alpm_list_t *i;
335 alpm_list_t *dbs_sync;
337 ALPM_LOG_FUNC;
339 /* Sanity checks */
340 ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1));
341 dbs_sync = handle->dbs_sync;
343 /* we are looking for a package in a specific database */
344 alpm_list_t *dbs = NULL;
345 _alpm_log(PM_LOG_DEBUG, "searching for target '%s' in repo '%s'\n", target, dbname);
346 for(i = dbs_sync; i; i = i->next) {
347 pmdb_t *db = i->data;
348 if(strcmp(db->treename, dbname) == 0) {
349 dbs = alpm_list_add(NULL, db);
350 break;
353 if(dbs == NULL) {
354 RET_ERR(PM_ERR_PKG_REPO_NOT_FOUND, -1);
356 int ret = sync_target(dbs, target);
357 alpm_list_free(dbs);
358 return(ret);
361 /** Add a sync target to the transaction.
362 * @param target the name of the sync target to add
363 * @return 0 on success, -1 on error (pm_errno is set accordingly)
365 int SYMEXPORT alpm_sync_target(char *target)
367 alpm_list_t *dbs_sync;
369 ALPM_LOG_FUNC;
371 /* Sanity checks */
372 ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1));
373 dbs_sync = handle->dbs_sync;
375 return(sync_target(dbs_sync,target));
378 /** Compute the size of the files that will be downloaded to install a
379 * package.
380 * @param newpkg the new package to upgrade to
382 static int compute_download_size(pmpkg_t *newpkg)
384 const char *fname;
385 char *fpath;
386 off_t size = 0;
388 if(newpkg->origin == PKG_FROM_FILE) {
389 newpkg->infolevel |= INFRQ_DSIZE;
390 newpkg->download_size = 0;
391 return(0);
394 fname = alpm_pkg_get_filename(newpkg);
395 ASSERT(fname != NULL, RET_ERR(PM_ERR_PKG_INVALID_NAME, -1));
396 fpath = _alpm_filecache_find(fname);
398 if(fpath) {
399 FREE(fpath);
400 size = 0;
401 } else if(handle->usedelta) {
402 off_t dltsize;
403 off_t pkgsize = alpm_pkg_get_size(newpkg);
405 dltsize = _alpm_shortest_delta_path(
406 alpm_pkg_get_deltas(newpkg),
407 alpm_pkg_get_filename(newpkg),
408 &newpkg->delta_path);
410 if(newpkg->delta_path && (dltsize < pkgsize * MAX_DELTA_RATIO)) {
411 _alpm_log(PM_LOG_DEBUG, "using delta size\n");
412 size = dltsize;
413 } else {
414 _alpm_log(PM_LOG_DEBUG, "using package size\n");
415 size = alpm_pkg_get_size(newpkg);
416 alpm_list_free(newpkg->delta_path);
417 newpkg->delta_path = NULL;
419 } else {
420 size = alpm_pkg_get_size(newpkg);
423 _alpm_log(PM_LOG_DEBUG, "setting download size %jd for pkg %s\n",
424 (intmax_t)size, alpm_pkg_get_name(newpkg));
426 newpkg->infolevel |= INFRQ_DSIZE;
427 newpkg->download_size = size;
428 return(0);
431 int _alpm_sync_prepare(pmtrans_t *trans, pmdb_t *db_local, alpm_list_t *dbs_sync, alpm_list_t **data)
433 alpm_list_t *deps = NULL;
434 alpm_list_t *unresolvable = NULL;
435 alpm_list_t *i, *j;
436 alpm_list_t *remove = NULL;
437 int ret = 0;
439 ALPM_LOG_FUNC;
441 ASSERT(db_local != NULL, RET_ERR(PM_ERR_DB_NULL, -1));
442 ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
444 if(data) {
445 *data = NULL;
448 if(!(trans->flags & PM_TRANS_FLAG_NODEPS)) {
449 alpm_list_t *resolved = NULL; /* target list after resolvedeps */
451 /* Build up list by repeatedly resolving each transaction package */
452 /* Resolve targets dependencies */
453 EVENT(trans, PM_TRANS_EVT_RESOLVEDEPS_START, NULL, NULL);
454 _alpm_log(PM_LOG_DEBUG, "resolving target's dependencies\n");
456 /* build remove list for resolvedeps */
457 for(i = trans->add; i; i = i->next) {
458 pmpkg_t *spkg = i->data;
459 for(j = spkg->removes; j; j = j->next) {
460 remove = alpm_list_add(remove, j->data);
464 /* Compute the fake local database for resolvedeps (partial fix for the phonon/qt issue) */
465 alpm_list_t *localpkgs = alpm_list_diff(_alpm_db_get_pkgcache(db_local), trans->add, _alpm_pkg_cmp);
467 /* Resolve packages in the transaction one at a time, in addtion
468 building up a list of packages which could not be resolved. */
469 for(i = trans->add; i; i = i->next) {
470 pmpkg_t *pkg = i->data;
471 if(_alpm_resolvedeps(localpkgs, dbs_sync, pkg, trans->add,
472 &resolved, remove, data) == -1) {
473 unresolvable = alpm_list_add(unresolvable, pkg);
475 /* Else, [resolved] now additionally contains [pkg] and all of its
476 dependencies not already on the list */
478 alpm_list_free(localpkgs);
480 /* If there were unresolvable top-level packages, prompt the user to
481 see if they'd like to ignore them rather than failing the sync */
482 if(unresolvable != NULL) {
483 int remove_unresolvable = 0;
484 QUESTION(handle->trans, PM_TRANS_CONV_REMOVE_PKGS, unresolvable,
485 NULL, NULL, &remove_unresolvable);
486 if (remove_unresolvable) {
487 /* User wants to remove the unresolvable packages from the
488 transaction. The packages will be removed from the actual
489 transaction when the transaction packages are replaced with a
490 dependency-reordered list below */
491 pm_errno = 0; /* pm_errno was set by resolvedeps */
492 if(data) {
493 alpm_list_free_inner(*data, (alpm_list_fn_free)_alpm_depmiss_free);
494 alpm_list_free(*data);
495 *data = NULL;
497 } else {
498 /* pm_errno is set by resolvedeps */
499 alpm_list_free(resolved);
500 ret = -1;
501 goto cleanup;
505 /* Set DEPEND reason for pulled packages */
506 for(i = resolved; i; i = i->next) {
507 pmpkg_t *pkg = i->data;
508 if(!_alpm_pkg_find(trans->add, pkg->name)) {
509 pkg->reason = PM_PKG_REASON_DEPEND;
513 /* Unresolvable packages will be removed from the target list, so
514 we free the transaction specific fields */
515 alpm_list_free_inner(unresolvable, (alpm_list_fn_free)_alpm_pkg_free_trans);
517 /* re-order w.r.t. dependencies */
518 alpm_list_free(trans->add);
519 trans->add = _alpm_sortbydeps(resolved, 0);
520 alpm_list_free(resolved);
522 EVENT(trans, PM_TRANS_EVT_RESOLVEDEPS_DONE, NULL, NULL);
525 if(!(trans->flags & PM_TRANS_FLAG_NOCONFLICTS)) {
526 /* check for inter-conflicts and whatnot */
527 EVENT(trans, PM_TRANS_EVT_INTERCONFLICTS_START, NULL, NULL);
529 _alpm_log(PM_LOG_DEBUG, "looking for conflicts\n");
531 /* 1. check for conflicts in the target list */
532 _alpm_log(PM_LOG_DEBUG, "check targets vs targets\n");
533 deps = _alpm_innerconflicts(trans->add);
535 for(i = deps; i; i = i->next) {
536 pmconflict_t *conflict = i->data;
537 pmpkg_t *rsync, *sync, *sync1, *sync2;
539 /* have we already removed one of the conflicting targets? */
540 sync1 = _alpm_pkg_find(trans->add, conflict->package1);
541 sync2 = _alpm_pkg_find(trans->add, conflict->package2);
542 if(!sync1 || !sync2) {
543 continue;
546 _alpm_log(PM_LOG_DEBUG, "conflicting packages in the sync list: '%s' <-> '%s'\n",
547 conflict->package1, conflict->package2);
549 /* if sync1 provides sync2, we remove sync2 from the targets, and vice versa */
550 pmdepend_t *dep1 = _alpm_splitdep(conflict->package1);
551 pmdepend_t *dep2 = _alpm_splitdep(conflict->package2);
552 if(alpm_depcmp(sync1, dep2)) {
553 rsync = sync2;
554 sync = sync1;
555 } else if(alpm_depcmp(sync2, dep1)) {
556 rsync = sync1;
557 sync = sync2;
558 } else {
559 _alpm_log(PM_LOG_ERROR, _("unresolvable package conflicts detected\n"));
560 pm_errno = PM_ERR_CONFLICTING_DEPS;
561 ret = -1;
562 if(data) {
563 pmconflict_t *newconflict = _alpm_conflict_dup(conflict);
564 if(newconflict) {
565 *data = alpm_list_add(*data, newconflict);
568 alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_conflict_free);
569 alpm_list_free(deps);
570 _alpm_dep_free(dep1);
571 _alpm_dep_free(dep2);
572 goto cleanup;
574 _alpm_dep_free(dep1);
575 _alpm_dep_free(dep2);
577 /* Prints warning */
578 _alpm_log(PM_LOG_WARNING,
579 _("removing '%s' from target list because it conflicts with '%s'\n"),
580 rsync->name, sync->name);
581 trans->add = alpm_list_remove(trans->add, rsync, _alpm_pkg_cmp, NULL);
582 _alpm_pkg_free_trans(rsync); /* rsync is not transaction target anymore */
583 continue;
586 alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_conflict_free);
587 alpm_list_free(deps);
588 deps = NULL;
590 /* 2. we check for target vs db conflicts (and resolve)*/
591 _alpm_log(PM_LOG_DEBUG, "check targets vs db and db vs targets\n");
592 deps = _alpm_outerconflicts(db_local, trans->add);
594 for(i = deps; i; i = i->next) {
595 pmconflict_t *conflict = i->data;
597 /* if conflict->package2 (the local package) is not elected for removal,
598 we ask the user */
599 int found = 0;
600 for(j = trans->add; j && !found; j = j->next) {
601 pmpkg_t *spkg = j->data;
602 if(_alpm_pkg_find(spkg->removes, conflict->package2)) {
603 found = 1;
606 if(found) {
607 continue;
610 _alpm_log(PM_LOG_DEBUG, "package '%s' conflicts with '%s'\n",
611 conflict->package1, conflict->package2);
613 pmpkg_t *sync = _alpm_pkg_find(trans->add, conflict->package1);
614 pmpkg_t *local = _alpm_db_get_pkgfromcache(db_local, conflict->package2);
615 int doremove = 0;
616 QUESTION(trans, PM_TRANS_CONV_CONFLICT_PKG, conflict->package1,
617 conflict->package2, conflict->reason, &doremove);
618 if(doremove) {
619 /* append to the removes list */
620 _alpm_log(PM_LOG_DEBUG, "electing '%s' for removal\n", conflict->package2);
621 sync->removes = alpm_list_add(sync->removes, local);
622 } else { /* abort */
623 _alpm_log(PM_LOG_ERROR, _("unresolvable package conflicts detected\n"));
624 pm_errno = PM_ERR_CONFLICTING_DEPS;
625 ret = -1;
626 if(data) {
627 pmconflict_t *newconflict = _alpm_conflict_dup(conflict);
628 if(newconflict) {
629 *data = alpm_list_add(*data, newconflict);
632 alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_conflict_free);
633 alpm_list_free(deps);
634 goto cleanup;
637 EVENT(trans, PM_TRANS_EVT_INTERCONFLICTS_DONE, NULL, NULL);
638 alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_conflict_free);
639 alpm_list_free(deps);
642 /* Build trans->remove list */
643 for(i = trans->add; i; i = i->next) {
644 pmpkg_t *spkg = i->data;
645 for(j = spkg->removes; j; j = j->next) {
646 trans->remove = alpm_list_add(trans->remove, _alpm_pkg_dup(j->data));
650 if(!(trans->flags & PM_TRANS_FLAG_NODEPS)) {
651 _alpm_log(PM_LOG_DEBUG, "checking dependencies\n");
652 deps = alpm_checkdeps(_alpm_db_get_pkgcache(db_local), 1, trans->remove, trans->add);
653 if(deps) {
654 pm_errno = PM_ERR_UNSATISFIED_DEPS;
655 ret = -1;
656 if(data) {
657 *data = deps;
658 } else {
659 alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_depmiss_free);
660 alpm_list_free(deps);
662 goto cleanup;
665 for(i = trans->add; i; i = i->next) {
666 /* update download size field */
667 pmpkg_t *spkg = i->data;
668 if(compute_download_size(spkg) != 0) {
669 ret = -1;
670 goto cleanup;
674 cleanup:
675 alpm_list_free(unresolvable);
676 alpm_list_free(remove);
678 return(ret);
681 /** Returns the size of the files that will be downloaded to install a
682 * package.
683 * @param newpkg the new package to upgrade to
684 * @return the size of the download
686 off_t SYMEXPORT alpm_pkg_download_size(pmpkg_t *newpkg)
688 if(!(newpkg->infolevel & INFRQ_DSIZE)) {
689 compute_download_size(newpkg);
691 return(newpkg->download_size);
694 static int endswith(const char *filename, const char *extension)
696 const char *s = filename + strlen(filename) - strlen(extension);
697 return(strcmp(s, extension) == 0);
700 /** Applies delta files to create an upgraded package file.
702 * All intermediate files are deleted, leaving only the starting and
703 * ending package files.
705 * @param trans the transaction
707 * @return 0 if all delta files were able to be applied, 1 otherwise.
709 static int apply_deltas(pmtrans_t *trans)
711 alpm_list_t *i;
712 int ret = 0;
713 const char *cachedir = _alpm_filecache_setup();
715 for(i = trans->add; i; i = i->next) {
716 pmpkg_t *spkg = i->data;
717 alpm_list_t *delta_path = spkg->delta_path;
718 alpm_list_t *dlts = NULL;
720 if(!delta_path) {
721 continue;
724 for(dlts = delta_path; dlts; dlts = dlts->next) {
725 pmdelta_t *d = dlts->data;
726 char *delta, *from, *to;
727 char command[PATH_MAX];
728 size_t len = 0;
730 delta = _alpm_filecache_find(d->delta);
731 /* the initial package might be in a different cachedir */
732 if(dlts == delta_path) {
733 from = _alpm_filecache_find(d->from);
734 } else {
735 /* len = cachedir len + from len + '/' + null */
736 len = strlen(cachedir) + strlen(d->from) + 2;
737 CALLOC(from, len, sizeof(char), RET_ERR(PM_ERR_MEMORY, 1));
738 snprintf(from, len, "%s/%s", cachedir, d->from);
740 len = strlen(cachedir) + strlen(d->to) + 2;
741 CALLOC(to, len, sizeof(char), RET_ERR(PM_ERR_MEMORY, 1));
742 snprintf(to, len, "%s/%s", cachedir, d->to);
744 /* build the patch command */
745 if(endswith(to, ".gz")) {
746 /* special handling for gzip : we disable timestamp with -n option */
747 snprintf(command, PATH_MAX, "xdelta3 -d -q -R -c -s %s %s | gzip -n > %s", from, delta, to);
748 } else {
749 snprintf(command, PATH_MAX, "xdelta3 -d -q -s %s %s %s", from, delta, to);
752 _alpm_log(PM_LOG_DEBUG, "command: %s\n", command);
754 EVENT(trans, PM_TRANS_EVT_DELTA_PATCH_START, d->to, d->delta);
756 int retval = system(command);
757 if(retval == 0) {
758 EVENT(trans, PM_TRANS_EVT_DELTA_PATCH_DONE, NULL, NULL);
760 /* delete the delta file */
761 unlink(delta);
763 /* Delete the 'from' package but only if it is an intermediate
764 * package. The starting 'from' package should be kept, just
765 * as if deltas were not used. */
766 if(dlts != delta_path) {
767 unlink(from);
770 FREE(from);
771 FREE(to);
772 FREE(delta);
774 if(retval != 0) {
775 /* one delta failed for this package, cancel the remaining ones */
776 EVENT(trans, PM_TRANS_EVT_DELTA_PATCH_FAILED, NULL, NULL);
777 ret = 1;
778 break;
783 return(ret);
786 /** Compares the md5sum of a file to the expected value.
788 * If the md5sum does not match, the user is asked whether the file
789 * should be deleted.
791 * @param trans the transaction
792 * @param filename the filename of the file to test
793 * @param md5sum the expected md5sum of the file
795 * @return 0 if the md5sum matched, 1 if not, -1 in case of errors
797 static int test_md5sum(pmtrans_t *trans, const char *filename,
798 const char *md5sum)
800 char *filepath;
801 int ret;
803 filepath = _alpm_filecache_find(filename);
805 ret = _alpm_test_md5sum(filepath, md5sum);
807 if(ret == 1) {
808 int doremove = 0;
809 QUESTION(trans, PM_TRANS_CONV_CORRUPTED_PKG, (char *)filename,
810 NULL, NULL, &doremove);
811 if(doremove) {
812 unlink(filepath);
816 FREE(filepath);
818 return(ret);
821 int _alpm_sync_commit(pmtrans_t *trans, pmdb_t *db_local, alpm_list_t **data)
823 alpm_list_t *i, *j, *files = NULL;
824 alpm_list_t *deltas = NULL;
825 int replaces = 0;
826 int errors = 0;
827 const char *cachedir = NULL;
828 int ret = -1;
830 ALPM_LOG_FUNC;
832 ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
834 cachedir = _alpm_filecache_setup();
835 trans->state = STATE_DOWNLOADING;
837 /* Total progress - figure out the total download size if required to
838 * pass to the callback. This function is called once, and it is up to the
839 * frontend to compute incremental progress. */
840 if(handle->totaldlcb) {
841 off_t total_size = (off_t)0;
842 /* sum up the download size for each package and store total */
843 for(i = trans->add; i; i = i->next) {
844 pmpkg_t *spkg = i->data;
845 total_size += spkg->download_size;
847 handle->totaldlcb(total_size);
850 /* group sync records by repository and download */
851 for(i = handle->dbs_sync; i; i = i->next) {
852 pmdb_t *current = i->data;
854 for(j = trans->add; j; j = j->next) {
855 pmpkg_t *spkg = j->data;
857 if(spkg->origin == PKG_FROM_CACHE && current == spkg->origin_data.db) {
858 const char *fname = NULL;
860 fname = alpm_pkg_get_filename(spkg);
861 ASSERT(fname != NULL, RET_ERR(PM_ERR_PKG_INVALID_NAME, -1));
862 alpm_list_t *delta_path = spkg->delta_path;
863 if(delta_path) {
864 /* using deltas */
865 alpm_list_t *dlts = NULL;
867 for(dlts = delta_path; dlts; dlts = dlts->next) {
868 pmdelta_t *d = dlts->data;
870 if(d->download_size != 0) {
871 /* add the delta filename to the download list if needed */
872 files = alpm_list_add(files, strdup(d->delta));
875 /* keep a list of all the delta files for md5sums */
876 deltas = alpm_list_add(deltas, d);
879 } else {
880 /* not using deltas */
881 if(spkg->download_size != 0) {
882 /* add the filename to the download list if needed */
883 files = alpm_list_add(files, strdup(fname));
890 if(files) {
891 EVENT(trans, PM_TRANS_EVT_RETRIEVE_START, current->treename, NULL);
892 errors = _alpm_download_files(files, current->servers, cachedir);
894 if (errors) {
895 _alpm_log(PM_LOG_WARNING, _("failed to retrieve some files from %s\n"),
896 current->treename);
897 if(pm_errno == 0) {
898 pm_errno = PM_ERR_RETRIEVE;
900 goto error;
902 FREELIST(files);
906 for(j = trans->add; j; j = j->next) {
907 pmpkg_t *pkg = j->data;
908 pkg->infolevel &= ~INFRQ_DSIZE;
909 pkg->download_size = 0;
912 /* clear out value to let callback know we are done */
913 if(handle->totaldlcb) {
914 handle->totaldlcb(0);
917 /* if we have deltas to work with */
918 if(handle->usedelta && deltas) {
919 int ret = 0;
920 errors = 0;
921 /* Check integrity of deltas */
922 EVENT(trans, PM_TRANS_EVT_DELTA_INTEGRITY_START, NULL, NULL);
924 for(i = deltas; i; i = i->next) {
925 pmdelta_t *d = alpm_list_getdata(i);
926 const char *filename = alpm_delta_get_filename(d);
927 const char *md5sum = alpm_delta_get_md5sum(d);
929 if(test_md5sum(trans, filename, md5sum) != 0) {
930 errors++;
931 *data = alpm_list_add(*data, strdup(filename));
934 if(errors) {
935 pm_errno = PM_ERR_DLT_INVALID;
936 goto error;
938 EVENT(trans, PM_TRANS_EVT_DELTA_INTEGRITY_DONE, NULL, NULL);
940 /* Use the deltas to generate the packages */
941 EVENT(trans, PM_TRANS_EVT_DELTA_PATCHES_START, NULL, NULL);
942 ret = apply_deltas(trans);
943 EVENT(trans, PM_TRANS_EVT_DELTA_PATCHES_DONE, NULL, NULL);
945 if(ret) {
946 pm_errno = PM_ERR_DLT_PATCHFAILED;
947 goto error;
951 /* Check integrity of packages */
952 EVENT(trans, PM_TRANS_EVT_INTEGRITY_START, NULL, NULL);
954 errors = 0;
955 for(i = trans->add; i; i = i->next) {
956 pmpkg_t *spkg = i->data;
957 if(spkg->origin == PKG_FROM_FILE) {
958 continue; /* pkg_load() has been already called, this package is valid */
961 const char *filename = alpm_pkg_get_filename(spkg);
962 const char *md5sum = alpm_pkg_get_md5sum(spkg);
964 if(test_md5sum(trans, filename, md5sum) != 0) {
965 errors++;
966 *data = alpm_list_add(*data, strdup(filename));
967 continue;
969 /* load the package file and replace pkgcache entry with it in the target list */
970 /* TODO: alpm_pkg_get_db() will not work on this target anymore */
971 _alpm_log(PM_LOG_DEBUG, "replacing pkgcache entry with package file for target %s\n", spkg->name);
972 char *filepath = _alpm_filecache_find(filename);
973 pmpkg_t *pkgfile;
974 if(alpm_pkg_load(filepath, 1, &pkgfile) != 0) {
975 _alpm_pkg_free(pkgfile);
976 errors++;
977 *data = alpm_list_add(*data, strdup(filename));
978 FREE(filepath);
979 continue;
981 FREE(filepath);
982 pkgfile->reason = spkg->reason; /* copy over install reason */
983 i->data = pkgfile;
984 _alpm_pkg_free_trans(spkg); /* spkg has been removed from the target list */
986 if(errors) {
987 pm_errno = PM_ERR_PKG_INVALID;
988 goto error;
990 EVENT(trans, PM_TRANS_EVT_INTEGRITY_DONE, NULL, NULL);
991 if(trans->flags & PM_TRANS_FLAG_DOWNLOADONLY) {
992 ret = 0;
993 goto error;
996 trans->state = STATE_COMMITING;
998 replaces = alpm_list_count(trans->remove);
1000 /* fileconflict check */
1001 if(!(trans->flags & PM_TRANS_FLAG_FORCE)) {
1002 EVENT(trans, PM_TRANS_EVT_FILECONFLICTS_START, NULL, NULL);
1004 _alpm_log(PM_LOG_DEBUG, "looking for file conflicts\n");
1005 alpm_list_t *conflict = _alpm_db_find_fileconflicts(db_local, trans,
1006 trans->add, trans->remove);
1007 if(conflict) {
1008 pm_errno = PM_ERR_FILE_CONFLICTS;
1009 if(data) {
1010 *data = conflict;
1011 } else {
1012 alpm_list_free_inner(conflict, (alpm_list_fn_free)_alpm_fileconflict_free);
1013 alpm_list_free(conflict);
1015 goto error;
1018 EVENT(trans, PM_TRANS_EVT_FILECONFLICTS_DONE, NULL, NULL);
1021 /* remove conflicting and to-be-replaced packages */
1022 if(replaces) {
1023 _alpm_log(PM_LOG_DEBUG, "removing conflicting and to-be-replaced packages\n");
1024 /* we want the frontend to be aware of commit details */
1025 if(_alpm_remove_packages(trans, handle->db_local) == -1) {
1026 _alpm_log(PM_LOG_ERROR, _("could not commit removal transaction\n"));
1027 goto error;
1031 /* install targets */
1032 _alpm_log(PM_LOG_DEBUG, "installing packages\n");
1033 if(_alpm_upgrade_packages(trans, handle->db_local) == -1) {
1034 _alpm_log(PM_LOG_ERROR, _("could not commit transaction\n"));
1035 goto error;
1037 ret = 0;
1039 error:
1040 FREELIST(files);
1041 alpm_list_free(deltas);
1042 return(ret);
1045 /* vim: set ts=2 sw=2 noet: */