sync.c 35.8 KB
Newer Older
Judd Vinet's avatar
Judd Vinet committed
1
2
/*
 *  sync.c
Dan McGee's avatar
Dan McGee committed
3
 *
Allan McRae's avatar
Allan McRae committed
4
 *  Copyright (c) 2006-2019 Pacman Development Team <pacman-dev@archlinux.org>
5
 *  Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
6
7
8
 *  Copyright (c) 2005 by Aurelien Foret <orelien@chez.com>
 *  Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu>
 *  Copyright (c) 2005, 2006 by Miklos Vajna <vmiklos@frugalware.org>
Dan McGee's avatar
Dan McGee committed
9
 *
Judd Vinet's avatar
Judd Vinet committed
10
11
12
13
14
15
16
17
18
19
20
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  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/>.
Judd Vinet's avatar
Judd Vinet committed
22
23
 */

Dan McGee's avatar
Dan McGee committed
24
#include <sys/types.h> /* off_t */
Judd Vinet's avatar
Judd Vinet committed
25
26
27
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
28
#include <stdint.h> /* intmax_t */
Dan McGee's avatar
Dan McGee committed
29
#include <unistd.h>
Allan McRae's avatar
Allan McRae committed
30
#include <limits.h>
31
32
33
34

/* libalpm */
#include "sync.h"
#include "alpm_list.h"
Judd Vinet's avatar
Judd Vinet committed
35
36
37
38
#include "log.h"
#include "package.h"
#include "db.h"
#include "deps.h"
39
#include "conflict.h"
Judd Vinet's avatar
Judd Vinet committed
40
#include "trans.h"
41
#include "add.h"
42
#include "util.h"
Judd Vinet's avatar
Judd Vinet committed
43
#include "handle.h"
44
#include "alpm.h"
Dan McGee's avatar
Dan McGee committed
45
#include "dload.h"
Xavier Chantry's avatar
Xavier Chantry committed
46
#include "remove.h"
47
#include "diskspace.h"
Dan McGee's avatar
Dan McGee committed
48
#include "signing.h"
Judd Vinet's avatar
Judd Vinet committed
49

50
51
52
53
54
55
struct keyinfo_t {
       char* uid;
       char* keyid;
};


Nagy Gabor's avatar
Nagy Gabor committed
56
57
58
/** Check for new version of pkg in sync repos
 * (only the first occurrence is considered in sync)
 */
59
alpm_pkg_t SYMEXPORT *alpm_sync_get_new_version(alpm_pkg_t *pkg, alpm_list_t *dbs_sync)
Nagy Gabor's avatar
Nagy Gabor committed
60
61
{
	alpm_list_t *i;
Allan McRae's avatar
Allan McRae committed
62
	alpm_pkg_t *spkg = NULL;
Nagy Gabor's avatar
Nagy Gabor committed
63

64
	ASSERT(pkg != NULL, return NULL);
Ivy Foster's avatar
Ivy Foster committed
65
	pkg->handle->pm_errno = ALPM_ERR_OK;
66

Nagy Gabor's avatar
Nagy Gabor committed
67
	for(i = dbs_sync; !spkg && i; i = i->next) {
68
69
		alpm_db_t *db = i->data;
		spkg = _alpm_db_get_pkgfromcache(db, pkg->name);
Nagy Gabor's avatar
Nagy Gabor committed
70
71
72
	}

	if(spkg == NULL) {
73
		_alpm_log(pkg->handle, ALPM_LOG_DEBUG, "'%s' not found in sync db => no upgrade\n",
74
				pkg->name);
75
		return NULL;
Nagy Gabor's avatar
Nagy Gabor committed
76
77
78
	}

	/* compare versions and see if spkg is an upgrade */
Nagy Gabor's avatar
Nagy Gabor committed
79
	if(_alpm_pkg_compare_versions(spkg, pkg) > 0) {
80
		_alpm_log(pkg->handle, ALPM_LOG_DEBUG, "new version of '%s' found (%s => %s)\n",
81
					pkg->name, pkg->version, spkg->version);
82
		return spkg;
Nagy Gabor's avatar
Nagy Gabor committed
83
	}
Nagy Gabor's avatar
Nagy Gabor committed
84
	/* spkg is not an upgrade */
85
	return NULL;
Nagy Gabor's avatar
Nagy Gabor committed
86
87
}

88
89
90
91
92
93
94
95
96
static int check_literal(alpm_handle_t *handle, alpm_pkg_t *lpkg,
		alpm_pkg_t *spkg, int enable_downgrade)
{
	/* 1. literal was found in sdb */
	int cmp = _alpm_pkg_compare_versions(spkg, lpkg);
	if(cmp > 0) {
		_alpm_log(handle, ALPM_LOG_DEBUG, "new version of '%s' found (%s => %s)\n",
				lpkg->name, lpkg->version, spkg->version);
		/* check IgnorePkg/IgnoreGroup */
Allan McRae's avatar
Allan McRae committed
97
98
		if(alpm_pkg_should_ignore(handle, spkg)
				|| alpm_pkg_should_ignore(handle, lpkg)) {
99
100
101
102
103
104
105
106
107
108
			_alpm_log(handle, ALPM_LOG_WARNING, _("%s: ignoring package upgrade (%s => %s)\n"),
					lpkg->name, lpkg->version, spkg->version);
		} else {
			_alpm_log(handle, ALPM_LOG_DEBUG, "adding package %s-%s to the transaction targets\n",
					spkg->name, spkg->version);
			return 1;
		}
	} else if(cmp < 0) {
		if(enable_downgrade) {
			/* check IgnorePkg/IgnoreGroup */
Allan McRae's avatar
Allan McRae committed
109
110
			if(alpm_pkg_should_ignore(handle, spkg)
					|| alpm_pkg_should_ignore(handle, lpkg)) {
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
				_alpm_log(handle, ALPM_LOG_WARNING, _("%s: ignoring package downgrade (%s => %s)\n"),
						lpkg->name, lpkg->version, spkg->version);
			} else {
				_alpm_log(handle, ALPM_LOG_WARNING, _("%s: downgrading from version %s to version %s\n"),
						lpkg->name, lpkg->version, spkg->version);
				return 1;
			}
		} else {
			alpm_db_t *sdb = alpm_pkg_get_db(spkg);
			_alpm_log(handle, ALPM_LOG_WARNING, _("%s: local (%s) is newer than %s (%s)\n"),
					lpkg->name, lpkg->version, sdb->treename, spkg->version);
		}
	}
	return 0;
}

127
128
129
130
131
132
133
static alpm_list_t *check_replacers(alpm_handle_t *handle, alpm_pkg_t *lpkg,
		alpm_db_t *sdb)
{
	/* 2. search for replacers in sdb */
	alpm_list_t *replacers = NULL;
	alpm_list_t *k;
	_alpm_log(handle, ALPM_LOG_DEBUG,
134
135
			"searching for replacements for %s in %s\n",
			lpkg->name, sdb->treename);
136
137
138
139
140
141
	for(k = _alpm_db_get_pkgcache(sdb); k; k = k->next) {
		int found = 0;
		alpm_pkg_t *spkg = k->data;
		alpm_list_t *l;
		for(l = alpm_pkg_get_replaces(spkg); l; l = l->next) {
			alpm_depend_t *replace = l->data;
Dan McGee's avatar
Dan McGee committed
142
143
			/* we only want to consider literal matches at this point. */
			if(_alpm_depcmp_literal(lpkg, replace)) {
144
145
146
147
148
				found = 1;
				break;
			}
		}
		if(found) {
Olivier Brunel's avatar
Olivier Brunel committed
149
150
151
152
153
154
155
			alpm_question_replace_t question = {
				.type = ALPM_QUESTION_REPLACE_PKG,
				.replace = 0,
				.oldpkg = lpkg,
				.newpkg = spkg,
				.newdb = sdb
			};
156
157
			alpm_pkg_t *tpkg;
			/* check IgnorePkg/IgnoreGroup */
Allan McRae's avatar
Allan McRae committed
158
159
			if(alpm_pkg_should_ignore(handle, spkg)
					|| alpm_pkg_should_ignore(handle, lpkg)) {
160
161
162
163
164
165
				_alpm_log(handle, ALPM_LOG_WARNING,
						_("ignoring package replacement (%s-%s => %s-%s)\n"),
						lpkg->name, lpkg->version, spkg->name, spkg->version);
				continue;
			}

Olivier Brunel's avatar
Olivier Brunel committed
166
167
			QUESTION(handle, &question);
			if(!question.replace) {
168
169
170
171
172
				continue;
			}

			/* If spkg is already in the target list, we append lpkg to spkg's
			 * removes list */
Allan McRae's avatar
Allan McRae committed
173
			tpkg = alpm_pkg_find(handle->trans->add, spkg->name);
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
			if(tpkg) {
				/* sanity check, multiple repos can contain spkg->name */
				if(tpkg->origin_data.db != sdb) {
					_alpm_log(handle, ALPM_LOG_WARNING, _("cannot replace %s by %s\n"),
							lpkg->name, spkg->name);
					continue;
				}
				_alpm_log(handle, ALPM_LOG_DEBUG, "appending %s to the removes list of %s\n",
						lpkg->name, tpkg->name);
				tpkg->removes = alpm_list_add(tpkg->removes, lpkg);
				/* check the to-be-replaced package's reason field */
				if(alpm_pkg_get_reason(lpkg) == ALPM_PKG_REASON_EXPLICIT) {
					tpkg->reason = ALPM_PKG_REASON_EXPLICIT;
				}
			} else {
				/* add spkg to the target list */
				/* copy over reason */
				spkg->reason = alpm_pkg_get_reason(lpkg);
				spkg->removes = alpm_list_add(NULL, lpkg);
				_alpm_log(handle, ALPM_LOG_DEBUG,
						"adding package %s-%s to the transaction targets\n",
						spkg->name, spkg->version);
				replacers = alpm_list_add(replacers, spkg);
			}
		}
	}
	return replacers;
}

203
/** Search for packages to upgrade and add them to the transaction. */
204
int SYMEXPORT alpm_sync_sysupgrade(alpm_handle_t *handle, int enable_downgrade)
205
{
206
	alpm_list_t *i, *j;
Allan McRae's avatar
Allan McRae committed
207
	alpm_trans_t *trans;
Judd Vinet's avatar
Judd Vinet committed
208

209
	CHECK_HANDLE(handle, return -1);
210
	trans = handle->trans;
211
212
	ASSERT(trans != NULL, RET_ERR(handle, ALPM_ERR_TRANS_NULL, -1));
	ASSERT(trans->state == STATE_INITIALIZED, RET_ERR(handle, ALPM_ERR_TRANS_NOT_INITIALIZED, -1));
213

214
	_alpm_log(handle, ALPM_LOG_DEBUG, "checking for package upgrades\n");
215
	for(i = _alpm_db_get_pkgcache(handle->db_local); i; i = i->next) {
Allan McRae's avatar
Allan McRae committed
216
		alpm_pkg_t *lpkg = i->data;
217

218
219
220
221
222
		if(alpm_pkg_find(trans->remove, lpkg->name)) {
			_alpm_log(handle, ALPM_LOG_DEBUG, "%s is marked for removal -- skipping\n", lpkg->name);
			continue;
		}

Allan McRae's avatar
Allan McRae committed
223
		if(alpm_pkg_find(trans->add, lpkg->name)) {
224
			_alpm_log(handle, ALPM_LOG_DEBUG, "%s is already in the target list -- skipping\n", lpkg->name);
225
226
			continue;
		}
227

228
		/* Search for replacers then literal (if no replacer) in each sync database. */
229
		for(j = handle->dbs_sync; j; j = j->next) {
Allan McRae's avatar
Allan McRae committed
230
			alpm_db_t *sdb = j->data;
231
232
233
234
235
236
			alpm_list_t *replacers;

			if(!(sdb->usage & ALPM_DB_USAGE_UPGRADE)) {
				continue;
			}

Nagy Gabor's avatar
Nagy Gabor committed
237
			/* Check sdb */
238
			replacers = check_replacers(handle, lpkg, sdb);
239
240
			if(replacers) {
				trans->add = alpm_list_join(trans->add, replacers);
Dan McGee's avatar
Dan McGee committed
241
242
243
				/* jump to next local package */
				break;
			} else {
244
245
				alpm_pkg_t *spkg = _alpm_db_get_pkgfromcache(sdb, lpkg->name);
				if(spkg) {
246
					if(check_literal(handle, lpkg, spkg, enable_downgrade)) {
247
248
249
250
						trans->add = alpm_list_add(trans->add, spkg);
					}
					/* jump to next local package */
					break;
Nagy Gabor's avatar
Nagy Gabor committed
251
				}
Nagy Gabor's avatar
Nagy Gabor committed
252
			}
Judd Vinet's avatar
Judd Vinet committed
253
		}
254
	}
255

256
	return 0;
Judd Vinet's avatar
Judd Vinet committed
257
258
}

Xavier Chantry's avatar
Xavier Chantry committed
259
260
261
/** Find group members across a list of databases.
 * If a member exists in several databases, only the first database is used.
 * IgnorePkg is also handled.
Allan McRae's avatar
Allan McRae committed
262
 * @param dbs the list of alpm_db_t *
263
 * @param name the name of the group
Allan McRae's avatar
Allan McRae committed
264
 * @return the list of alpm_pkg_t * (caller is responsible for alpm_list_free)
Xavier Chantry's avatar
Xavier Chantry committed
265
 */
266
alpm_list_t SYMEXPORT *alpm_find_group_pkgs(alpm_list_t *dbs,
Xavier Chantry's avatar
Xavier Chantry committed
267
268
269
270
271
		const char *name)
{
	alpm_list_t *i, *j, *pkgs = NULL, *ignorelist = NULL;

	for(i = dbs; i; i = i->next) {
Allan McRae's avatar
Allan McRae committed
272
		alpm_db_t *db = i->data;
273
		alpm_group_t *grp = alpm_db_get_group(db, name);
Xavier Chantry's avatar
Xavier Chantry committed
274

275
		if(!grp) {
Xavier Chantry's avatar
Xavier Chantry committed
276
			continue;
277
		}
Xavier Chantry's avatar
Xavier Chantry committed
278

Dan McGee's avatar
Dan McGee committed
279
		for(j = grp->packages; j; j = j->next) {
Allan McRae's avatar
Allan McRae committed
280
			alpm_pkg_t *pkg = j->data;
281
			alpm_trans_t *trans = db->handle->trans;
Xavier Chantry's avatar
Xavier Chantry committed
282

Allan McRae's avatar
Allan McRae committed
283
			if(alpm_pkg_find(ignorelist, pkg->name)) {
Xavier Chantry's avatar
Xavier Chantry committed
284
285
				continue;
			}
286
287
288
289
290
291
292
293
294
295
			if(trans != NULL && trans->flags & ALPM_TRANS_FLAG_NEEDED) {
				alpm_pkg_t *local = _alpm_db_get_pkgfromcache(db->handle->db_local, pkg->name);
				if(local && _alpm_pkg_compare_versions(pkg, local) == 0) {
					/* with the NEEDED flag, packages up to date are not reinstalled */
					_alpm_log(db->handle, ALPM_LOG_WARNING, _("%s-%s is up to date -- skipping\n"),
							local->name, local->version);
					ignorelist = alpm_list_add(ignorelist, pkg);
					continue;
				}
			}
Allan McRae's avatar
Allan McRae committed
296
			if(alpm_pkg_should_ignore(db->handle, pkg)) {
Olivier Brunel's avatar
Olivier Brunel committed
297
298
299
300
301
				alpm_question_install_ignorepkg_t question = {
					.type = ALPM_QUESTION_INSTALL_IGNOREPKG,
					.install = 0,
					.pkg = pkg
				};
Xavier Chantry's avatar
Xavier Chantry committed
302
				ignorelist = alpm_list_add(ignorelist, pkg);
Olivier Brunel's avatar
Olivier Brunel committed
303
				QUESTION(db->handle, &question);
304
				if(!question.install) {
Xavier Chantry's avatar
Xavier Chantry committed
305
					continue;
306
				}
Xavier Chantry's avatar
Xavier Chantry committed
307
			}
Allan McRae's avatar
Allan McRae committed
308
			if(!alpm_pkg_find(pkgs, pkg->name)) {
Xavier Chantry's avatar
Xavier Chantry committed
309
310
311
312
313
				pkgs = alpm_list_add(pkgs, pkg);
			}
		}
	}
	alpm_list_free(ignorelist);
314
	return pkgs;
Xavier Chantry's avatar
Xavier Chantry committed
315
316
}

317
318
319
320
/** Compute the size of the files that will be downloaded to install a
 * package.
 * @param newpkg the new package to upgrade to
 */
Allan McRae's avatar
Allan McRae committed
321
static int compute_download_size(alpm_pkg_t *newpkg)
322
{
323
324
	const char *fname;
	char *fpath, *fnamepart = NULL;
Dan McGee's avatar
Dan McGee committed
325
	off_t size = 0;
326
	alpm_handle_t *handle = newpkg->handle;
327
	int ret = 0;
328

329
	if(newpkg->origin != ALPM_PKG_FROM_SYNCDB) {
330
		newpkg->infolevel |= INFRQ_DSIZE;
331
		newpkg->download_size = 0;
332
		return 0;
333
334
	}

Dan McGee's avatar
Dan McGee committed
335
	ASSERT(newpkg->filename != NULL, RET_ERR(handle, ALPM_ERR_PKG_INVALID_NAME, -1));
336
337
	fname = newpkg->filename;
	fpath = _alpm_filecache_find(handle, fname);
338

339
	/* downloaded file exists, so there's nothing to grab */
340
341
	if(fpath) {
		size = 0;
342
343
344
345
346
347
348
349
350
351
352
353
354
355
		goto finish;
	}

	CALLOC(fnamepart, strlen(fname) + 6, sizeof(char), return -1);
	sprintf(fnamepart, "%s.part", fname);
	fpath = _alpm_filecache_find(handle, fnamepart);
	if(fpath) {
		struct stat st;
		if(stat(fpath, &st) == 0) {
			/* subtract the size of the .part file */
			_alpm_log(handle, ALPM_LOG_DEBUG, "using (package - .part) size\n");
			size = newpkg->size - st.st_size;
			size = size < 0 ? 0 : size;
		}
356
357
358

		/* tell the caller that we have a partial */
		ret = 1;
359
	} else {
Dan McGee's avatar
Dan McGee committed
360
		size = newpkg->size;
361
362
	}

363
finish:
364
	_alpm_log(handle, ALPM_LOG_DEBUG, "setting download size %jd for pkg %s\n",
365
			(intmax_t)size, newpkg->name);
366

367
	newpkg->infolevel |= INFRQ_DSIZE;
368
	newpkg->download_size = size;
369
370
371
372

	FREE(fpath);
	FREE(fnamepart);

373
	return ret;
374
375
}

376
int _alpm_sync_prepare(alpm_handle_t *handle, alpm_list_t **data)
Judd Vinet's avatar
Judd Vinet committed
377
{
Dan McGee's avatar
Dan McGee committed
378
	alpm_list_t *i, *j;
379
	alpm_list_t *deps = NULL;
380
	alpm_list_t *unresolvable = NULL;
381
	int from_sync = 0;
Aurelien Foret's avatar
Aurelien Foret committed
382
	int ret = 0;
Allan McRae's avatar
Allan McRae committed
383
	alpm_trans_t *trans = handle->trans;
Olivier Brunel's avatar
Olivier Brunel committed
384
	alpm_event_t event;
Aurelien Foret's avatar
Aurelien Foret committed
385

386
387
388
	if(data) {
		*data = NULL;
	}
389

390
391
	for(i = trans->add; i; i = i->next) {
		alpm_pkg_t *spkg = i->data;
392
393
394
395
		if (spkg->origin == ALPM_PKG_FROM_SYNCDB){
			from_sync = 1;
			break;
		}
396
397
398
	}

	/* ensure all sync database are valid if we will be using them */
399
400
	for(i = handle->dbs_sync; i; i = i->next) {
		const alpm_db_t *db = i->data;
401
		if(db->status & DB_STATUS_INVALID) {
402
403
			RET_ERR(handle, ALPM_ERR_DB_INVALID, -1);
		}
404
405
		/* missing databases are not allowed if we have sync targets */
		if(from_sync && db->status & DB_STATUS_MISSING) {
406
407
			RET_ERR(handle, ALPM_ERR_DB_NOT_FOUND, -1);
		}
408
409
	}

410
	if(!(trans->flags & ALPM_TRANS_FLAG_NODEPS)) {
411
		alpm_list_t *resolved = NULL;
412
		alpm_list_t *remove = alpm_list_copy(trans->remove);
413
		alpm_list_t *localpkgs;
Nagy Gabor's avatar
Nagy Gabor committed
414

415
		/* Build up list by repeatedly resolving each transaction package */
Aurelien Foret's avatar
Aurelien Foret committed
416
		/* Resolve targets dependencies */
Olivier Brunel's avatar
Olivier Brunel committed
417
418
		event.type = ALPM_EVENT_RESOLVEDEPS_START;
		EVENT(handle, &event);
419
		_alpm_log(handle, ALPM_LOG_DEBUG, "resolving target's dependencies\n");
420

Nagy Gabor's avatar
Nagy Gabor committed
421
		/* build remove list for resolvedeps */
Xavier Chantry's avatar
Xavier Chantry committed
422
		for(i = trans->add; i; i = i->next) {
Allan McRae's avatar
Allan McRae committed
423
			alpm_pkg_t *spkg = i->data;
Nagy Gabor's avatar
Nagy Gabor committed
424
			for(j = spkg->removes; j; j = j->next) {
Nagy Gabor's avatar
Nagy Gabor committed
425
				remove = alpm_list_add(remove, j->data);
426
427
428
			}
		}

Dan McGee's avatar
Dan McGee committed
429
430
		/* Compute the fake local database for resolvedeps (partial fix for the
		 * phonon/qt issue) */
431
		localpkgs = alpm_list_diff(_alpm_db_get_pkgcache(handle->db_local),
Dan McGee's avatar
Dan McGee committed
432
				trans->add, _alpm_pkg_cmp);
433

434
		/* Resolve packages in the transaction one at a time, in addition
435
		   building up a list of packages which could not be resolved. */
Xavier Chantry's avatar
Xavier Chantry committed
436
		for(i = trans->add; i; i = i->next) {
Allan McRae's avatar
Allan McRae committed
437
			alpm_pkg_t *pkg = i->data;
438
			if(_alpm_resolvedeps(handle, localpkgs, pkg, trans->add,
439
						&resolved, remove, data) == -1) {
440
441
				unresolvable = alpm_list_add(unresolvable, pkg);
			}
Nagy Gabor's avatar
Nagy Gabor committed
442
			/* Else, [resolved] now additionally contains [pkg] and all of its
443
444
			   dependencies not already on the list */
		}
445
		alpm_list_free(localpkgs);
446
		alpm_list_free(remove);
447

448
449
		/* If there were unresolvable top-level packages, prompt the user to
		   see if they'd like to ignore them rather than failing the sync */
450
		if(unresolvable != NULL) {
Olivier Brunel's avatar
Olivier Brunel committed
451
452
453
454
455
456
457
			alpm_question_remove_pkgs_t question = {
				.type = ALPM_QUESTION_REMOVE_PKGS,
				.skip = 0,
				.packages = unresolvable
			};
			QUESTION(handle, &question);
			if(question.skip) {
458
				/* User wants to remove the unresolvable packages from the
459
460
				   transaction. The packages will be removed from the actual
				   transaction when the transaction packages are replaced with a
461
				   dependency-reordered list below */
Ivy Foster's avatar
Ivy Foster committed
462
				handle->pm_errno = ALPM_ERR_OK;
463
				if(data) {
Andrew Gregory's avatar
Andrew Gregory committed
464
465
					alpm_list_free_inner(*data,
							(alpm_list_fn_free)alpm_depmissing_free);
466
467
468
469
					alpm_list_free(*data);
					*data = NULL;
				}
			} else {
470
				/* pm_errno was set by resolvedeps, callback may have overwrote it */
471
				handle->pm_errno = ALPM_ERR_UNSATISFIED_DEPS;
Nagy Gabor's avatar
Nagy Gabor committed
472
				alpm_list_free(resolved);
473
				alpm_list_free(unresolvable);
474
475
476
				ret = -1;
				goto cleanup;
			}
477
		}
478

Nagy Gabor's avatar
Nagy Gabor committed
479
480
		/* Set DEPEND reason for pulled packages */
		for(i = resolved; i; i = i->next) {
Allan McRae's avatar
Allan McRae committed
481
			alpm_pkg_t *pkg = i->data;
Allan McRae's avatar
Allan McRae committed
482
			if(!alpm_pkg_find(trans->add, pkg->name)) {
483
				pkg->reason = ALPM_PKG_REASON_DEPEND;
Aurelien Foret's avatar
Aurelien Foret committed
484
485
			}
		}
486

487
488
489
490
491
		/* Unresolvable packages will be removed from the target list; set these
		 * aside in the transaction as a list we won't operate on. If we free them
		 * before the end of the transaction, we may kill pointers the frontend
		 * holds to package objects. */
		trans->unresolvable = unresolvable;
492

Xavier Chantry's avatar
Xavier Chantry committed
493
		alpm_list_free(trans->add);
494
		trans->add = resolved;
495

Olivier Brunel's avatar
Olivier Brunel committed
496
497
		event.type = ALPM_EVENT_RESOLVEDEPS_DONE;
		EVENT(handle, &event);
Aurelien Foret's avatar
Aurelien Foret committed
498
499
	}

500
	if(!(trans->flags & ALPM_TRANS_FLAG_NOCONFLICTS)) {
Aurelien Foret's avatar
Aurelien Foret committed
501
		/* check for inter-conflicts and whatnot */
Olivier Brunel's avatar
Olivier Brunel committed
502
503
		event.type = ALPM_EVENT_INTERCONFLICTS_START;
		EVENT(handle, &event);
Aurelien Foret's avatar
Aurelien Foret committed
504

505
		_alpm_log(handle, ALPM_LOG_DEBUG, "looking for conflicts\n");
Aurelien Foret's avatar
Aurelien Foret committed
506

507
		/* 1. check for conflicts in the target list */
508
		_alpm_log(handle, ALPM_LOG_DEBUG, "check targets vs targets\n");
509
		deps = _alpm_innerconflicts(handle, trans->add);
510
511

		for(i = deps; i; i = i->next) {
512
			alpm_conflict_t *conflict = i->data;
Allan McRae's avatar
Allan McRae committed
513
			alpm_pkg_t *rsync, *sync, *sync1, *sync2;
514
515

			/* have we already removed one of the conflicting targets? */
Allan McRae's avatar
Allan McRae committed
516
517
			sync1 = alpm_pkg_find(trans->add, conflict->package1);
			sync2 = alpm_pkg_find(trans->add, conflict->package2);
518
519
520
521
			if(!sync1 || !sync2) {
				continue;
			}

522
			_alpm_log(handle, ALPM_LOG_DEBUG, "conflicting packages in the sync list: '%s' <-> '%s'\n",
523
524
525
					conflict->package1, conflict->package2);

			/* if sync1 provides sync2, we remove sync2 from the targets, and vice versa */
526
527
			alpm_depend_t *dep1 = alpm_dep_from_string(conflict->package1);
			alpm_depend_t *dep2 = alpm_dep_from_string(conflict->package2);
528
			if(_alpm_depcmp(sync1, dep2)) {
529
530
				rsync = sync2;
				sync = sync1;
531
			} else if(_alpm_depcmp(sync2, dep1)) {
532
533
534
				rsync = sync1;
				sync = sync2;
			} else {
535
				_alpm_log(handle, ALPM_LOG_ERROR, _("unresolvable package conflicts detected\n"));
536
				handle->pm_errno = ALPM_ERR_CONFLICTING_DEPS;
537
				ret = -1;
538
				if(data) {
539
					alpm_conflict_t *newconflict = _alpm_conflict_dup(conflict);
540
541
542
543
					if(newconflict) {
						*data = alpm_list_add(*data, newconflict);
					}
				}
Andrew Gregory's avatar
Andrew Gregory committed
544
				alpm_list_free_inner(deps, (alpm_list_fn_free)alpm_conflict_free);
545
				alpm_list_free(deps);
546
547
				alpm_dep_free(dep1);
				alpm_dep_free(dep2);
548
549
				goto cleanup;
			}
550
551
			alpm_dep_free(dep1);
			alpm_dep_free(dep2);
552

Nagy Gabor's avatar
Nagy Gabor committed
553
			/* Prints warning */
554
			_alpm_log(handle, ALPM_LOG_WARNING,
Nagy Gabor's avatar
Nagy Gabor committed
555
					_("removing '%s' from target list because it conflicts with '%s'\n"),
Nagy Gabor's avatar
Nagy Gabor committed
556
					rsync->name, sync->name);
Xavier Chantry's avatar
Xavier Chantry committed
557
			trans->add = alpm_list_remove(trans->add, rsync, _alpm_pkg_cmp, NULL);
558
559
			/* rsync is not a transaction target anymore */
			trans->unresolvable = alpm_list_add(trans->unresolvable, rsync);
560
561
		}

Andrew Gregory's avatar
Andrew Gregory committed
562
		alpm_list_free_inner(deps, (alpm_list_fn_free)alpm_conflict_free);
563
564
565
566
		alpm_list_free(deps);
		deps = NULL;

		/* 2. we check for target vs db conflicts (and resolve)*/
567
		_alpm_log(handle, ALPM_LOG_DEBUG, "check targets vs db and db vs targets\n");
Dan McGee's avatar
Dan McGee committed
568
		deps = _alpm_outerconflicts(handle->db_local, trans->add);
569
570

		for(i = deps; i; i = i->next) {
Olivier Brunel's avatar
Olivier Brunel committed
571
572
573
574
575
			alpm_question_conflict_t question = {
				.type = ALPM_QUESTION_CONFLICT_PKG,
				.remove = 0,
				.conflict = i->data
			};
576
			alpm_conflict_t *conflict = i->data;
Andrew Gregory's avatar
Andrew Gregory committed
577
			int found = 0;
578
579
580

			/* if conflict->package2 (the local package) is not elected for removal,
			   we ask the user */
581
582
583
			if(alpm_pkg_find(trans->remove, conflict->package2)) {
				found = 1;
			}
Xavier Chantry's avatar
Xavier Chantry committed
584
			for(j = trans->add; j && !found; j = j->next) {
Allan McRae's avatar
Allan McRae committed
585
				alpm_pkg_t *spkg = j->data;
Allan McRae's avatar
Allan McRae committed
586
				if(alpm_pkg_find(spkg->removes, conflict->package2)) {
Nagy Gabor's avatar
Nagy Gabor committed
587
					found = 1;
Aurelien Foret's avatar
Aurelien Foret committed
588
				}
589
590
591
592
593
			}
			if(found) {
				continue;
			}

594
			_alpm_log(handle, ALPM_LOG_DEBUG, "package '%s' conflicts with '%s'\n",
595
596
					conflict->package1, conflict->package2);

Olivier Brunel's avatar
Olivier Brunel committed
597
598
			QUESTION(handle, &question);
			if(question.remove) {
Nagy Gabor's avatar
Nagy Gabor committed
599
				/* append to the removes list */
Andrew Gregory's avatar
Andrew Gregory committed
600
601
				alpm_pkg_t *sync = alpm_pkg_find(trans->add, conflict->package1);
				alpm_pkg_t *local = _alpm_db_get_pkgfromcache(handle->db_local, conflict->package2);
602
				_alpm_log(handle, ALPM_LOG_DEBUG, "electing '%s' for removal\n", conflict->package2);
Nagy Gabor's avatar
Nagy Gabor committed
603
				sync->removes = alpm_list_add(sync->removes, local);
604
			} else { /* abort */
605
				_alpm_log(handle, ALPM_LOG_ERROR, _("unresolvable package conflicts detected\n"));
606
				handle->pm_errno = ALPM_ERR_CONFLICTING_DEPS;
607
				ret = -1;
608
				if(data) {
609
					alpm_conflict_t *newconflict = _alpm_conflict_dup(conflict);
610
611
					if(newconflict) {
						*data = alpm_list_add(*data, newconflict);
612
613
					}
				}
Andrew Gregory's avatar
Andrew Gregory committed
614
				alpm_list_free_inner(deps, (alpm_list_fn_free)alpm_conflict_free);
615
				alpm_list_free(deps);
Aurelien Foret's avatar
Aurelien Foret committed
616
				goto cleanup;
Aurelien Foret's avatar
Aurelien Foret committed
617
			}
618
		}
Olivier Brunel's avatar
Olivier Brunel committed
619
620
		event.type = ALPM_EVENT_INTERCONFLICTS_DONE;
		EVENT(handle, &event);
Andrew Gregory's avatar
Andrew Gregory committed
621
		alpm_list_free_inner(deps, (alpm_list_fn_free)alpm_conflict_free);
622
		alpm_list_free(deps);
Aurelien Foret's avatar
Aurelien Foret committed
623
	}
Aurelien Foret's avatar
Aurelien Foret committed
624

Nagy Gabor's avatar
Nagy Gabor committed
625
626
	/* Build trans->remove list */
	for(i = trans->add; i; i = i->next) {
Allan McRae's avatar
Allan McRae committed
627
		alpm_pkg_t *spkg = i->data;
Nagy Gabor's avatar
Nagy Gabor committed
628
		for(j = spkg->removes; j; j = j->next) {
Allan McRae's avatar
Allan McRae committed
629
			alpm_pkg_t *rpkg = j->data;
Allan McRae's avatar
Allan McRae committed
630
			if(!alpm_pkg_find(trans->remove, rpkg->name)) {
631
				alpm_pkg_t *copy;
632
				_alpm_log(handle, ALPM_LOG_DEBUG, "adding '%s' to remove list\n", rpkg->name);
633
634
635
636
				if(_alpm_pkg_dup(rpkg, &copy) == -1) {
					return -1;
				}
				trans->remove = alpm_list_add(trans->remove, copy);
637
			}
638
		}
Nagy Gabor's avatar
Nagy Gabor committed
639
	}
640

641
	if(!(trans->flags & ALPM_TRANS_FLAG_NODEPS)) {
642
		_alpm_log(handle, ALPM_LOG_DEBUG, "checking dependencies\n");
643
644
		deps = alpm_checkdeps(handle, _alpm_db_get_pkgcache(handle->db_local),
				trans->remove, trans->add, 1);
645
		if(deps) {
646
			handle->pm_errno = ALPM_ERR_UNSATISFIED_DEPS;
647
			ret = -1;
648
649
650
			if(data) {
				*data = deps;
			} else {
Andrew Gregory's avatar
Andrew Gregory committed
651
652
				alpm_list_free_inner(deps,
						(alpm_list_fn_free)alpm_depmissing_free);
653
				alpm_list_free(deps);
654
			}
655
			goto cleanup;
656
657
		}
	}
Xavier Chantry's avatar
Xavier Chantry committed
658
	for(i = trans->add; i; i = i->next) {
659
		/* update download size field */
Allan McRae's avatar
Allan McRae committed
660
		alpm_pkg_t *spkg = i->data;
661
		alpm_pkg_t *lpkg = alpm_db_get_pkg(handle->db_local, spkg->name);
662
		if(compute_download_size(spkg) < 0) {
663
664
665
			ret = -1;
			goto cleanup;
		}
666
667
668
669
		if(lpkg && _alpm_pkg_dup(lpkg, &spkg->oldpkg) != 0) {
			ret = -1;
			goto cleanup;
		}
670
	}
Judd Vinet's avatar
Judd Vinet committed
671

Aurelien Foret's avatar
Aurelien Foret committed
672
cleanup:
673
	return ret;
Judd Vinet's avatar
Judd Vinet committed
674
675
}

676
677
678
679
680
/** Returns the size of the files that will be downloaded to install a
 * package.
 * @param newpkg the new package to upgrade to
 * @return the size of the download
 */
Allan McRae's avatar
Allan McRae committed
681
off_t SYMEXPORT alpm_pkg_download_size(alpm_pkg_t *newpkg)
682
{
683
684
685
	if(!(newpkg->infolevel & INFRQ_DSIZE)) {
		compute_download_size(newpkg);
	}
686
	return newpkg->download_size;
687
688
}

689
690
691
/**
 * Prompts to delete the file now that we know it is invalid.
 * @param handle the context handle
Dan McGee's avatar
Dan McGee committed
692
 * @param filename the absolute path of the file to test
693
 * @param reason an error code indicating the reason for package invalidity
Nathan Jones's avatar
Nathan Jones committed
694
 *
695
 * @return 1 if file was removed, 0 otherwise
Nathan Jones's avatar
Nathan Jones committed
696
 */
697
static int prompt_to_delete(alpm_handle_t *handle, const char *filepath,
698
		alpm_errno_t reason)
Nathan Jones's avatar
Nathan Jones committed
699
{
Olivier Brunel's avatar
Olivier Brunel committed
700
701
702
703
704
705
706
707
	alpm_question_corrupted_t question = {
		.type = ALPM_QUESTION_CORRUPTED_PKG,
		.remove = 0,
		.filepath = filepath,
		.reason = reason
	};
	QUESTION(handle, &question);
	if(question.remove) {
708
		unlink(filepath);
Nathan Jones's avatar
Nathan Jones committed
709
	}
Olivier Brunel's avatar
Olivier Brunel committed
710
	return question.remove;
Nathan Jones's avatar
Nathan Jones committed
711
712
}

713
714
715
716
717
718
static struct dload_payload *build_payload(alpm_handle_t *handle,
		const char *filename, size_t size, alpm_list_t *servers)
{
		struct dload_payload *payload;

		CALLOC(payload, 1, sizeof(*payload), RET_ERR(handle, ALPM_ERR_MEMORY, NULL));
719
		STRDUP(payload->remote_name, filename, FREE(payload); RET_ERR(handle, ALPM_ERR_MEMORY, NULL));
720
721
722
723
724
		payload->max_size = size;
		payload->servers = servers;
		return payload;
}

725
static int find_dl_candidates(alpm_db_t *repo, alpm_list_t **files)
726
727
728
729
730
731
732
{
	alpm_list_t *i;
	alpm_handle_t *handle = repo->handle;

	for(i = handle->trans->add; i; i = i->next) {
		alpm_pkg_t *spkg = i->data;

733
		if(spkg->origin != ALPM_PKG_FROM_FILE && repo == spkg->origin_data.db) {
734
735
			char *fpath = NULL;

736
737
738
739
740
741
742
			if(!repo->servers) {
				handle->pm_errno = ALPM_ERR_SERVER_NONE;
				_alpm_log(handle, ALPM_LOG_ERROR, "%s: %s\n",
						alpm_strerror(handle->pm_errno), repo->treename);
				return 1;
			}

743
744
745
746
747
748
749
750
			ASSERT(spkg->filename != NULL, RET_ERR(handle, ALPM_ERR_PKG_INVALID_NAME, -1));

			if(spkg->download_size == 0) {
				/* check for file in cache - allows us to handle complete .part files */
				fpath = _alpm_filecache_find(handle, spkg->filename);
			}

			if(spkg->download_size != 0 || !fpath) {
751
				struct dload_payload *payload;
752
753
				payload = build_payload(handle, spkg->filename, spkg->size, repo->servers);
				ASSERT(payload, return -1);
754
755
				*files = alpm_list_add(*files, payload);
			}
756
757

			FREE(fpath);
758
759
760
761
762
763
		}
	}

	return 0;
}

764
765
766
static int download_single_file(alpm_handle_t *handle, struct dload_payload *payload,
		const char *cachedir)
{
767
768
769
770
	alpm_event_pkgdownload_t event = {
		.type = ALPM_EVENT_PKGDOWNLOAD_START,
		.file = payload->remote_name
	};
771
772
	const alpm_list_t *server;

Dan McGee's avatar
Dan McGee committed
773
774
775
	payload->handle = handle;
	payload->allow_resume = 1;

776
	EVENT(handle, &event);
777
778
779
780
781
782
783
784
785
	for(server = payload->servers; server; server = server->next) {
		const char *server_url = server->data;
		size_t len;

		/* print server + filename into a buffer */
		len = strlen(server_url) + strlen(payload->remote_name) + 2;
		MALLOC(payload->fileurl, len, RET_ERR(handle, ALPM_ERR_MEMORY, -1));
		snprintf(payload->fileurl, len, "%s/%s", server_url, payload->remote_name);

786
		if(_alpm_download(payload, cachedir, NULL, NULL) != -1) {
787
788
			event.type = ALPM_EVENT_PKGDOWNLOAD_DONE;
			EVENT(handle, &event);
789
790
			return 0;
		}
791
		_alpm_dload_payload_reset_for_retry(payload);
792
793
	}

794
795
	event.type = ALPM_EVENT_PKGDOWNLOAD_FAILED;
	EVENT(handle, &event);
796
797
798
	return -1;
}

799
static int download_files(alpm_handle_t *handle)
Judd Vinet's avatar
Judd Vinet committed
800
{
801
	const char *cachedir;
802
	alpm_list_t *i, *files = NULL;
803
	int errors = 0;
Olivier Brunel's avatar
Olivier Brunel committed
804
	alpm_event_t event;
Aurelien Foret's avatar
Aurelien Foret committed
805

806
	cachedir = _alpm_filecache_setup(handle);
807
	handle->trans->state = STATE_DOWNLOADING;
Nathan Jones's avatar
Nathan Jones committed
808

809
810
811
812
813
814
	/* Total progress - figure out the total download size if required to
	 * pass to the callback. This function is called once, and it is up to the
	 * frontend to compute incremental progress. */
	if(handle->totaldlcb) {
		off_t total_size = (off_t)0;
		/* sum up the download size for each package and store total */
815
		for(i = handle->trans->add; i; i = i->next) {
Allan McRae's avatar
Allan McRae committed
816
			alpm_pkg_t *spkg = i->data;
817
818
819
820
821
			total_size += spkg->download_size;
		}
		handle->totaldlcb(total_size);
	}

822
	for(i = handle->dbs_sync; i; i = i->next) {
823
		errors += find_dl_candidates(i->data, &files);
824
	}
825

826
	if(files) {
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
		/* check for necessary disk space for download */
		if(handle->checkspace) {
			off_t *file_sizes;
			size_t idx, num_files;
			int ret;

			_alpm_log(handle, ALPM_LOG_DEBUG, "checking available disk space for download\n");

			num_files = alpm_list_count(files);
			CALLOC(file_sizes, num_files, sizeof(off_t), goto finish);

			for(i = files, idx = 0; i; i = i->next, idx++) {
				const struct dload_payload *payload = i->data;
				file_sizes[idx] = payload->max_size;
			}

			ret = _alpm_check_downloadspace(handle, cachedir, num_files, file_sizes);
			free(file_sizes);

			if(ret != 0) {
				errors++;
				goto finish;
			}
		}

Olivier Brunel's avatar
Olivier Brunel committed
852
853
		event.type = ALPM_EVENT_RETRIEVE_START;
		EVENT(handle, &event);
854
		event.type = ALPM_EVENT_RETRIEVE_DONE;
855
		for(i = files; i; i = i->next) {
856
			if(download_single_file(handle, i->data, cachedir) == -1) {
857
				errors++;
858
				event.type = ALPM_EVENT_RETRIEVE_FAILED;
859
				_alpm_log(handle, ALPM_LOG_WARNING, _("failed to retrieve some files\n"));
860
			}
861
		}
862
		EVENT(handle, &event);
863
	}
864

865
866
finish:
	if(files) {
867
868
		alpm_list_free_inner(files, (alpm_list_fn_free)_alpm_dload_payload_reset);
		FREELIST(files);
869
870
	}

871
872
	for(i = handle->trans->add; i; i = i->next) {
		alpm_pkg_t *pkg = i->data;
873
874
875
876
		pkg->infolevel &= ~INFRQ_DSIZE;
		pkg->download_size = 0;
	}

877
878
879
880
	/* clear out value to let callback know we are done */
	if(handle->totaldlcb) {
		handle->totaldlcb(0);
	}