sync.c 38.7 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-2014 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"
46
#include "delta.h"
Xavier Chantry's avatar
Xavier Chantry committed
47
#include "remove.h"
48
#include "diskspace.h"
Dan McGee's avatar
Dan McGee committed
49
#include "signing.h"
Judd Vinet's avatar
Judd Vinet committed
50

Nagy Gabor's avatar
Nagy Gabor committed
51
52
53
/** Check for new version of pkg in sync repos
 * (only the first occurrence is considered in sync)
 */
Allan McRae's avatar
Allan McRae committed
54
alpm_pkg_t SYMEXPORT *alpm_sync_newversion(alpm_pkg_t *pkg, alpm_list_t *dbs_sync)
Nagy Gabor's avatar
Nagy Gabor committed
55
56
{
	alpm_list_t *i;
Allan McRae's avatar
Allan McRae committed
57
	alpm_pkg_t *spkg = NULL;
Nagy Gabor's avatar
Nagy Gabor committed
58

59
60
61
	ASSERT(pkg != NULL, return NULL);
	pkg->handle->pm_errno = 0;

Nagy Gabor's avatar
Nagy Gabor committed
62
	for(i = dbs_sync; !spkg && i; i = i->next) {
63
64
65
66
67
68
		alpm_db_t *db = i->data;
		if(!(db->usage & ALPM_DB_USAGE_SEARCH)) {
			continue;
		}

		spkg = _alpm_db_get_pkgfromcache(db, pkg->name);
Nagy Gabor's avatar
Nagy Gabor committed
69
70
71
	}

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

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

87
88
89
90
91
92
93
94
95
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
96
97
		if(alpm_pkg_should_ignore(handle, spkg)
				|| alpm_pkg_should_ignore(handle, lpkg)) {
98
99
100
101
102
103
104
105
106
107
			_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
108
109
			if(alpm_pkg_should_ignore(handle, spkg)
					|| alpm_pkg_should_ignore(handle, lpkg)) {
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
				_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;
}

126
127
128
129
130
131
132
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,
133
134
			"searching for replacements for %s in %s\n",
			lpkg->name, sdb->treename);
135
136
137
138
139
140
	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
141
142
			/* we only want to consider literal matches at this point. */
			if(_alpm_depcmp_literal(lpkg, replace)) {
143
144
145
146
147
				found = 1;
				break;
			}
		}
		if(found) {
Olivier Brunel's avatar
Olivier Brunel committed
148
149
150
151
152
153
154
			alpm_question_replace_t question = {
				.type = ALPM_QUESTION_REPLACE_PKG,
				.replace = 0,
				.oldpkg = lpkg,
				.newpkg = spkg,
				.newdb = sdb
			};
155
156
			alpm_pkg_t *tpkg;
			/* check IgnorePkg/IgnoreGroup */
Allan McRae's avatar
Allan McRae committed
157
158
			if(alpm_pkg_should_ignore(handle, spkg)
					|| alpm_pkg_should_ignore(handle, lpkg)) {
159
160
161
162
163
164
				_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
165
166
			QUESTION(handle, &question);
			if(!question.replace) {
167
168
169
170
171
				continue;
			}

			/* If spkg is already in the target list, we append lpkg to spkg's
			 * removes list */
Allan McRae's avatar
Allan McRae committed
172
			tpkg = alpm_pkg_find(handle->trans->add, spkg->name);
173
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
			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;
}

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

208
	CHECK_HANDLE(handle, return -1);
209
	trans = handle->trans;
210
211
	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));
212

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

217
218
219
220
221
		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
222
		if(alpm_pkg_find(trans->add, lpkg->name)) {
223
			_alpm_log(handle, ALPM_LOG_DEBUG, "%s is already in the target list -- skipping\n", lpkg->name);
224
225
			continue;
		}
226

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

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

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

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

Xavier Chantry's avatar
Xavier Chantry committed
258
259
260
/** 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
261
 * @param dbs the list of alpm_db_t *
262
 * @param name the name of the group
Allan McRae's avatar
Allan McRae committed
263
 * @return the list of alpm_pkg_t * (caller is responsible for alpm_list_free)
Xavier Chantry's avatar
Xavier Chantry committed
264
 */
265
alpm_list_t SYMEXPORT *alpm_find_group_pkgs(alpm_list_t *dbs,
Xavier Chantry's avatar
Xavier Chantry committed
266
267
268
269
270
		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
271
		alpm_db_t *db = i->data;
272
		alpm_group_t *grp = alpm_db_get_group(db, name);
Xavier Chantry's avatar
Xavier Chantry committed
273
274
275
276

		if(!grp)
			continue;

Dan McGee's avatar
Dan McGee committed
277
		for(j = grp->packages; j; j = j->next) {
Allan McRae's avatar
Allan McRae committed
278
			alpm_pkg_t *pkg = j->data;
Xavier Chantry's avatar
Xavier Chantry committed
279

Allan McRae's avatar
Allan McRae committed
280
			if(alpm_pkg_find(ignorelist, pkg->name)) {
Xavier Chantry's avatar
Xavier Chantry committed
281
282
				continue;
			}
Allan McRae's avatar
Allan McRae committed
283
			if(alpm_pkg_should_ignore(db->handle, pkg)) {
Olivier Brunel's avatar
Olivier Brunel committed
284
285
286
287
288
				alpm_question_install_ignorepkg_t question = {
					.type = ALPM_QUESTION_INSTALL_IGNOREPKG,
					.install = 0,
					.pkg = pkg
				};
Xavier Chantry's avatar
Xavier Chantry committed
289
				ignorelist = alpm_list_add(ignorelist, pkg);
Olivier Brunel's avatar
Olivier Brunel committed
290
291
				QUESTION(db->handle, &question);
				if(!question.install)
Xavier Chantry's avatar
Xavier Chantry committed
292
293
					continue;
			}
Allan McRae's avatar
Allan McRae committed
294
			if(!alpm_pkg_find(pkgs, pkg->name)) {
Xavier Chantry's avatar
Xavier Chantry committed
295
296
297
298
299
				pkgs = alpm_list_add(pkgs, pkg);
			}
		}
	}
	alpm_list_free(ignorelist);
300
	return pkgs;
Xavier Chantry's avatar
Xavier Chantry committed
301
302
}

303
304
305
306
/** 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
307
static int compute_download_size(alpm_pkg_t *newpkg)
308
{
309
310
	const char *fname;
	char *fpath, *fnamepart = NULL;
Dan McGee's avatar
Dan McGee committed
311
	off_t size = 0;
312
	alpm_handle_t *handle = newpkg->handle;
313
	int ret = 0;
314

315
	if(newpkg->origin != ALPM_PKG_FROM_SYNCDB) {
316
		newpkg->infolevel |= INFRQ_DSIZE;
317
		newpkg->download_size = 0;
318
		return 0;
319
320
	}

Dan McGee's avatar
Dan McGee committed
321
	ASSERT(newpkg->filename != NULL, RET_ERR(handle, ALPM_ERR_PKG_INVALID_NAME, -1));
322
323
	fname = newpkg->filename;
	fpath = _alpm_filecache_find(handle, fname);
324

325
	/* downloaded file exists, so there's nothing to grab */
326
327
	if(fpath) {
		size = 0;
328
329
330
331
332
333
334
335
336
337
338
339
340
341
		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;
		}
342
343
344

		/* tell the caller that we have a partial */
		ret = 1;
345
	} else if(handle->deltaratio > 0.0) {
Dan McGee's avatar
Dan McGee committed
346
		off_t dltsize;
347

Dan McGee's avatar
Dan McGee committed
348
349
		dltsize = _alpm_shortest_delta_path(handle, newpkg->deltas,
				newpkg->filename, &newpkg->delta_path);
350

351
		if(newpkg->delta_path && (dltsize < newpkg->size * handle->deltaratio)) {
352
			_alpm_log(handle, ALPM_LOG_DEBUG, "using delta size\n");
353
354
			size = dltsize;
		} else {
355
			_alpm_log(handle, ALPM_LOG_DEBUG, "using package size\n");
Dan McGee's avatar
Dan McGee committed
356
			size = newpkg->size;
357
358
359
360
			alpm_list_free(newpkg->delta_path);
			newpkg->delta_path = NULL;
		}
	} else {
Dan McGee's avatar
Dan McGee committed
361
		size = newpkg->size;
362
363
	}

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

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

	FREE(fpath);
	FREE(fnamepart);

374
	return ret;
375
376
}

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

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

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

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

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

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

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

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

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

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

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

488
489
490
491
492
		/* 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;
493

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Aurelien Foret's avatar
Aurelien Foret committed
668
cleanup:
669
	return ret;
Judd Vinet's avatar
Judd Vinet committed
670
671
}

672
673
674
675
676
/** 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
677
off_t SYMEXPORT alpm_pkg_download_size(alpm_pkg_t *newpkg)
678
{
679
680
681
	if(!(newpkg->infolevel & INFRQ_DSIZE)) {
		compute_download_size(newpkg);
	}
682
	return newpkg->download_size;
683
684
}

Dan McGee's avatar
Dan McGee committed
685
static int endswith(const char *filename, const char *extension)
686
{
Dan McGee's avatar
Dan McGee committed
687
	const char *s = filename + strlen(filename) - strlen(extension);
688
	return strcmp(s, extension) == 0;
689
690
}

691
692
693
694
695
/** Applies delta files to create an upgraded package file.
 *
 * All intermediate files are deleted, leaving only the starting and
 * ending package files.
 *
696
 * @param handle the context handle
697
698
699
 *
 * @return 0 if all delta files were able to be applied, 1 otherwise.
 */
700
static int apply_deltas(alpm_handle_t *handle)
701
{
702
	alpm_list_t *i;
703
704
	size_t deltas_found = 0;
	int ret = 0;
705
	const char *cachedir = _alpm_filecache_setup(handle);
Allan McRae's avatar
Allan McRae committed
706
	alpm_trans_t *trans = handle->trans;
Olivier Brunel's avatar
Olivier Brunel committed
707
	alpm_event_delta_patch_t event;
708

Xavier Chantry's avatar
Xavier Chantry committed
709
	for(i = trans->add; i; i = i->next) {
Allan McRae's avatar
Allan McRae committed
710
		alpm_pkg_t *spkg = i->data;
711
712
		alpm_list_t *delta_path = spkg->delta_path;
		alpm_list_t *dlts = NULL;
713

714
715
716
		if(!delta_path) {
			continue;
		}
717

718
719
720
		if(!deltas_found) {
			/* only show this if we actually have deltas to apply, and it is before
			 * the very first one */
Olivier Brunel's avatar
Olivier Brunel committed
721
722
			event.type = ALPM_EVENT_DELTA_PATCHES_START;
			EVENT(handle, &event);
723
724
725
			deltas_found = 1;
		}

726
		for(dlts = delta_path; dlts; dlts = dlts->next) {
Allan McRae's avatar
Allan McRae committed
727
			alpm_delta_t *d = dlts->data;
728
729
			char *delta, *from, *to;
			char command[PATH_MAX];
730
			size_t len = 0;
731

732
			delta = _alpm_filecache_find(handle, d->delta);
733
734
			/* the initial package might be in a different cachedir */
			if(dlts == delta_path) {
735
				from = _alpm_filecache_find(handle, d->from);
736
			} else {
737
738
				/* len = cachedir len + from len + '/' + null */
				len = strlen(cachedir) + strlen(d->from) + 2;
739
				MALLOC(from, len, RET_ERR(handle, ALPM_ERR_MEMORY, 1));
740
				snprintf(from, len, "%s/%s", cachedir, d->from);
741
			}
742
			len = strlen(cachedir) + strlen(d->to) + 2;
743
			MALLOC(to, len, free(from); RET_ERR(handle, ALPM_ERR_MEMORY, 1));
744
			snprintf(to, len, "%s/%s", cachedir, d->to);
745

746
			/* build the patch command */
747
			if(endswith(to, ".gz")) {
748
749
750
751
				/* special handling for gzip : we disable timestamp with -n option */
				snprintf(command, PATH_MAX, "xdelta3 -d -q -R -c -s %s %s | gzip -n > %s", from, delta, to);
			} else {
				snprintf(command, PATH_MAX, "xdelta3 -d -q -s %s %s %s", from, delta, to);
752
			}
753

754
			_alpm_log(handle, ALPM_LOG_DEBUG, "command: %s\n", command);
755

Olivier Brunel's avatar
Olivier Brunel committed
756
757
758
			event.type = ALPM_EVENT_DELTA_PATCH_START;
			event.delta = d;
			EVENT(handle, &event);
759
760
761

			int retval = system(command);
			if(retval == 0) {
Olivier Brunel's avatar
Olivier Brunel committed
762
763
				event.type = ALPM_EVENT_DELTA_PATCH_DONE;
				EVENT(handle, &event);
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780

				/* delete the delta file */
				unlink(delta);

				/* Delete the 'from' package but only if it is an intermediate
				 * package. The starting 'from' package should be kept, just
				 * as if deltas were not used. */
				if(dlts != delta_path) {
					unlink(from);
				}
			}
			FREE(from);
			FREE(to);
			FREE(delta);

			if(retval != 0) {
				/* one delta failed for this package, cancel the remaining ones */
Olivier Brunel's avatar
Olivier Brunel committed
781
782
				event.type = ALPM_EVENT_DELTA_PATCH_FAILED;
				EVENT(handle, &event);
783
				handle->pm_errno = ALPM_ERR_DLT_PATCHFAILED;
784
785
				ret = 1;
				break;
786
787
788
			}
		}
	}
789
	if(deltas_found) {
Olivier Brunel's avatar
Olivier Brunel committed
790
791
		event.type = ALPM_EVENT_DELTA_PATCHES_DONE;
		EVENT(handle, &event);
792
	}
793

794
	return ret;
795
796
}

797
798
799
/**
 * Prompts to delete the file now that we know it is invalid.
 * @param handle the context handle
Dan McGee's avatar
Dan McGee committed
800
 * @param filename the absolute path of the file to test
801
 * @param reason an error code indicating the reason for package invalidity
Nathan Jones's avatar
Nathan Jones committed
802
 *
803
 * @return 1 if file was removed, 0 otherwise
Nathan Jones's avatar
Nathan Jones committed
804
 */
805
static int prompt_to_delete(alpm_handle_t *handle, const char *filepath,
806
		alpm_errno_t reason)
Nathan Jones's avatar
Nathan Jones committed
807
{
Olivier Brunel's avatar
Olivier Brunel committed
808
809
810
811
812
813
814
815
	alpm_question_corrupted_t question = {
		.type = ALPM_QUESTION_CORRUPTED_PKG,
		.remove = 0,
		.filepath = filepath,
		.reason = reason
	};
	QUESTION(handle, &question);
	if(question.remove) {
816
		unlink(filepath);
Nathan Jones's avatar
Nathan Jones committed
817
	}
Olivier Brunel's avatar
Olivier Brunel committed
818
	return question.remove;
Nathan Jones's avatar
Nathan Jones committed
819
820
}

821
static int validate_deltas(alpm_handle_t *handle, alpm_list_t *deltas)
822
{
823
	alpm_list_t *i, *errors = NULL;
Olivier Brunel's avatar
Olivier Brunel committed
824
	alpm_event_t event;
825
826
827
828
829
830

	if(!deltas) {
		return 0;
	}

	/* Check integrity of deltas */
Olivier Brunel's avatar
Olivier Brunel committed
831
832
	event.type = ALPM_EVENT_DELTA_INTEGRITY_START;
	EVENT(handle, &event);
833
	for(i = deltas; i; i = i->next) {
834
		alpm_delta_t *d = i->data;
Dan McGee's avatar
Dan McGee committed
835
		char *filepath = _alpm_filecache_find(handle, d->delta);
836

Allan McRae's avatar
Allan McRae committed
837
		if(_alpm_test_checksum(filepath, d->delta_md5, ALPM_PKG_VALIDATION_MD5SUM)) {
838
839
840
			errors = alpm_list_add(errors, filepath);
		} else {
			FREE(filepath);
841
842
		}
	}
Olivier Brunel's avatar
Olivier Brunel committed
843
844
	event.type = ALPM_EVENT_DELTA_INTEGRITY_DONE;
	EVENT(handle, &event);
845

846
	if(errors) {
847
848
849
850
851
852
		for(i = errors; i; i = i->next<