Put comments on their own line
[pacman-ng.git] / lib / libalpm / sync.c
blob9f906d39b3e50f4756dac6afc0d667a198b1f138
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) {
121 /* 1. literal was found in sdb */
122 int cmp = _alpm_pkg_compare_versions(spkg, lpkg);
123 if(cmp > 0) {
124 _alpm_log(PM_LOG_DEBUG, "new version of '%s' found (%s => %s)\n",
125 lpkg->name, lpkg->version, spkg->version);
126 /* check IgnorePkg/IgnoreGroup */
127 if(_alpm_pkg_should_ignore(spkg) || _alpm_pkg_should_ignore(lpkg)) {
128 _alpm_log(PM_LOG_WARNING, _("%s: ignoring package upgrade (%s => %s)\n"),
129 lpkg->name, lpkg->version, spkg->version);
130 } else {
131 _alpm_log(PM_LOG_DEBUG, "adding package %s-%s to the transaction targets\n",
132 spkg->name, spkg->version);
133 trans->add = alpm_list_add(trans->add, spkg);
135 } else if(cmp < 0) {
136 if(enable_downgrade) {
137 /* check IgnorePkg/IgnoreGroup */
138 if(_alpm_pkg_should_ignore(spkg) || _alpm_pkg_should_ignore(lpkg)) {
139 _alpm_log(PM_LOG_WARNING, _("%s: ignoring package downgrade (%s => %s)\n"),
140 lpkg->name, lpkg->version, spkg->version);
141 } else {
142 _alpm_log(PM_LOG_WARNING, _("%s: downgrading from version %s to version %s\n"),
143 lpkg->name, lpkg->version, spkg->version);
144 trans->add = alpm_list_add(trans->add, spkg);
146 } else {
147 _alpm_log(PM_LOG_WARNING, _("%s: local (%s) is newer than %s (%s)\n"),
148 lpkg->name, lpkg->version, sdb->treename, spkg->version);
151 /* jump to next local package */
152 break;
153 } else {
154 /* 2. search for replacers in sdb */
155 int found = 0;
156 for(k = _alpm_db_get_pkgcache(sdb); k; k = k->next) {
157 spkg = k->data;
158 if(alpm_list_find_str(alpm_pkg_get_replaces(spkg), lpkg->name)) {
159 found = 1;
160 /* check IgnorePkg/IgnoreGroup */
161 if(_alpm_pkg_should_ignore(spkg) || _alpm_pkg_should_ignore(lpkg)) {
162 _alpm_log(PM_LOG_WARNING, _("ignoring package replacement (%s-%s => %s-%s)\n"),
163 lpkg->name, lpkg->version, spkg->name, spkg->version);
164 continue;
167 int doreplace = 0;
168 QUESTION(trans, PM_TRANS_CONV_REPLACE_PKG, lpkg, spkg, sdb->treename, &doreplace);
169 if(!doreplace) {
170 continue;
173 /* If spkg is already in the target list, we append lpkg to spkg's
174 * removes list */
175 pmpkg_t *tpkg = _alpm_pkg_find(trans->add, spkg->name);
176 if(tpkg) {
177 /* sanity check, multiple repos can contain spkg->name */
178 if(tpkg->origin_data.db != sdb) {
179 _alpm_log(PM_LOG_WARNING, _("cannot replace %s by %s\n"),
180 lpkg->name, spkg->name);
181 continue;
183 _alpm_log(PM_LOG_DEBUG, "appending %s to the removes list of %s\n",
184 lpkg->name, tpkg->name);
185 tpkg->removes = alpm_list_add(tpkg->removes, lpkg);
186 /* check the to-be-replaced package's reason field */
187 if(alpm_pkg_get_reason(lpkg) == PM_PKG_REASON_EXPLICIT) {
188 tpkg->reason = PM_PKG_REASON_EXPLICIT;
190 } else {
191 /* add spkg to the target list */
192 /* copy over reason */
193 spkg->reason = alpm_pkg_get_reason(lpkg);
194 spkg->removes = alpm_list_add(NULL, lpkg);
195 _alpm_log(PM_LOG_DEBUG, "adding package %s-%s to the transaction targets\n",
196 spkg->name, spkg->version);
197 trans->add = alpm_list_add(trans->add, spkg);
201 if(found) {
202 break; /* jump to next local package */
208 return 0;
211 /** Find group members across a list of databases.
212 * If a member exists in several databases, only the first database is used.
213 * IgnorePkg is also handled.
214 * @param dbs the list of pmdb_t *
215 * @pram name the name of the group
216 * @return the list of pmpkg_t * (caller is responsible for alpm_list_free)
218 alpm_list_t SYMEXPORT *alpm_find_grp_pkgs(alpm_list_t *dbs,
219 const char *name)
221 alpm_list_t *i, *j, *pkgs = NULL, *ignorelist = NULL;
223 for(i = dbs; i; i = i->next) {
224 pmdb_t *db = i->data;
225 pmgrp_t *grp = alpm_db_readgrp(db, name);
227 if(!grp)
228 continue;
230 for(j = alpm_grp_get_pkgs(grp); j; j = j->next) {
231 pmpkg_t *pkg = j->data;
233 if(_alpm_pkg_find(ignorelist, alpm_pkg_get_name(pkg))) {
234 continue;
236 if(_alpm_pkg_should_ignore(pkg)) {
237 ignorelist = alpm_list_add(ignorelist, pkg);
238 int install = 0;
239 QUESTION(handle->trans, PM_TRANS_CONV_INSTALL_IGNOREPKG, pkg,
240 NULL, NULL, &install);
241 if(!install)
242 continue;
244 if(!_alpm_pkg_find(pkgs, alpm_pkg_get_name(pkg))) {
245 pkgs = alpm_list_add(pkgs, pkg);
249 alpm_list_free(ignorelist);
250 return pkgs;
253 /** Compute the size of the files that will be downloaded to install a
254 * package.
255 * @param newpkg the new package to upgrade to
257 static int compute_download_size(pmpkg_t *newpkg)
259 const char *fname;
260 char *fpath;
261 off_t size = 0;
263 if(newpkg->origin != PKG_FROM_SYNCDB) {
264 newpkg->infolevel |= INFRQ_DSIZE;
265 newpkg->download_size = 0;
266 return 0;
269 fname = alpm_pkg_get_filename(newpkg);
270 ASSERT(fname != NULL, RET_ERR(PM_ERR_PKG_INVALID_NAME, -1));
271 fpath = _alpm_filecache_find(fname);
273 if(fpath) {
274 FREE(fpath);
275 size = 0;
276 } else if(handle->usedelta) {
277 off_t dltsize;
278 off_t pkgsize = alpm_pkg_get_size(newpkg);
280 dltsize = _alpm_shortest_delta_path(
281 alpm_pkg_get_deltas(newpkg),
282 alpm_pkg_get_filename(newpkg),
283 &newpkg->delta_path);
285 if(newpkg->delta_path && (dltsize < pkgsize * MAX_DELTA_RATIO)) {
286 _alpm_log(PM_LOG_DEBUG, "using delta size\n");
287 size = dltsize;
288 } else {
289 _alpm_log(PM_LOG_DEBUG, "using package size\n");
290 size = alpm_pkg_get_size(newpkg);
291 alpm_list_free(newpkg->delta_path);
292 newpkg->delta_path = NULL;
294 } else {
295 size = alpm_pkg_get_size(newpkg);
298 _alpm_log(PM_LOG_DEBUG, "setting download size %jd for pkg %s\n",
299 (intmax_t)size, alpm_pkg_get_name(newpkg));
301 newpkg->infolevel |= INFRQ_DSIZE;
302 newpkg->download_size = size;
303 return 0;
306 int _alpm_sync_prepare(pmtrans_t *trans, pmdb_t *db_local, alpm_list_t *dbs_sync, alpm_list_t **data)
308 alpm_list_t *deps = NULL;
309 alpm_list_t *unresolvable = NULL;
310 alpm_list_t *i, *j;
311 alpm_list_t *remove = NULL;
312 int ret = 0;
314 ALPM_LOG_FUNC;
316 ASSERT(db_local != NULL, RET_ERR(PM_ERR_DB_NULL, -1));
317 ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
319 if(data) {
320 *data = NULL;
323 if(!(trans->flags & PM_TRANS_FLAG_NODEPS)) {
324 alpm_list_t *resolved = NULL; /* target list after resolvedeps */
326 /* Build up list by repeatedly resolving each transaction package */
327 /* Resolve targets dependencies */
328 EVENT(trans, PM_TRANS_EVT_RESOLVEDEPS_START, NULL, NULL);
329 _alpm_log(PM_LOG_DEBUG, "resolving target's dependencies\n");
331 /* build remove list for resolvedeps */
332 for(i = trans->add; i; i = i->next) {
333 pmpkg_t *spkg = i->data;
334 for(j = spkg->removes; j; j = j->next) {
335 remove = alpm_list_add(remove, j->data);
339 /* Compute the fake local database for resolvedeps (partial fix for the
340 * phonon/qt issue) */
341 alpm_list_t *localpkgs = alpm_list_diff(_alpm_db_get_pkgcache(db_local),
342 trans->add, _alpm_pkg_cmp);
344 /* Resolve packages in the transaction one at a time, in addition
345 building up a list of packages which could not be resolved. */
346 for(i = trans->add; i; i = i->next) {
347 pmpkg_t *pkg = i->data;
348 if(_alpm_resolvedeps(localpkgs, dbs_sync, pkg, trans->add,
349 &resolved, remove, data) == -1) {
350 unresolvable = alpm_list_add(unresolvable, pkg);
352 /* Else, [resolved] now additionally contains [pkg] and all of its
353 dependencies not already on the list */
355 alpm_list_free(localpkgs);
357 /* If there were unresolvable top-level packages, prompt the user to
358 see if they'd like to ignore them rather than failing the sync */
359 if(unresolvable != NULL) {
360 int remove_unresolvable = 0;
361 QUESTION(handle->trans, PM_TRANS_CONV_REMOVE_PKGS, unresolvable,
362 NULL, NULL, &remove_unresolvable);
363 if (remove_unresolvable) {
364 /* User wants to remove the unresolvable packages from the
365 transaction. The packages will be removed from the actual
366 transaction when the transaction packages are replaced with a
367 dependency-reordered list below */
368 pm_errno = 0; /* pm_errno was set by resolvedeps */
369 if(data) {
370 alpm_list_free_inner(*data, (alpm_list_fn_free)_alpm_depmiss_free);
371 alpm_list_free(*data);
372 *data = NULL;
374 } else {
375 /* pm_errno is set by resolvedeps */
376 alpm_list_free(resolved);
377 ret = -1;
378 goto cleanup;
382 /* Set DEPEND reason for pulled packages */
383 for(i = resolved; i; i = i->next) {
384 pmpkg_t *pkg = i->data;
385 if(!_alpm_pkg_find(trans->add, pkg->name)) {
386 pkg->reason = PM_PKG_REASON_DEPEND;
390 /* Unresolvable packages will be removed from the target list, so
391 we free the transaction specific fields */
392 alpm_list_free_inner(unresolvable, (alpm_list_fn_free)_alpm_pkg_free_trans);
394 /* re-order w.r.t. dependencies */
395 alpm_list_free(trans->add);
396 trans->add = _alpm_sortbydeps(resolved, 0);
397 alpm_list_free(resolved);
399 EVENT(trans, PM_TRANS_EVT_RESOLVEDEPS_DONE, NULL, NULL);
402 if(!(trans->flags & PM_TRANS_FLAG_NOCONFLICTS)) {
403 /* check for inter-conflicts and whatnot */
404 EVENT(trans, PM_TRANS_EVT_INTERCONFLICTS_START, NULL, NULL);
406 _alpm_log(PM_LOG_DEBUG, "looking for conflicts\n");
408 /* 1. check for conflicts in the target list */
409 _alpm_log(PM_LOG_DEBUG, "check targets vs targets\n");
410 deps = _alpm_innerconflicts(trans->add);
412 for(i = deps; i; i = i->next) {
413 pmconflict_t *conflict = i->data;
414 pmpkg_t *rsync, *sync, *sync1, *sync2;
416 /* have we already removed one of the conflicting targets? */
417 sync1 = _alpm_pkg_find(trans->add, conflict->package1);
418 sync2 = _alpm_pkg_find(trans->add, conflict->package2);
419 if(!sync1 || !sync2) {
420 continue;
423 _alpm_log(PM_LOG_DEBUG, "conflicting packages in the sync list: '%s' <-> '%s'\n",
424 conflict->package1, conflict->package2);
426 /* if sync1 provides sync2, we remove sync2 from the targets, and vice versa */
427 pmdepend_t *dep1 = _alpm_splitdep(conflict->package1);
428 pmdepend_t *dep2 = _alpm_splitdep(conflict->package2);
429 if(_alpm_depcmp(sync1, dep2)) {
430 rsync = sync2;
431 sync = sync1;
432 } else if(_alpm_depcmp(sync2, dep1)) {
433 rsync = sync1;
434 sync = sync2;
435 } else {
436 _alpm_log(PM_LOG_ERROR, _("unresolvable package conflicts detected\n"));
437 pm_errno = PM_ERR_CONFLICTING_DEPS;
438 ret = -1;
439 if(data) {
440 pmconflict_t *newconflict = _alpm_conflict_dup(conflict);
441 if(newconflict) {
442 *data = alpm_list_add(*data, newconflict);
445 alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_conflict_free);
446 alpm_list_free(deps);
447 _alpm_dep_free(dep1);
448 _alpm_dep_free(dep2);
449 goto cleanup;
451 _alpm_dep_free(dep1);
452 _alpm_dep_free(dep2);
454 /* Prints warning */
455 _alpm_log(PM_LOG_WARNING,
456 _("removing '%s' from target list because it conflicts with '%s'\n"),
457 rsync->name, sync->name);
458 trans->add = alpm_list_remove(trans->add, rsync, _alpm_pkg_cmp, NULL);
459 _alpm_pkg_free_trans(rsync); /* rsync is not transaction target anymore */
460 continue;
463 alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_conflict_free);
464 alpm_list_free(deps);
465 deps = NULL;
467 /* 2. we check for target vs db conflicts (and resolve)*/
468 _alpm_log(PM_LOG_DEBUG, "check targets vs db and db vs targets\n");
469 deps = _alpm_outerconflicts(db_local, trans->add);
471 for(i = deps; i; i = i->next) {
472 pmconflict_t *conflict = i->data;
474 /* if conflict->package2 (the local package) is not elected for removal,
475 we ask the user */
476 int found = 0;
477 for(j = trans->add; j && !found; j = j->next) {
478 pmpkg_t *spkg = j->data;
479 if(_alpm_pkg_find(spkg->removes, conflict->package2)) {
480 found = 1;
483 if(found) {
484 continue;
487 _alpm_log(PM_LOG_DEBUG, "package '%s' conflicts with '%s'\n",
488 conflict->package1, conflict->package2);
490 pmpkg_t *sync = _alpm_pkg_find(trans->add, conflict->package1);
491 pmpkg_t *local = _alpm_db_get_pkgfromcache(db_local, conflict->package2);
492 int doremove = 0;
493 QUESTION(trans, PM_TRANS_CONV_CONFLICT_PKG, conflict->package1,
494 conflict->package2, conflict->reason, &doremove);
495 if(doremove) {
496 /* append to the removes list */
497 _alpm_log(PM_LOG_DEBUG, "electing '%s' for removal\n", conflict->package2);
498 sync->removes = alpm_list_add(sync->removes, local);
499 } else { /* abort */
500 _alpm_log(PM_LOG_ERROR, _("unresolvable package conflicts detected\n"));
501 pm_errno = PM_ERR_CONFLICTING_DEPS;
502 ret = -1;
503 if(data) {
504 pmconflict_t *newconflict = _alpm_conflict_dup(conflict);
505 if(newconflict) {
506 *data = alpm_list_add(*data, newconflict);
509 alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_conflict_free);
510 alpm_list_free(deps);
511 goto cleanup;
514 EVENT(trans, PM_TRANS_EVT_INTERCONFLICTS_DONE, NULL, NULL);
515 alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_conflict_free);
516 alpm_list_free(deps);
519 /* Build trans->remove list */
520 for(i = trans->add; i; i = i->next) {
521 pmpkg_t *spkg = i->data;
522 for(j = spkg->removes; j; j = j->next) {
523 pmpkg_t *rpkg = j->data;
524 if(!_alpm_pkg_find(trans->remove, rpkg->name)) {
525 _alpm_log(PM_LOG_DEBUG, "adding '%s' to remove list\n", rpkg->name);
526 trans->remove = alpm_list_add(trans->remove, _alpm_pkg_dup(rpkg));
531 if(!(trans->flags & PM_TRANS_FLAG_NODEPS)) {
532 _alpm_log(PM_LOG_DEBUG, "checking dependencies\n");
533 deps = alpm_checkdeps(_alpm_db_get_pkgcache(db_local), 1, trans->remove, trans->add);
534 if(deps) {
535 pm_errno = PM_ERR_UNSATISFIED_DEPS;
536 ret = -1;
537 if(data) {
538 *data = deps;
539 } else {
540 alpm_list_free_inner(deps, (alpm_list_fn_free)_alpm_depmiss_free);
541 alpm_list_free(deps);
543 goto cleanup;
546 for(i = trans->add; i; i = i->next) {
547 /* update download size field */
548 pmpkg_t *spkg = i->data;
549 if(compute_download_size(spkg) != 0) {
550 ret = -1;
551 goto cleanup;
555 cleanup:
556 alpm_list_free(unresolvable);
557 alpm_list_free(remove);
559 return ret;
562 /** Returns the size of the files that will be downloaded to install a
563 * package.
564 * @param newpkg the new package to upgrade to
565 * @return the size of the download
567 off_t SYMEXPORT alpm_pkg_download_size(pmpkg_t *newpkg)
569 if(!(newpkg->infolevel & INFRQ_DSIZE)) {
570 compute_download_size(newpkg);
572 return newpkg->download_size;
575 static int endswith(const char *filename, const char *extension)
577 const char *s = filename + strlen(filename) - strlen(extension);
578 return strcmp(s, extension) == 0;
581 /** Applies delta files to create an upgraded package file.
583 * All intermediate files are deleted, leaving only the starting and
584 * ending package files.
586 * @param trans the transaction
588 * @return 0 if all delta files were able to be applied, 1 otherwise.
590 static int apply_deltas(pmtrans_t *trans)
592 alpm_list_t *i;
593 int ret = 0;
594 const char *cachedir = _alpm_filecache_setup();
596 for(i = trans->add; i; i = i->next) {
597 pmpkg_t *spkg = i->data;
598 alpm_list_t *delta_path = spkg->delta_path;
599 alpm_list_t *dlts = NULL;
601 if(!delta_path) {
602 continue;
605 for(dlts = delta_path; dlts; dlts = dlts->next) {
606 pmdelta_t *d = dlts->data;
607 char *delta, *from, *to;
608 char command[PATH_MAX];
609 size_t len = 0;
611 delta = _alpm_filecache_find(d->delta);
612 /* the initial package might be in a different cachedir */
613 if(dlts == delta_path) {
614 from = _alpm_filecache_find(d->from);
615 } else {
616 /* len = cachedir len + from len + '/' + null */
617 len = strlen(cachedir) + strlen(d->from) + 2;
618 CALLOC(from, len, sizeof(char), RET_ERR(PM_ERR_MEMORY, 1));
619 snprintf(from, len, "%s/%s", cachedir, d->from);
621 len = strlen(cachedir) + strlen(d->to) + 2;
622 CALLOC(to, len, sizeof(char), RET_ERR(PM_ERR_MEMORY, 1));
623 snprintf(to, len, "%s/%s", cachedir, d->to);
625 /* build the patch command */
626 if(endswith(to, ".gz")) {
627 /* special handling for gzip : we disable timestamp with -n option */
628 snprintf(command, PATH_MAX, "xdelta3 -d -q -R -c -s %s %s | gzip -n > %s", from, delta, to);
629 } else {
630 snprintf(command, PATH_MAX, "xdelta3 -d -q -s %s %s %s", from, delta, to);
633 _alpm_log(PM_LOG_DEBUG, "command: %s\n", command);
635 EVENT(trans, PM_TRANS_EVT_DELTA_PATCH_START, d->to, d->delta);
637 int retval = system(command);
638 if(retval == 0) {
639 EVENT(trans, PM_TRANS_EVT_DELTA_PATCH_DONE, NULL, NULL);
641 /* delete the delta file */
642 unlink(delta);
644 /* Delete the 'from' package but only if it is an intermediate
645 * package. The starting 'from' package should be kept, just
646 * as if deltas were not used. */
647 if(dlts != delta_path) {
648 unlink(from);
651 FREE(from);
652 FREE(to);
653 FREE(delta);
655 if(retval != 0) {
656 /* one delta failed for this package, cancel the remaining ones */
657 EVENT(trans, PM_TRANS_EVT_DELTA_PATCH_FAILED, NULL, NULL);
658 ret = 1;
659 break;
664 return ret;
667 /** Compares the md5sum of a file to the expected value.
669 * If the md5sum does not match, the user is asked whether the file
670 * should be deleted.
672 * @param trans the transaction
673 * @param filename the absolute path of the file to test
674 * @param md5sum the expected md5sum of the file
676 * @return 0 if the md5sum matched, 1 if not, -1 in case of errors
678 static int test_md5sum(pmtrans_t *trans, const char *filepath,
679 const char *md5sum)
681 int ret = _alpm_test_md5sum(filepath, md5sum);
682 if(ret == 1) {
683 int doremove = 0;
684 QUESTION(trans, PM_TRANS_CONV_CORRUPTED_PKG, (char*)filepath,
685 NULL, NULL, &doremove);
686 if(doremove) {
687 unlink(filepath);
691 return ret;
694 int _alpm_sync_commit(pmtrans_t *trans, pmdb_t *db_local, alpm_list_t **data)
696 alpm_list_t *i, *j, *files = NULL;
697 alpm_list_t *deltas = NULL;
698 size_t numtargs, current = 0, replaces = 0;
699 int errors = 0;
700 const char *cachedir = NULL;
701 int ret = -1;
703 ALPM_LOG_FUNC;
705 ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
707 cachedir = _alpm_filecache_setup();
708 trans->state = STATE_DOWNLOADING;
710 /* Total progress - figure out the total download size if required to
711 * pass to the callback. This function is called once, and it is up to the
712 * frontend to compute incremental progress. */
713 if(handle->totaldlcb) {
714 off_t total_size = (off_t)0;
715 /* sum up the download size for each package and store total */
716 for(i = trans->add; i; i = i->next) {
717 pmpkg_t *spkg = i->data;
718 total_size += spkg->download_size;
720 handle->totaldlcb(total_size);
723 /* group sync records by repository and download */
724 for(i = handle->dbs_sync; i; i = i->next) {
725 pmdb_t *current = i->data;
727 for(j = trans->add; j; j = j->next) {
728 pmpkg_t *spkg = j->data;
730 if(spkg->origin != PKG_FROM_FILE && current == spkg->origin_data.db) {
731 const char *fname = NULL;
733 fname = alpm_pkg_get_filename(spkg);
734 ASSERT(fname != NULL, RET_ERR(PM_ERR_PKG_INVALID_NAME, -1));
735 alpm_list_t *delta_path = spkg->delta_path;
736 if(delta_path) {
737 /* using deltas */
738 alpm_list_t *dlts = NULL;
740 for(dlts = delta_path; dlts; dlts = dlts->next) {
741 pmdelta_t *d = dlts->data;
743 if(d->download_size != 0) {
744 /* add the delta filename to the download list if needed */
745 files = alpm_list_add(files, strdup(d->delta));
748 /* keep a list of all the delta files for md5sums */
749 deltas = alpm_list_add(deltas, d);
752 } else {
753 /* not using deltas */
754 if(spkg->download_size != 0) {
755 /* add the filename to the download list if needed */
756 files = alpm_list_add(files, strdup(fname));
763 if(files) {
764 EVENT(trans, PM_TRANS_EVT_RETRIEVE_START, current->treename, NULL);
765 errors = _alpm_download_files(files, current->servers, cachedir);
767 if (errors) {
768 _alpm_log(PM_LOG_WARNING, _("failed to retrieve some files from %s\n"),
769 current->treename);
770 if(pm_errno == 0) {
771 pm_errno = PM_ERR_RETRIEVE;
773 goto error;
775 FREELIST(files);
779 for(j = trans->add; j; j = j->next) {
780 pmpkg_t *pkg = j->data;
781 pkg->infolevel &= ~INFRQ_DSIZE;
782 pkg->download_size = 0;
785 /* clear out value to let callback know we are done */
786 if(handle->totaldlcb) {
787 handle->totaldlcb(0);
790 /* if we have deltas to work with */
791 if(handle->usedelta && deltas) {
792 int ret = 0;
793 errors = 0;
794 /* Check integrity of deltas */
795 EVENT(trans, PM_TRANS_EVT_DELTA_INTEGRITY_START, NULL, NULL);
797 for(i = deltas; i; i = i->next) {
798 pmdelta_t *d = alpm_list_getdata(i);
799 const char *filename = alpm_delta_get_filename(d);
800 char *filepath = _alpm_filecache_find(filename);
801 const char *md5sum = alpm_delta_get_md5sum(d);
803 if(test_md5sum(trans, filepath, md5sum) != 0) {
804 errors++;
805 *data = alpm_list_add(*data, strdup(filename));
807 FREE(filepath);
809 if(errors) {
810 pm_errno = PM_ERR_DLT_INVALID;
811 goto error;
813 EVENT(trans, PM_TRANS_EVT_DELTA_INTEGRITY_DONE, NULL, NULL);
815 /* Use the deltas to generate the packages */
816 EVENT(trans, PM_TRANS_EVT_DELTA_PATCHES_START, NULL, NULL);
817 ret = apply_deltas(trans);
818 EVENT(trans, PM_TRANS_EVT_DELTA_PATCHES_DONE, NULL, NULL);
820 if(ret) {
821 pm_errno = PM_ERR_DLT_PATCHFAILED;
822 goto error;
826 /* Check integrity of packages */
827 numtargs = alpm_list_count(trans->add);
828 EVENT(trans, PM_TRANS_EVT_INTEGRITY_START, NULL, NULL);
830 errors = 0;
832 for(i = trans->add; i; i = i->next, current++) {
833 pmpkg_t *spkg = i->data;
834 int percent = (current * 100) / numtargs;
835 if(spkg->origin == PKG_FROM_FILE) {
836 continue; /* pkg_load() has been already called, this package is valid */
838 PROGRESS(trans, PM_TRANS_PROGRESS_INTEGRITY_START, "", percent,
839 numtargs, current);
841 const char *filename = alpm_pkg_get_filename(spkg);
842 char *filepath = _alpm_filecache_find(filename);
843 const char *md5sum = alpm_pkg_get_md5sum(spkg);
844 const pmpgpsig_t *pgpsig = alpm_pkg_get_pgpsig(spkg);
846 /* check md5sum first */
847 if(test_md5sum(trans, filepath, md5sum) != 0) {
848 errors++;
849 *data = alpm_list_add(*data, strdup(filename));
850 FREE(filepath);
851 continue;
853 /* check PGP signature next */
854 pmdb_t *sdb = alpm_pkg_get_db(spkg);
856 if(sdb->pgp_verify != PM_PGP_VERIFY_NEVER) {
857 int ret = _alpm_gpgme_checksig(filepath, pgpsig);
858 if((sdb->pgp_verify == PM_PGP_VERIFY_ALWAYS && ret != 0) ||
859 (sdb->pgp_verify == PM_PGP_VERIFY_OPTIONAL && ret == 1)) {
860 errors++;
861 *data = alpm_list_add(*data, strdup(filename));
862 FREE(filepath);
863 continue;
866 /* load the package file and replace pkgcache entry with it in the target list */
867 /* TODO: alpm_pkg_get_db() will not work on this target anymore */
868 _alpm_log(PM_LOG_DEBUG, "replacing pkgcache entry with package file for target %s\n", spkg->name);
869 pmpkg_t *pkgfile;
870 if(alpm_pkg_load(filepath, 1, &pkgfile) != 0) {
871 _alpm_pkg_free(pkgfile);
872 errors++;
873 *data = alpm_list_add(*data, strdup(filename));
874 FREE(filepath);
875 continue;
877 FREE(filepath);
878 pkgfile->reason = spkg->reason; /* copy over install reason */
879 i->data = pkgfile;
880 _alpm_pkg_free_trans(spkg); /* spkg has been removed from the target list */
883 PROGRESS(trans, PM_TRANS_PROGRESS_INTEGRITY_START, "", 100,
884 numtargs, current);
885 EVENT(trans, PM_TRANS_EVT_INTEGRITY_DONE, NULL, NULL);
888 if(errors) {
889 pm_errno = PM_ERR_PKG_INVALID;
890 goto error;
893 if(trans->flags & PM_TRANS_FLAG_DOWNLOADONLY) {
894 ret = 0;
895 goto error;
898 trans->state = STATE_COMMITING;
900 replaces = alpm_list_count(trans->remove);
902 /* fileconflict check */
903 if(!(trans->flags & PM_TRANS_FLAG_FORCE)) {
904 EVENT(trans, PM_TRANS_EVT_FILECONFLICTS_START, NULL, NULL);
906 _alpm_log(PM_LOG_DEBUG, "looking for file conflicts\n");
907 alpm_list_t *conflict = _alpm_db_find_fileconflicts(db_local, trans,
908 trans->add, trans->remove);
909 if(conflict) {
910 pm_errno = PM_ERR_FILE_CONFLICTS;
911 if(data) {
912 *data = conflict;
913 } else {
914 alpm_list_free_inner(conflict, (alpm_list_fn_free)_alpm_fileconflict_free);
915 alpm_list_free(conflict);
917 goto error;
920 EVENT(trans, PM_TRANS_EVT_FILECONFLICTS_DONE, NULL, NULL);
923 /* check available disk space */
924 if(handle->checkspace) {
925 EVENT(trans, PM_TRANS_EVT_DISKSPACE_START, NULL, NULL);
927 _alpm_log(PM_LOG_DEBUG, "checking available disk space\n");
928 if(_alpm_check_diskspace(trans, handle->db_local) == -1) {
929 _alpm_log(PM_LOG_ERROR, "%s\n", _("not enough free disk space"));
930 goto error;
933 EVENT(trans, PM_TRANS_EVT_DISKSPACE_DONE, NULL, NULL);
936 /* remove conflicting and to-be-replaced packages */
937 if(replaces) {
938 _alpm_log(PM_LOG_DEBUG, "removing conflicting and to-be-replaced packages\n");
939 /* we want the frontend to be aware of commit details */
940 if(_alpm_remove_packages(trans, handle->db_local) == -1) {
941 _alpm_log(PM_LOG_ERROR, _("could not commit removal transaction\n"));
942 goto error;
946 /* install targets */
947 _alpm_log(PM_LOG_DEBUG, "installing packages\n");
948 if(_alpm_upgrade_packages(trans, handle->db_local) == -1) {
949 _alpm_log(PM_LOG_ERROR, _("could not commit transaction\n"));
950 goto error;
952 ret = 0;
954 error:
955 FREELIST(files);
956 alpm_list_free(deltas);
957 return ret;
960 /* vim: set ts=2 sw=2 noet: */