Added gpg verification options per repo to the config file.
[pacman-ng.git] / lib / libalpm / sync.c
blob5428e40bd93b006eaa5390fc34491b5bf12e725c
1 /*
2 * sync.c
4 * Copyright (c) 2006-2011 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 <limits.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 "deps.h"
43 #include "conflict.h"
44 #include "trans.h"
45 #include "add.h"
46 #include "util.h"
47 #include "handle.h"
48 #include "alpm.h"
49 #include "dload.h"
50 #include "delta.h"
51 #include "remove.h"
52 #include "diskspace.h"
53 #include "signing.h"
55 /** Check for new version of pkg in sync repos
56 * (only the first occurrence is considered in sync)
58 pmpkg_t SYMEXPORT *alpm_sync_newversion(pmpkg_t *pkg, alpm_list_t *dbs_sync)
60 ASSERT(pkg != NULL, return NULL);
62 alpm_list_t *i;
63 pmpkg_t *spkg = NULL;
65 for(i = dbs_sync; !spkg && i; i = i->next) {
66 spkg = _alpm_db_get_pkgfromcache(i->data, alpm_pkg_get_name(pkg));
69 if(spkg == NULL) {
70 _alpm_log(PM_LOG_DEBUG, "'%s' not found in sync db => no upgrade\n",
71 alpm_pkg_get_name(pkg));
72 return NULL;
75 /* compare versions and see if spkg is an upgrade */
76 if(_alpm_pkg_compare_versions(spkg, pkg) > 0) {
77 _alpm_log(PM_LOG_DEBUG, "new version of '%s' found (%s => %s)\n",
78 alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg),
79 alpm_pkg_get_version(spkg));
80 return spkg;
82 /* spkg is not an upgrade */
83 return NULL;
86 /** Search for packages to upgrade and add them to the transaction.
87 * @return 0 on success, -1 on error (pm_errno is set accordingly)
89 int SYMEXPORT alpm_sync_sysupgrade(int enable_downgrade)
91 alpm_list_t *i, *j, *k;
92 pmtrans_t *trans;
93 pmdb_t *db_local;
94 alpm_list_t *dbs_sync;
96 ALPM_LOG_FUNC;
98 ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1));
99 trans = handle->trans;
100 db_local = handle->db_local;
101 dbs_sync = handle->dbs_sync;
102 ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
103 ASSERT(trans->state == STATE_INITIALIZED, RET_ERR(PM_ERR_TRANS_NOT_INITIALIZED, -1));
105 _alpm_log(PM_LOG_DEBUG, "checking for package upgrades\n");
106 for(i = _alpm_db_get_pkgcache(db_local); i; i = i->next) {
107 pmpkg_t *lpkg = i->data;
109 if(_alpm_pkg_find(trans->add, lpkg->name)) {
110 _alpm_log(PM_LOG_DEBUG, "%s is already in the target list -- skipping\n", lpkg->name);
111 continue;
114 /* Search for literal then replacers in each sync database.
115 * If found, don't check other databases */
116 for(j = dbs_sync; j; j = j->next) {
117 pmdb_t *sdb = j->data;
118 /* Check sdb */
119 pmpkg_t *spkg = _alpm_db_get_pkgfromcache(sdb, lpkg->name);
120 if(spkg) { /* 1. literal was found in sdb */
121 int cmp = _alpm_pkg_compare_versions(spkg, lpkg);
122 if(cmp > 0) {
123 _alpm_log(PM_LOG_DEBUG, "new version of '%s' found (%s => %s)\n",
124 lpkg->name, lpkg->version, spkg->version);
125 /* check IgnorePkg/IgnoreGroup */
126 if(_alpm_pkg_should_ignore(spkg) || _alpm_pkg_should_ignore(lpkg)) {
127 _alpm_log(PM_LOG_WARNING, _("%s: ignoring package upgrade (%s => %s)\n"),
128 lpkg->name, lpkg->version, spkg->version);
129 } else {
130 _alpm_log(PM_LOG_DEBUG, "adding package %s-%s to the transaction targets\n",
131 spkg->name, spkg->version);
132 trans->add = alpm_list_add(trans->add, spkg);
134 } else if(cmp < 0) {
135 if(enable_downgrade) {
136 /* check IgnorePkg/IgnoreGroup */
137 if(_alpm_pkg_should_ignore(spkg) || _alpm_pkg_should_ignore(lpkg)) {
138 _alpm_log(PM_LOG_WARNING, _("%s: ignoring package downgrade (%s => %s)\n"),
139 lpkg->name, lpkg->version, spkg->version);
140 } else {
141 _alpm_log(PM_LOG_WARNING, _("%s: downgrading from version %s to version %s\n"),
142 lpkg->name, lpkg->version, spkg->version);
143 trans->add = alpm_list_add(trans->add, spkg);
145 } else {
146 _alpm_log(PM_LOG_WARNING, _("%s: local (%s) is newer than %s (%s)\n"),
147 lpkg->name, lpkg->version, sdb->treename, spkg->version);
150 break; /* jump to next local package */
151 } else { /* 2. search for replacers in sdb */
152 int found = 0;
153 for(k = _alpm_db_get_pkgcache(sdb); k; k = k->next) {
154 spkg = k->data;
155 if(alpm_list_find_str(alpm_pkg_get_replaces(spkg), lpkg->name)) {
156 found = 1;
157 /* check IgnorePkg/IgnoreGroup */
158 if(_alpm_pkg_should_ignore(spkg) || _alpm_pkg_should_ignore(lpkg)) {
159 _alpm_log(PM_LOG_WARNING, _("ignoring package replacement (%s-%s => %s-%s)\n"),
160 lpkg->name, lpkg->version, spkg->name, spkg->version);
161 continue;
164 int doreplace = 0;
165 QUESTION(trans, PM_TRANS_CONV_REPLACE_PKG, lpkg, spkg, sdb->treename, &doreplace);
166 if(!doreplace) {
167 continue;
170 /* If spkg is already in the target list, we append lpkg to spkg's
171 * removes list */
172 pmpkg_t *tpkg = _alpm_pkg_find(trans->add, spkg->name);
173 if(tpkg) {
174 /* sanity check, multiple repos can contain spkg->name */
175 if(tpkg->origin_data.db != sdb) {
176 _alpm_log(PM_LOG_WARNING, _("cannot replace %s by %s\n"),
177 lpkg->name, spkg->name);
178 continue;
180 _alpm_log(PM_LOG_DEBUG, "appending %s to the removes list of %s\n",
181 lpkg->name, tpkg->name);
182 tpkg->removes = alpm_list_add(tpkg->removes, lpkg);
183 /* check the to-be-replaced package's reason field */
184 if(alpm_pkg_get_reason(lpkg) == PM_PKG_REASON_EXPLICIT) {
185 tpkg->reason = PM_PKG_REASON_EXPLICIT;
187 } else { /* add spkg to the target list */
188 /* copy over reason */
189 spkg->reason = alpm_pkg_get_reason(lpkg);
190 spkg->removes = alpm_list_add(NULL, lpkg);
191 _alpm_log(PM_LOG_DEBUG, "adding package %s-%s to the transaction targets\n",
192 spkg->name, spkg->version);
193 trans->add = alpm_list_add(trans->add, spkg);
197 if(found) {
198 break; /* jump to next local package */
204 return 0;
207 /** Find group members across a list of databases.
208 * If a member exists in several databases, only the first database is used.
209 * IgnorePkg is also handled.
210 * @param dbs the list of pmdb_t *
211 * @pram name the name of the group
212 * @return the list of pmpkg_t * (caller is responsible for alpm_list_free)
214 alpm_list_t SYMEXPORT *alpm_find_grp_pkgs(alpm_list_t *dbs,
215 const char *name)
217 alpm_list_t *i, *j, *pkgs = NULL, *ignorelist = NULL;
219 for(i = dbs; i; i = i->next) {
220 pmdb_t *db = i->data;
221 pmgrp_t *grp = alpm_db_readgrp(db, name);
223 if(!grp)
224 continue;
226 for(j = alpm_grp_get_pkgs(grp); j; j = j->next) {
227 pmpkg_t *pkg = j->data;
229 if(_alpm_pkg_find(ignorelist, alpm_pkg_get_name(pkg))) {
230 continue;
232 if(_alpm_pkg_should_ignore(pkg)) {
233 ignorelist = alpm_list_add(ignorelist, pkg);
234 int install = 0;
235 QUESTION(handle->trans, PM_TRANS_CONV_INSTALL_IGNOREPKG, pkg,
236 NULL, NULL, &install);
237 if(!install)
238 continue;
240 if(!_alpm_pkg_find(pkgs, alpm_pkg_get_name(pkg))) {
241 pkgs = alpm_list_add(pkgs, pkg);
245 alpm_list_free(ignorelist);
246 return pkgs;
249 /** Compute the size of the files that will be downloaded to install a
250 * package.
251 * @param newpkg the new package to upgrade to
253 static int compute_download_size(pmpkg_t *newpkg)
255 const char *fname;
256 char *fpath;
257 off_t size = 0;
259 if(newpkg->origin != PKG_FROM_SYNCDB) {
260 newpkg->infolevel |= INFRQ_DSIZE;
261 newpkg->download_size = 0;
262 return 0;
265 fname = alpm_pkg_get_filename(newpkg);
266 ASSERT(fname != NULL, RET_ERR(PM_ERR_PKG_INVALID_NAME, -1));
267 fpath = _alpm_filecache_find(fname);
269 if(fpath) {
270 FREE(fpath);
271 size = 0;
272 } else if(handle->usedelta) {
273 off_t dltsize;
274 off_t pkgsize = alpm_pkg_get_size(newpkg);
276 dltsize = _alpm_shortest_delta_path(
277 alpm_pkg_get_deltas(newpkg),
278 alpm_pkg_get_filename(newpkg),
279 &newpkg->delta_path);
281 if(newpkg->delta_path && (dltsize < pkgsize * MAX_DELTA_RATIO)) {
282 _alpm_log(PM_LOG_DEBUG, "using delta size\n");
283 size = dltsize;
284 } else {
285 _alpm_log(PM_LOG_DEBUG, "using package size\n");
286 size = alpm_pkg_get_size(newpkg);
287 alpm_list_free(newpkg->delta_path);
288 newpkg->delta_path = NULL;
290 } else {
291 size = alpm_pkg_get_size(newpkg);
294 _alpm_log(PM_LOG_DEBUG, "setting download size %jd for pkg %s\n",
295 (intmax_t)size, alpm_pkg_get_name(newpkg));
297 newpkg->infolevel |= INFRQ_DSIZE;
298 newpkg->download_size = size;
299 return 0;
302 int _alpm_sync_prepare(pmtrans_t *trans, pmdb_t *db_local, alpm_list_t *dbs_sync, alpm_list_t **data)
304 alpm_list_t *deps = NULL;
305 alpm_list_t *unresolvable = NULL;
306 alpm_list_t *i, *j;
307 alpm_list_t *remove = NULL;
308 int ret = 0;
310 ALPM_LOG_FUNC;
312 ASSERT(db_local != NULL, RET_ERR(PM_ERR_DB_NULL, -1));
313 ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
315 if(data) {
316 *data = NULL;
319 if(!(trans->flags & PM_TRANS_FLAG_NODEPS)) {
320 alpm_list_t *resolved = NULL; /* target list after resolvedeps */
322 /* Build up list by repeatedly resolving each transaction package */
323 /* Resolve targets dependencies */
324 EVENT(trans, PM_TRANS_EVT_RESOLVEDEPS_START, NULL, NULL);
325 _alpm_log(PM_LOG_DEBUG, "resolving target's dependencies\n");
327 /* build remove list for resolvedeps */
328 for(i = trans->add; i; i = i->next) {
329 pmpkg_t *spkg = i->data;
330 for(j = spkg->removes; j; j = j->next) {
331 remove = alpm_list_add(remove, j->data);
335 /* Compute the fake local database for resolvedeps (partial fix for the
336 * phonon/qt issue) */
337 alpm_list_t *localpkgs = alpm_list_diff(_alpm_db_get_pkgcache(db_local),
338 trans->add, _alpm_pkg_cmp);
340 /* Resolve packages in the transaction one at a time, in addition
341 building up a list of packages which could not be resolved. */
342 for(i = trans->add; i; i = i->next) {
343 pmpkg_t *pkg = i->data;
344 if(_alpm_resolvedeps(localpkgs, dbs_sync, pkg, trans->add,
345 &resolved, remove, data) == -1) {
346 unresolvable = alpm_list_add(unresolvable, pkg);
348 /* Else, [resolved] now additionally contains [pkg] and all of its
349 dependencies not already on the list */
351 alpm_list_free(localpkgs);
353 /* If there were unresolvable top-level packages, prompt the user to
354 see if they'd like to ignore them rather than failing the sync */
355 if(unresolvable != NULL) {
356 int remove_unresolvable = 0;
357 QUESTION(handle->trans, PM_TRANS_CONV_REMOVE_PKGS, unresolvable,
358 NULL, NULL, &remove_unresolvable);
359 if (remove_unresolvable) {
360 /* User wants to remove the unresolvable packages from the
361 transaction. The packages will be removed from the actual
362 transaction when the transaction packages are replaced with a
363 dependency-reordered list below */
364 pm_errno = 0; /* pm_errno was set by resolvedeps */
365 if(data) {
366 alpm_list_free_inner(*data, (alpm_list_fn_free)_alpm_depmiss_free);
367 alpm_list_free(*data);
368 *data = NULL;
370 } else {
371 /* pm_errno is set by resolvedeps */
372 alpm_list_free(resolved);
373 ret = -1;
374 goto cleanup;
378 /* Set DEPEND reason for pulled packages */
379 for(i = resolved; i; i = i->next) {
380 pmpkg_t *pkg = i->data;
381 if(!_alpm_pkg_find(trans->add, pkg->name)) {
382 pkg->reason = PM_PKG_REASON_DEPEND;
386 /* Unresolvable packages will be removed from the target list, so
387 we free the transaction specific fields */
388 alpm_list_free_inner(unresolvable, (alpm_list_fn_free)_alpm_pkg_free_trans);
390 /* re-order w.r.t. dependencies */
391 alpm_list_free(trans->add);
392 trans->add = _alpm_sortbydeps(resolved, 0);
393 alpm_list_free(resolved);
395 EVENT(trans, PM_TRANS_EVT_RESOLVEDEPS_DONE, NULL, NULL);
398 if(!(trans->flags & PM_TRANS_FLAG_NOCONFLICTS)) {
399 /* check for inter-conflicts and whatnot */
400 EVENT(trans, PM_TRANS_EVT_INTERCONFLICTS_START, NULL, NULL);
402 _alpm_log(PM_LOG_DEBUG, "looking for conflicts\n");
404 /* 1. check for conflicts in the target list */
405 _alpm_log(PM_LOG_DEBUG, "check targets vs targets\n");
406 deps = _alpm_innerconflicts(trans->add);
408 for(i = deps; i; i = i->next) {
409 pmconflict_t *conflict = i->data;
410 pmpkg_t *rsync, *sync, *sync1, *sync2;
412 /* have we already removed one of the conflicting targets? */
413 sync1 = _alpm_pkg_find(trans->add, conflict->package1);
414 sync2 = _alpm_pkg_find(trans->add, conflict->package2);
415 if(!sync1 || !sync2) {
416 continue;
419 _alpm_log(PM_LOG_DEBUG, "conflicting packages in the sync list: '%s' <-> '%s'\n",
420 conflict->package1, conflict->package2);
422 /* if sync1 provides sync2, we remove sync2 from the targets, and vice versa */
423 pmdepend_t *dep1 = _alpm_splitdep(conflict->package1);
424 pmdepend_t *dep2 = _alpm_splitdep(conflict->package2);
425 if(_alpm_depcmp(sync1, dep2)) {
426 rsync = sync2;
427 sync = sync1;
428 } else if(_alpm_depcmp(sync2, dep1)) {
429 rsync = sync1;
430 sync = sync2;
431 } else {
432 _alpm_log(PM_LOG_ERROR, _("unresolvable package conflicts detected\n"));
433 pm_errno = PM_ERR_CONFLICTING_DEPS;
434 ret = -1;
435 if(data) {
436 pmconflict_t *newconflict = _alpm_conflict_dup(conflict);
437 if(newconflict) {
438 *data = alpm_list_add(*data, newconflict);
441 alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_conflict_free);
442 alpm_list_free(deps);
443 _alpm_dep_free(dep1);
444 _alpm_dep_free(dep2);
445 goto cleanup;
447 _alpm_dep_free(dep1);
448 _alpm_dep_free(dep2);
450 /* Prints warning */
451 _alpm_log(PM_LOG_WARNING,
452 _("removing '%s' from target list because it conflicts with '%s'\n"),
453 rsync->name, sync->name);
454 trans->add = alpm_list_remove(trans->add, rsync, _alpm_pkg_cmp, NULL);
455 _alpm_pkg_free_trans(rsync); /* rsync is not transaction target anymore */
456 continue;
459 alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_conflict_free);
460 alpm_list_free(deps);
461 deps = NULL;
463 /* 2. we check for target vs db conflicts (and resolve)*/
464 _alpm_log(PM_LOG_DEBUG, "check targets vs db and db vs targets\n");
465 deps = _alpm_outerconflicts(db_local, trans->add);
467 for(i = deps; i; i = i->next) {
468 pmconflict_t *conflict = i->data;
470 /* if conflict->package2 (the local package) is not elected for removal,
471 we ask the user */
472 int found = 0;
473 for(j = trans->add; j && !found; j = j->next) {
474 pmpkg_t *spkg = j->data;
475 if(_alpm_pkg_find(spkg->removes, conflict->package2)) {
476 found = 1;
479 if(found) {
480 continue;
483 _alpm_log(PM_LOG_DEBUG, "package '%s' conflicts with '%s'\n",
484 conflict->package1, conflict->package2);
486 pmpkg_t *sync = _alpm_pkg_find(trans->add, conflict->package1);
487 pmpkg_t *local = _alpm_db_get_pkgfromcache(db_local, conflict->package2);
488 int doremove = 0;
489 QUESTION(trans, PM_TRANS_CONV_CONFLICT_PKG, conflict->package1,
490 conflict->package2, conflict->reason, &doremove);
491 if(doremove) {
492 /* append to the removes list */
493 _alpm_log(PM_LOG_DEBUG, "electing '%s' for removal\n", conflict->package2);
494 sync->removes = alpm_list_add(sync->removes, local);
495 } else { /* abort */
496 _alpm_log(PM_LOG_ERROR, _("unresolvable package conflicts detected\n"));
497 pm_errno = PM_ERR_CONFLICTING_DEPS;
498 ret = -1;
499 if(data) {
500 pmconflict_t *newconflict = _alpm_conflict_dup(conflict);
501 if(newconflict) {
502 *data = alpm_list_add(*data, newconflict);
505 alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_conflict_free);
506 alpm_list_free(deps);
507 goto cleanup;
510 EVENT(trans, PM_TRANS_EVT_INTERCONFLICTS_DONE, NULL, NULL);
511 alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_conflict_free);
512 alpm_list_free(deps);
515 /* Build trans->remove list */
516 for(i = trans->add; i; i = i->next) {
517 pmpkg_t *spkg = i->data;
518 for(j = spkg->removes; j; j = j->next) {
519 pmpkg_t *rpkg = j->data;
520 if(!_alpm_pkg_find(trans->remove, rpkg->name)) {
521 _alpm_log(PM_LOG_DEBUG, "adding '%s' to remove list\n", rpkg->name);
522 trans->remove = alpm_list_add(trans->remove, _alpm_pkg_dup(rpkg));
527 if(!(trans->flags & PM_TRANS_FLAG_NODEPS)) {
528 _alpm_log(PM_LOG_DEBUG, "checking dependencies\n");
529 deps = alpm_checkdeps(_alpm_db_get_pkgcache(db_local), 1, trans->remove, trans->add);
530 if(deps) {
531 pm_errno = PM_ERR_UNSATISFIED_DEPS;
532 ret = -1;
533 if(data) {
534 *data = deps;
535 } else {
536 alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_depmiss_free);
537 alpm_list_free(deps);
539 goto cleanup;
542 for(i = trans->add; i; i = i->next) {
543 /* update download size field */
544 pmpkg_t *spkg = i->data;
545 if(compute_download_size(spkg) != 0) {
546 ret = -1;
547 goto cleanup;
551 cleanup:
552 alpm_list_free(unresolvable);
553 alpm_list_free(remove);
555 return ret;
558 /** Returns the size of the files that will be downloaded to install a
559 * package.
560 * @param newpkg the new package to upgrade to
561 * @return the size of the download
563 off_t SYMEXPORT alpm_pkg_download_size(pmpkg_t *newpkg)
565 if(!(newpkg->infolevel & INFRQ_DSIZE)) {
566 compute_download_size(newpkg);
568 return newpkg->download_size;
571 static int endswith(const char *filename, const char *extension)
573 const char *s = filename + strlen(filename) - strlen(extension);
574 return strcmp(s, extension) == 0;
577 /** Applies delta files to create an upgraded package file.
579 * All intermediate files are deleted, leaving only the starting and
580 * ending package files.
582 * @param trans the transaction
584 * @return 0 if all delta files were able to be applied, 1 otherwise.
586 static int apply_deltas(pmtrans_t *trans)
588 alpm_list_t *i;
589 int ret = 0;
590 const char *cachedir = _alpm_filecache_setup();
592 for(i = trans->add; i; i = i->next) {
593 pmpkg_t *spkg = i->data;
594 alpm_list_t *delta_path = spkg->delta_path;
595 alpm_list_t *dlts = NULL;
597 if(!delta_path) {
598 continue;
601 for(dlts = delta_path; dlts; dlts = dlts->next) {
602 pmdelta_t *d = dlts->data;
603 char *delta, *from, *to;
604 char command[PATH_MAX];
605 size_t len = 0;
607 delta = _alpm_filecache_find(d->delta);
608 /* the initial package might be in a different cachedir */
609 if(dlts == delta_path) {
610 from = _alpm_filecache_find(d->from);
611 } else {
612 /* len = cachedir len + from len + '/' + null */
613 len = strlen(cachedir) + strlen(d->from) + 2;
614 CALLOC(from, len, sizeof(char), RET_ERR(PM_ERR_MEMORY, 1));
615 snprintf(from, len, "%s/%s", cachedir, d->from);
617 len = strlen(cachedir) + strlen(d->to) + 2;
618 CALLOC(to, len, sizeof(char), RET_ERR(PM_ERR_MEMORY, 1));
619 snprintf(to, len, "%s/%s", cachedir, d->to);
621 /* build the patch command */
622 if(endswith(to, ".gz")) {
623 /* special handling for gzip : we disable timestamp with -n option */
624 snprintf(command, PATH_MAX, "xdelta3 -d -q -R -c -s %s %s | gzip -n > %s", from, delta, to);
625 } else {
626 snprintf(command, PATH_MAX, "xdelta3 -d -q -s %s %s %s", from, delta, to);
629 _alpm_log(PM_LOG_DEBUG, "command: %s\n", command);
631 EVENT(trans, PM_TRANS_EVT_DELTA_PATCH_START, d->to, d->delta);
633 int retval = system(command);
634 if(retval == 0) {
635 EVENT(trans, PM_TRANS_EVT_DELTA_PATCH_DONE, NULL, NULL);
637 /* delete the delta file */
638 unlink(delta);
640 /* Delete the 'from' package but only if it is an intermediate
641 * package. The starting 'from' package should be kept, just
642 * as if deltas were not used. */
643 if(dlts != delta_path) {
644 unlink(from);
647 FREE(from);
648 FREE(to);
649 FREE(delta);
651 if(retval != 0) {
652 /* one delta failed for this package, cancel the remaining ones */
653 EVENT(trans, PM_TRANS_EVT_DELTA_PATCH_FAILED, NULL, NULL);
654 ret = 1;
655 break;
660 return ret;
663 /** Compares the md5sum of a file to the expected value.
665 * If the md5sum does not match, the user is asked whether the file
666 * should be deleted.
668 * @param trans the transaction
669 * @param filename the absolute path of the file to test
670 * @param md5sum the expected md5sum of the file
672 * @return 0 if the md5sum matched, 1 if not, -1 in case of errors
674 static int test_md5sum(pmtrans_t *trans, const char *filepath,
675 const char *md5sum)
677 int ret = _alpm_test_md5sum(filepath, md5sum);
678 if(ret == 1) {
679 int doremove = 0;
680 QUESTION(trans, PM_TRANS_CONV_CORRUPTED_PKG, (char*)filepath,
681 NULL, NULL, &doremove);
682 if(doremove) {
683 unlink(filepath);
687 return ret;
690 int _alpm_sync_commit(pmtrans_t *trans, pmdb_t *db_local, alpm_list_t **data)
692 alpm_list_t *i, *j, *files = NULL;
693 alpm_list_t *deltas = NULL;
694 size_t numtargs, current = 0, replaces = 0;
695 int errors = 0;
696 const char *cachedir = NULL;
697 int ret = -1;
699 ALPM_LOG_FUNC;
701 ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
703 cachedir = _alpm_filecache_setup();
704 trans->state = STATE_DOWNLOADING;
706 /* Total progress - figure out the total download size if required to
707 * pass to the callback. This function is called once, and it is up to the
708 * frontend to compute incremental progress. */
709 if(handle->totaldlcb) {
710 off_t total_size = (off_t)0;
711 /* sum up the download size for each package and store total */
712 for(i = trans->add; i; i = i->next) {
713 pmpkg_t *spkg = i->data;
714 total_size += spkg->download_size;
716 handle->totaldlcb(total_size);
719 /* group sync records by repository and download */
720 for(i = handle->dbs_sync; i; i = i->next) {
721 pmdb_t *current = i->data;
723 for(j = trans->add; j; j = j->next) {
724 pmpkg_t *spkg = j->data;
726 if(spkg->origin != PKG_FROM_FILE && current == spkg->origin_data.db) {
727 const char *fname = NULL;
729 fname = alpm_pkg_get_filename(spkg);
730 ASSERT(fname != NULL, RET_ERR(PM_ERR_PKG_INVALID_NAME, -1));
731 alpm_list_t *delta_path = spkg->delta_path;
732 if(delta_path) {
733 /* using deltas */
734 alpm_list_t *dlts = NULL;
736 for(dlts = delta_path; dlts; dlts = dlts->next) {
737 pmdelta_t *d = dlts->data;
739 if(d->download_size != 0) {
740 /* add the delta filename to the download list if needed */
741 files = alpm_list_add(files, strdup(d->delta));
744 /* keep a list of all the delta files for md5sums */
745 deltas = alpm_list_add(deltas, d);
748 } else {
749 /* not using deltas */
750 if(spkg->download_size != 0) {
751 /* add the filename to the download list if needed */
752 files = alpm_list_add(files, strdup(fname));
759 if(files) {
760 EVENT(trans, PM_TRANS_EVT_RETRIEVE_START, current->treename, NULL);
761 errors = _alpm_download_files(files, current->servers, cachedir);
763 if (errors) {
764 _alpm_log(PM_LOG_WARNING, _("failed to retrieve some files from %s\n"),
765 current->treename);
766 if(pm_errno == 0) {
767 pm_errno = PM_ERR_RETRIEVE;
769 goto error;
771 FREELIST(files);
775 for(j = trans->add; j; j = j->next) {
776 pmpkg_t *pkg = j->data;
777 pkg->infolevel &= ~INFRQ_DSIZE;
778 pkg->download_size = 0;
781 /* clear out value to let callback know we are done */
782 if(handle->totaldlcb) {
783 handle->totaldlcb(0);
786 /* if we have deltas to work with */
787 if(handle->usedelta && deltas) {
788 int ret = 0;
789 errors = 0;
790 /* Check integrity of deltas */
791 EVENT(trans, PM_TRANS_EVT_DELTA_INTEGRITY_START, NULL, NULL);
793 for(i = deltas; i; i = i->next) {
794 pmdelta_t *d = alpm_list_getdata(i);
795 const char *filename = alpm_delta_get_filename(d);
796 char *filepath = _alpm_filecache_find(filename);
797 const char *md5sum = alpm_delta_get_md5sum(d);
799 if(test_md5sum(trans, filepath, md5sum) != 0) {
800 errors++;
801 *data = alpm_list_add(*data, strdup(filename));
803 FREE(filepath);
805 if(errors) {
806 pm_errno = PM_ERR_DLT_INVALID;
807 goto error;
809 EVENT(trans, PM_TRANS_EVT_DELTA_INTEGRITY_DONE, NULL, NULL);
811 /* Use the deltas to generate the packages */
812 EVENT(trans, PM_TRANS_EVT_DELTA_PATCHES_START, NULL, NULL);
813 ret = apply_deltas(trans);
814 EVENT(trans, PM_TRANS_EVT_DELTA_PATCHES_DONE, NULL, NULL);
816 if(ret) {
817 pm_errno = PM_ERR_DLT_PATCHFAILED;
818 goto error;
822 /* Check integrity of packages */
823 numtargs = alpm_list_count(trans->add);
824 EVENT(trans, PM_TRANS_EVT_INTEGRITY_START, NULL, NULL);
826 errors = 0;
828 for(i = trans->add; i; i = i->next, current++) {
829 pmpkg_t *spkg = i->data;
830 int percent = (current * 100) / numtargs;
831 if(spkg->origin == PKG_FROM_FILE) {
832 continue; /* pkg_load() has been already called, this package is valid */
834 PROGRESS(trans, PM_TRANS_PROGRESS_INTEGRITY_START, "", percent,
835 numtargs, current);
837 const char *filename = alpm_pkg_get_filename(spkg);
838 char *filepath = _alpm_filecache_find(filename);
839 const char *md5sum = alpm_pkg_get_md5sum(spkg);
840 const pmpgpsig_t *pgpsig = alpm_pkg_get_pgpsig(spkg);
842 /* check md5sum first */
843 if(test_md5sum(trans, filepath, md5sum) != 0) {
844 errors++;
845 *data = alpm_list_add(*data, strdup(filename));
846 FREE(filepath);
847 continue;
849 /* check PGP signature next */
850 pmdb_t *sdb = alpm_pkg_get_db(spkg);
852 if(sdb->pgp_verify != PM_PGP_VERIFY_NEVER) {
853 int ret = _alpm_gpgme_checksig(filepath, pgpsig);
854 if((sdb->pgp_verify == PM_PGP_VERIFY_ALWAYS && ret != 0) ||
855 (sdb->pgp_verify == PM_PGP_VERIFY_OPTIONAL && ret == 1)) {
856 errors++;
857 *data = alpm_list_add(*data, strdup(filename));
858 FREE(filepath);
859 continue;
862 /* load the package file and replace pkgcache entry with it in the target list */
863 /* TODO: alpm_pkg_get_db() will not work on this target anymore */
864 _alpm_log(PM_LOG_DEBUG, "replacing pkgcache entry with package file for target %s\n", spkg->name);
865 pmpkg_t *pkgfile;
866 if(alpm_pkg_load(filepath, 1, &pkgfile) != 0) {
867 _alpm_pkg_free(pkgfile);
868 errors++;
869 *data = alpm_list_add(*data, strdup(filename));
870 FREE(filepath);
871 continue;
873 FREE(filepath);
874 pkgfile->reason = spkg->reason; /* copy over install reason */
875 i->data = pkgfile;
876 _alpm_pkg_free_trans(spkg); /* spkg has been removed from the target list */
879 PROGRESS(trans, PM_TRANS_PROGRESS_INTEGRITY_START, "", 100,
880 numtargs, current);
881 EVENT(trans, PM_TRANS_EVT_INTEGRITY_DONE, NULL, NULL);
884 if(errors) {
885 pm_errno = PM_ERR_PKG_INVALID;
886 goto error;
889 if(trans->flags & PM_TRANS_FLAG_DOWNLOADONLY) {
890 ret = 0;
891 goto error;
894 trans->state = STATE_COMMITING;
896 replaces = alpm_list_count(trans->remove);
898 /* fileconflict check */
899 if(!(trans->flags & PM_TRANS_FLAG_FORCE)) {
900 EVENT(trans, PM_TRANS_EVT_FILECONFLICTS_START, NULL, NULL);
902 _alpm_log(PM_LOG_DEBUG, "looking for file conflicts\n");
903 alpm_list_t *conflict = _alpm_db_find_fileconflicts(db_local, trans,
904 trans->add, trans->remove);
905 if(conflict) {
906 pm_errno = PM_ERR_FILE_CONFLICTS;
907 if(data) {
908 *data = conflict;
909 } else {
910 alpm_list_free_inner(conflict, (alpm_list_fn_free)_alpm_fileconflict_free);
911 alpm_list_free(conflict);
913 goto error;
916 EVENT(trans, PM_TRANS_EVT_FILECONFLICTS_DONE, NULL, NULL);
919 /* check available disk space */
920 if(handle->checkspace) {
921 EVENT(trans, PM_TRANS_EVT_DISKSPACE_START, NULL, NULL);
923 _alpm_log(PM_LOG_DEBUG, "checking available disk space\n");
924 if(_alpm_check_diskspace(trans, handle->db_local) == -1) {
925 _alpm_log(PM_LOG_ERROR, "%s\n", _("not enough free disk space"));
926 goto error;
929 EVENT(trans, PM_TRANS_EVT_DISKSPACE_DONE, NULL, NULL);
932 /* remove conflicting and to-be-replaced packages */
933 if(replaces) {
934 _alpm_log(PM_LOG_DEBUG, "removing conflicting and to-be-replaced packages\n");
935 /* we want the frontend to be aware of commit details */
936 if(_alpm_remove_packages(trans, handle->db_local) == -1) {
937 _alpm_log(PM_LOG_ERROR, _("could not commit removal transaction\n"));
938 goto error;
942 /* install targets */
943 _alpm_log(PM_LOG_DEBUG, "installing packages\n");
944 if(_alpm_upgrade_packages(trans, handle->db_local) == -1) {
945 _alpm_log(PM_LOG_ERROR, _("could not commit transaction\n"));
946 goto error;
948 ret = 0;
950 error:
951 FREELIST(files);
952 alpm_list_free(deltas);
953 return ret;
956 /* vim: set ts=2 sw=2 noet: */