1 /*
2  *  add.c
3  *
4  *  Copyright (c) 2006-2018 Pacman Development Team <pacman-dev@archlinux.org>
5  *  Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <stdlib.h>
22 #include <errno.h>
23 #include <string.h>
24 #include <limits.h>
25 #include <fcntl.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29 #include <stdint.h> /* int64_t */
30 
31 /* libarchive */
32 #include <archive.h>
33 #include <archive_entry.h>
34 
35 /* libalpm */
36 #include "add.h"
37 #include "alpm.h"
38 #include "alpm_list.h"
39 #include "handle.h"
40 #include "libarchive-compat.h"
41 #include "trans.h"
42 #include "util.h"
43 #include "log.h"
44 #include "backup.h"
45 #include "package.h"
46 #include "db.h"
47 #include "remove.h"
48 #include "handle.h"
49 
50 /** Add a package to the transaction. */
alpm_add_pkg(alpm_handle_t * handle,alpm_pkg_t * pkg)51 int SYMEXPORT alpm_add_pkg(alpm_handle_t *handle, alpm_pkg_t *pkg)
52 {
53 	const char *pkgname, *pkgver;
54 	alpm_trans_t *trans;
55 	alpm_pkg_t *local;
56 
57 	/* Sanity checks */
58 	CHECK_HANDLE(handle, return -1);
59 	ASSERT(pkg != NULL, RET_ERR(handle, ALPM_ERR_WRONG_ARGS, -1));
60 	ASSERT(pkg->origin != ALPM_PKG_FROM_LOCALDB,
61 			RET_ERR(handle, ALPM_ERR_WRONG_ARGS, -1));
62 	ASSERT(handle == pkg->handle, RET_ERR(handle, ALPM_ERR_WRONG_ARGS, -1));
63 	trans = handle->trans;
64 	ASSERT(trans != NULL, RET_ERR(handle, ALPM_ERR_TRANS_NULL, -1));
65 	ASSERT(trans->state == STATE_INITIALIZED,
66 			RET_ERR(handle, ALPM_ERR_TRANS_NOT_INITIALIZED, -1));
67 
68 	pkgname = pkg->name;
69 	pkgver = pkg->version;
70 
71 	_alpm_log(handle, ALPM_LOG_DEBUG, "adding package '%s'\n", pkgname);
72 
73 	if(alpm_pkg_find(trans->add, pkgname)) {
74 		RET_ERR(handle, ALPM_ERR_TRANS_DUP_TARGET, -1);
75 	}
76 
77 	local = _alpm_db_get_pkgfromcache(handle->db_local, pkgname);
78 	if(local) {
79 		const char *localpkgname = local->name;
80 		const char *localpkgver = local->version;
81 		int cmp = _alpm_pkg_compare_versions(pkg, local);
82 
83 		if(cmp == 0) {
84 			if(trans->flags & ALPM_TRANS_FLAG_NEEDED) {
85 				/* with the NEEDED flag, packages up to date are not reinstalled */
86 				_alpm_log(handle, ALPM_LOG_WARNING, _("%s-%s is up to date -- skipping\n"),
87 						localpkgname, localpkgver);
88 				return 0;
89 			} else if(!(trans->flags & ALPM_TRANS_FLAG_DOWNLOADONLY)) {
90 				_alpm_log(handle, ALPM_LOG_WARNING, _("%s-%s is up to date -- reinstalling\n"),
91 						localpkgname, localpkgver);
92 			}
93 		} else if(cmp < 0 && !(trans->flags & ALPM_TRANS_FLAG_DOWNLOADONLY)) {
94 			/* local version is newer */
95 			_alpm_log(handle, ALPM_LOG_WARNING, _("downgrading package %s (%s => %s)\n"),
96 					localpkgname, localpkgver, pkgver);
97 		}
98 	}
99 
100 	/* add the package to the transaction */
101 	pkg->reason = ALPM_PKG_REASON_EXPLICIT;
102 	_alpm_log(handle, ALPM_LOG_DEBUG, "adding package %s-%s to the transaction add list\n",
103 						pkgname, pkgver);
104 	trans->add = alpm_list_add(trans->add, pkg);
105 
106 	return 0;
107 }
108 
perform_extraction(alpm_handle_t * handle,struct archive * archive,struct archive_entry * entry,const char * filename)109 static int perform_extraction(alpm_handle_t *handle, struct archive *archive,
110 		struct archive_entry *entry, const char *filename)
111 {
112 	int ret;
113 	struct archive *archive_writer;
114 	const int archive_flags = ARCHIVE_EXTRACT_OWNER |
115 	                          ARCHIVE_EXTRACT_PERM |
116 	                          ARCHIVE_EXTRACT_TIME |
117 	                          ARCHIVE_EXTRACT_UNLINK |
118 	                          ARCHIVE_EXTRACT_SECURE_SYMLINKS;
119 
120 	archive_entry_set_pathname(entry, filename);
121 
122 	archive_writer = archive_write_disk_new();
123 	if (archive_writer == NULL) {
124 		_alpm_log(handle, ALPM_LOG_ERROR, _("cannot allocate disk archive object"));
125 		alpm_logaction(handle, ALPM_CALLER_PREFIX,
126 				"error: cannot allocate disk archive object");
127 		return 1;
128 	}
129 
130 	archive_write_disk_set_options(archive_writer, archive_flags);
131 
132 	ret = archive_read_extract2(archive, entry, archive_writer);
133 
134 	archive_write_free(archive_writer);
135 
136 	if(ret == ARCHIVE_WARN && archive_errno(archive) != ENOSPC) {
137 		/* operation succeeded but a "non-critical" error was encountered */
138 		_alpm_log(handle, ALPM_LOG_WARNING, _("warning given when extracting %s (%s)\n"),
139 				filename, archive_error_string(archive));
140 	} else if(ret != ARCHIVE_OK) {
141 		_alpm_log(handle, ALPM_LOG_ERROR, _("could not extract %s (%s)\n"),
142 				filename, archive_error_string(archive));
143 		alpm_logaction(handle, ALPM_CALLER_PREFIX,
144 				"error: could not extract %s (%s)\n",
145 				filename, archive_error_string(archive));
146 		return 1;
147 	}
148 	return 0;
149 }
150 
try_rename(alpm_handle_t * handle,const char * src,const char * dest)151 static int try_rename(alpm_handle_t *handle, const char *src, const char *dest)
152 {
153 	if(rename(src, dest)) {
154 		_alpm_log(handle, ALPM_LOG_ERROR, _("could not rename %s to %s (%s)\n"),
155 				src, dest, strerror(errno));
156 		alpm_logaction(handle, ALPM_CALLER_PREFIX,
157 				"error: could not rename %s to %s (%s)\n", src, dest, strerror(errno));
158 		return 1;
159 	}
160 	return 0;
161 }
162 
extract_db_file(alpm_handle_t * handle,struct archive * archive,struct archive_entry * entry,alpm_pkg_t * newpkg,const char * entryname)163 static int extract_db_file(alpm_handle_t *handle, struct archive *archive,
164 		struct archive_entry *entry, alpm_pkg_t *newpkg, const char *entryname)
165 {
166 	char filename[PATH_MAX]; /* the actual file we're extracting */
167 	const char *dbfile = NULL;
168 	if(strcmp(entryname, ".INSTALL") == 0) {
169 		dbfile = "install";
170 	} else if(strcmp(entryname, ".CHANGELOG") == 0) {
171 		dbfile = "changelog";
172 	} else if(strcmp(entryname, ".MTREE") == 0) {
173 		dbfile = "mtree";
174 	} else if(*entryname == '.') {
175 		/* reserve all files starting with '.' for future possibilities */
176 		_alpm_log(handle, ALPM_LOG_DEBUG, "skipping extraction of '%s'\n", entryname);
177 		archive_read_data_skip(archive);
178 		return 0;
179 	}
180 	archive_entry_set_perm(entry, 0644);
181 	snprintf(filename, PATH_MAX, "%s%s-%s/%s",
182 			_alpm_db_path(handle->db_local), newpkg->name, newpkg->version, dbfile);
183 	return perform_extraction(handle, archive, entry, filename);
184 }
185 
extract_single_file(alpm_handle_t * handle,struct archive * archive,struct archive_entry * entry,alpm_pkg_t * newpkg,alpm_pkg_t * oldpkg)186 static int extract_single_file(alpm_handle_t *handle, struct archive *archive,
187 		struct archive_entry *entry, alpm_pkg_t *newpkg, alpm_pkg_t *oldpkg)
188 {
189 	const char *entryname = archive_entry_pathname(entry);
190 	mode_t entrymode = archive_entry_mode(entry);
191 	alpm_backup_t *backup = _alpm_needbackup(entryname, newpkg);
192 	char filename[PATH_MAX]; /* the actual file we're extracting */
193 	int needbackup = 0, notouch = 0;
194 	const char *hash_orig = NULL;
195 	int isnewfile = 0, errors = 0;
196 	struct stat lsbuf;
197 	size_t filename_len;
198 
199 	if(*entryname == '.') {
200 		return extract_db_file(handle, archive, entry, newpkg, entryname);
201 	}
202 
203 	if (!alpm_filelist_contains(&newpkg->files, entryname)) {
204 		_alpm_log(handle, ALPM_LOG_WARNING,
205 				_("file not found in file list for package %s. skipping extraction of %s\n"),
206 				newpkg->name, entryname);
207 		return 0;
208 	}
209 
210 	/* build the new entryname relative to handle->root */
211 	filename_len = snprintf(filename, PATH_MAX, "%s%s", handle->root, entryname);
212 	if(filename_len >= PATH_MAX) {
213 		_alpm_log(handle, ALPM_LOG_ERROR,
214 				_("unable to extract %s%s: path too long"), handle->root, entryname);
215 		return 1;
216 	}
217 
218 	/* if a file is in NoExtract then we never extract it */
219 	if(_alpm_fnmatch_patterns(handle->noextract, entryname) == 0) {
220 		_alpm_log(handle, ALPM_LOG_DEBUG, "%s is in NoExtract,"
221 				" skipping extraction of %s\n",
222 				entryname, filename);
223 		archive_read_data_skip(archive);
224 		return 0;
225 	}
226 
227 	/* Check for file existence. This is one of the more crucial parts
228 	 * to get 'right'. Here are the possibilities, with the filesystem
229 	 * on the left and the package on the top:
230 	 * (F=file, N=node, S=symlink, D=dir)
231 	 *               |  F/N  |   D
232 	 *  non-existent |   1   |   2
233 	 *  F/N          |   3   |   4
234 	 *  D            |   5   |   6
235 	 *
236 	 *  1,2- extract, no magic necessary. lstat (llstat) will fail here.
237 	 *  3,4- conflict checks should have caught this. either overwrite
238 	 *      or backup the file.
239 	 *  5- file replacing directory- don't allow it.
240 	 *  6- skip extraction, dir already exists.
241 	 */
242 
243 	isnewfile = llstat(filename, &lsbuf) != 0;
244 	if(isnewfile) {
245 		/* cases 1,2: file doesn't exist, skip all backup checks */
246 	} else if(S_ISDIR(lsbuf.st_mode) && S_ISDIR(entrymode)) {
247 #if 0
248 		uid_t entryuid = archive_entry_uid(entry);
249 		gid_t entrygid = archive_entry_gid(entry);
250 #endif
251 
252 		/* case 6: existing dir, ignore it */
253 		if(lsbuf.st_mode != entrymode) {
254 			/* if filesystem perms are different than pkg perms, warn user */
255 			mode_t mask = 07777;
256 			_alpm_log(handle, ALPM_LOG_WARNING, _("directory permissions differ on %s\n"
257 					"filesystem: %o  package: %o\n"), filename, lsbuf.st_mode & mask,
258 					entrymode & mask);
259 			alpm_logaction(handle, ALPM_CALLER_PREFIX,
260 					"warning: directory permissions differ on %s\n"
261 					"filesystem: %o  package: %o\n", filename, lsbuf.st_mode & mask,
262 					entrymode & mask);
263 		}
264 
265 #if 0
266 		/* Disable this warning until our user management in packages has improved.
267 		   Currently many packages have to create users in post_install and chown the
268 		   directories. These all resulted in "false-positive" warnings. */
269 
270 		if((entryuid != lsbuf.st_uid) || (entrygid != lsbuf.st_gid)) {
271 			_alpm_log(handle, ALPM_LOG_WARNING, _("directory ownership differs on %s\n"
272 					"filesystem: %u:%u  package: %u:%u\n"), filename,
273 					lsbuf.st_uid, lsbuf.st_gid, entryuid, entrygid);
274 			alpm_logaction(handle, ALPM_CALLER_PREFIX,
275 					"warning: directory ownership differs on %s\n"
276 					"filesystem: %u:%u  package: %u:%u\n", filename,
277 					lsbuf.st_uid, lsbuf.st_gid, entryuid, entrygid);
278 		}
279 #endif
280 
281 		_alpm_log(handle, ALPM_LOG_DEBUG, "extract: skipping dir extraction of %s\n",
282 				filename);
283 		archive_read_data_skip(archive);
284 		return 0;
285 	} else if(S_ISDIR(lsbuf.st_mode)) {
286 		/* case 5: trying to overwrite dir with file, don't allow it */
287 		_alpm_log(handle, ALPM_LOG_ERROR, _("extract: not overwriting dir with file %s\n"),
288 				filename);
289 		archive_read_data_skip(archive);
290 		return 1;
291 	} else if(S_ISDIR(entrymode)) {
292 		/* case 4: trying to overwrite file with dir */
293 		_alpm_log(handle, ALPM_LOG_DEBUG, "extract: overwriting file with dir %s\n",
294 				filename);
295 	} else {
296 		/* case 3: trying to overwrite file with file */
297 		/* if file is in NoUpgrade, don't touch it */
298 		if(_alpm_fnmatch_patterns(handle->noupgrade, entryname) == 0) {
299 			notouch = 1;
300 		} else {
301 			alpm_backup_t *oldbackup;
302 			if(oldpkg && (oldbackup = _alpm_needbackup(entryname, oldpkg))) {
303 				hash_orig = oldbackup->hash;
304 				needbackup = 1;
305 			} else if(backup) {
306 				/* allow adding backup files retroactively */
307 				needbackup = 1;
308 			}
309 		}
310 	}
311 
312 	if(notouch || needbackup) {
313 		if(filename_len + strlen(".pacnew") >= PATH_MAX) {
314 			_alpm_log(handle, ALPM_LOG_ERROR,
315 					_("unable to extract %s.pacnew: path too long"), filename);
316 			return 1;
317 		}
318 		strcpy(filename + filename_len, ".pacnew");
319 		isnewfile = (llstat(filename, &lsbuf) != 0 && errno == ENOENT);
320 	}
321 
322 	_alpm_log(handle, ALPM_LOG_DEBUG, "extracting %s\n", filename);
323 	if(perform_extraction(handle, archive, entry, filename)) {
324 		errors++;
325 		return errors;
326 	}
327 
328 	if(backup) {
329 		FREE(backup->hash);
330 		backup->hash = alpm_compute_md5sum(filename);
331 	}
332 
333 	if(notouch) {
334 		alpm_event_pacnew_created_t event = {
335 			.type = ALPM_EVENT_PACNEW_CREATED,
336 			.from_noupgrade = 1,
337 			.oldpkg = oldpkg,
338 			.newpkg = newpkg,
339 			.file = filename
340 		};
341 		/* "remove" the .pacnew suffix */
342 		filename[filename_len] = '\0';
343 		EVENT(handle, &event);
344 		alpm_logaction(handle, ALPM_CALLER_PREFIX,
345 				"warning: %s installed as %s.pacnew\n", filename, filename);
346 	} else if(needbackup) {
347 		char *hash_local = NULL, *hash_pkg = NULL;
348 		char origfile[PATH_MAX] = "";
349 
350 		strncat(origfile, filename, filename_len);
351 
352 		hash_local = alpm_compute_md5sum(origfile);
353 		hash_pkg = backup ? backup->hash : alpm_compute_md5sum(filename);
354 
355 		_alpm_log(handle, ALPM_LOG_DEBUG, "checking hashes for %s\n", origfile);
356 		_alpm_log(handle, ALPM_LOG_DEBUG, "current:  %s\n", hash_local);
357 		_alpm_log(handle, ALPM_LOG_DEBUG, "new:      %s\n", hash_pkg);
358 		_alpm_log(handle, ALPM_LOG_DEBUG, "original: %s\n", hash_orig);
359 
360 		if(hash_local && hash_pkg && strcmp(hash_local, hash_pkg) == 0) {
361 			/* local and new files are the same, updating anyway to get
362 			 * correct timestamps */
363 			_alpm_log(handle, ALPM_LOG_DEBUG, "action: installing new file: %s\n",
364 					origfile);
365 			if(try_rename(handle, filename, origfile)) {
366 				errors++;
367 			}
368 		} else if(hash_orig && hash_pkg && strcmp(hash_orig, hash_pkg) == 0) {
369 			/* original and new files are the same, leave the local version alone,
370 			 * including any user changes */
371 			_alpm_log(handle, ALPM_LOG_DEBUG,
372 					"action: leaving existing file in place\n");
373 			if(isnewfile) {
374 				unlink(filename);
375 			}
376 		} else if(hash_orig && hash_local && strcmp(hash_orig, hash_local) == 0) {
377 			/* installed file has NOT been changed by user,
378 			 * update to the new version */
379 			_alpm_log(handle, ALPM_LOG_DEBUG, "action: installing new file: %s\n",
380 					origfile);
381 			if(try_rename(handle, filename, origfile)) {
382 				errors++;
383 			}
384 		} else {
385 			/* none of the three files matched another,  leave the unpacked
386 			 * file alongside the local file */
387 			alpm_event_pacnew_created_t event = {
388 				.type = ALPM_EVENT_PACNEW_CREATED,
389 				.from_noupgrade = 0,
390 				.oldpkg = oldpkg,
391 				.newpkg = newpkg,
392 				.file = origfile
393 			};
394 			_alpm_log(handle, ALPM_LOG_DEBUG,
395 					"action: keeping current file and installing"
396 					" new one with .pacnew ending\n");
397 			EVENT(handle, &event);
398 			alpm_logaction(handle, ALPM_CALLER_PREFIX,
399 					"warning: %s installed as %s\n", origfile, filename);
400 		}
401 
402 		free(hash_local);
403 		if(!backup) {
404 			free(hash_pkg);
405 		}
406 	}
407 	return errors;
408 }
409 
commit_single_pkg(alpm_handle_t * handle,alpm_pkg_t * newpkg,size_t pkg_current,size_t pkg_count)410 static int commit_single_pkg(alpm_handle_t *handle, alpm_pkg_t *newpkg,
411 		size_t pkg_current, size_t pkg_count)
412 {
413 	int i, ret = 0, errors = 0;
414 	int is_upgrade = 0;
415 	alpm_pkg_t *oldpkg = NULL;
416 	alpm_db_t *db = handle->db_local;
417 	alpm_trans_t *trans = handle->trans;
418 	alpm_progress_t progress = ALPM_PROGRESS_ADD_START;
419 	alpm_event_package_operation_t event;
420 	const char *log_msg = "adding";
421 	const char *pkgfile;
422 	struct archive *archive;
423 	struct archive_entry *entry;
424 	int fd, cwdfd;
425 	struct stat buf;
426 
427 	ASSERT(trans != NULL, return -1);
428 
429 	/* see if this is an upgrade. if so, remove the old package first */
430 	if((oldpkg = newpkg->oldpkg)) {
431 		int cmp = _alpm_pkg_compare_versions(newpkg, oldpkg);
432 		if(cmp < 0) {
433 			log_msg = "downgrading";
434 			progress = ALPM_PROGRESS_DOWNGRADE_START;
435 			event.operation = ALPM_PACKAGE_DOWNGRADE;
436 		} else if(cmp == 0) {
437 			log_msg = "reinstalling";
438 			progress = ALPM_PROGRESS_REINSTALL_START;
439 			event.operation = ALPM_PACKAGE_REINSTALL;
440 		} else {
441 			log_msg = "upgrading";
442 			progress = ALPM_PROGRESS_UPGRADE_START;
443 			event.operation = ALPM_PACKAGE_UPGRADE;
444 		}
445 		is_upgrade = 1;
446 
447 		/* copy over the install reason */
448 		newpkg->reason = alpm_pkg_get_reason(oldpkg);
449 	} else {
450 		event.operation = ALPM_PACKAGE_INSTALL;
451 	}
452 
453 	event.type = ALPM_EVENT_PACKAGE_OPERATION_START;
454 	event.oldpkg = oldpkg;
455 	event.newpkg = newpkg;
456 	EVENT(handle, &event);
457 
458 	pkgfile = newpkg->origin_data.file;
459 
460 	_alpm_log(handle, ALPM_LOG_DEBUG, "%s package %s-%s\n",
461 			log_msg, newpkg->name, newpkg->version);
462 		/* pre_install/pre_upgrade scriptlet */
463 	if(alpm_pkg_has_scriptlet(newpkg) &&
464 			!(trans->flags & ALPM_TRANS_FLAG_NOSCRIPTLET)) {
465 		const char *scriptlet_name = is_upgrade ? "pre_upgrade" : "pre_install";
466 
467 		_alpm_runscriptlet(handle, pkgfile, scriptlet_name,
468 				newpkg->version, oldpkg ? oldpkg->version : NULL, 1);
469 	}
470 
471 	/* we override any pre-set reason if we have alldeps or allexplicit set */
472 	if(trans->flags & ALPM_TRANS_FLAG_ALLDEPS) {
473 		newpkg->reason = ALPM_PKG_REASON_DEPEND;
474 	} else if(trans->flags & ALPM_TRANS_FLAG_ALLEXPLICIT) {
475 		newpkg->reason = ALPM_PKG_REASON_EXPLICIT;
476 	}
477 
478 	if(oldpkg) {
479 		/* set up fake remove transaction */
480 		if(_alpm_remove_single_package(handle, oldpkg, newpkg, 0, 0) == -1) {
481 			handle->pm_errno = ALPM_ERR_TRANS_ABORT;
482 			ret = -1;
483 			goto cleanup;
484 		}
485 	}
486 
487 	/* prepare directory for database entries so permissions are correct after
488 	   changelog/install script installation */
489 	if(_alpm_local_db_prepare(db, newpkg)) {
490 		alpm_logaction(handle, ALPM_CALLER_PREFIX,
491 				"error: could not create database entry %s-%s\n",
492 				newpkg->name, newpkg->version);
493 		handle->pm_errno = ALPM_ERR_DB_WRITE;
494 		ret = -1;
495 		goto cleanup;
496 	}
497 
498 	fd = _alpm_open_archive(db->handle, pkgfile, &buf,
499 			&archive, ALPM_ERR_PKG_OPEN);
500 	if(fd < 0) {
501 		ret = -1;
502 		goto cleanup;
503 	}
504 
505 	/* save the cwd so we can restore it later */
506 	OPEN(cwdfd, ".", O_RDONLY | O_CLOEXEC);
507 	if(cwdfd < 0) {
508 		_alpm_log(handle, ALPM_LOG_ERROR, _("could not get current working directory\n"));
509 	}
510 
511 	/* libarchive requires this for extracting hard links */
512 	if(chdir(handle->root) != 0) {
513 		_alpm_log(handle, ALPM_LOG_ERROR, _("could not change directory to %s (%s)\n"),
514 				handle->root, strerror(errno));
515 		_alpm_archive_read_free(archive);
516 		if(cwdfd >= 0) {
517 			close(cwdfd);
518 		}
519 		close(fd);
520 		ret = -1;
521 		goto cleanup;
522 	}
523 
524 	if(trans->flags & ALPM_TRANS_FLAG_DBONLY) {
525 		_alpm_log(handle, ALPM_LOG_DEBUG, "extracting db files\n");
526 		while(archive_read_next_header(archive, &entry) == ARCHIVE_OK) {
527 			const char *entryname = archive_entry_pathname(entry);
528 			if(entryname[0] == '.') {
529 				errors += extract_db_file(handle, archive, entry, newpkg, entryname);
530 			} else {
531 				archive_read_data_skip(archive);
532 			}
533 		}
534 	} else {
535 		_alpm_log(handle, ALPM_LOG_DEBUG, "extracting files\n");
536 
537 		/* call PROGRESS once with 0 percent, as we sort-of skip that here */
538 		PROGRESS(handle, progress, newpkg->name, 0, pkg_count, pkg_current);
539 
540 		for(i = 0; archive_read_next_header(archive, &entry) == ARCHIVE_OK; i++) {
541 			int percent;
542 
543 			if(newpkg->size != 0) {
544 				/* Using compressed size for calculations here, as newpkg->isize is not
545 				 * exact when it comes to comparing to the ACTUAL uncompressed size
546 				 * (missing metadata sizes) */
547 				int64_t pos = _alpm_archive_compressed_ftell(archive);
548 				percent = (pos * 100) / newpkg->size;
549 				if(percent >= 100) {
550 					percent = 100;
551 				}
552 			} else {
553 				percent = 0;
554 			}
555 
556 			PROGRESS(handle, progress, newpkg->name, percent, pkg_count, pkg_current);
557 
558 			/* extract the next file from the archive */
559 			errors += extract_single_file(handle, archive, entry, newpkg, oldpkg);
560 		}
561 	}
562 
563 	_alpm_archive_read_free(archive);
564 	close(fd);
565 
566 	/* restore the old cwd if we have it */
567 	if(cwdfd >= 0) {
568 		if(fchdir(cwdfd) != 0) {
569 			_alpm_log(handle, ALPM_LOG_ERROR,
570 					_("could not restore working directory (%s)\n"), strerror(errno));
571 		}
572 		close(cwdfd);
573 	}
574 
575 	if(errors) {
576 		ret = -1;
577 		if(is_upgrade) {
578 			_alpm_log(handle, ALPM_LOG_ERROR, _("problem occurred while upgrading %s\n"),
579 					newpkg->name);
580 			alpm_logaction(handle, ALPM_CALLER_PREFIX,
581 					"error: problem occurred while upgrading %s\n",
582 					newpkg->name);
583 		} else {
584 			_alpm_log(handle, ALPM_LOG_ERROR, _("problem occurred while installing %s\n"),
585 					newpkg->name);
586 			alpm_logaction(handle, ALPM_CALLER_PREFIX,
587 					"error: problem occurred while installing %s\n",
588 					newpkg->name);
589 		}
590 	}
591 
592 	/* make an install date (in UTC) */
593 	newpkg->installdate = time(NULL);
594 
595 	_alpm_log(handle, ALPM_LOG_DEBUG, "updating database\n");
596 	_alpm_log(handle, ALPM_LOG_DEBUG, "adding database entry '%s'\n", newpkg->name);
597 
598 	if(_alpm_local_db_write(db, newpkg, INFRQ_ALL)) {
599 		_alpm_log(handle, ALPM_LOG_ERROR, _("could not update database entry %s-%s\n"),
600 				newpkg->name, newpkg->version);
601 		alpm_logaction(handle, ALPM_CALLER_PREFIX,
602 				"error: could not update database entry %s-%s\n",
603 				newpkg->name, newpkg->version);
604 		handle->pm_errno = ALPM_ERR_DB_WRITE;
605 		ret = -1;
606 		goto cleanup;
607 	}
608 
609 	if(_alpm_db_add_pkgincache(db, newpkg) == -1) {
610 		_alpm_log(handle, ALPM_LOG_ERROR, _("could not add entry '%s' in cache\n"),
611 				newpkg->name);
612 	}
613 
614 	PROGRESS(handle, progress, newpkg->name, 100, pkg_count, pkg_current);
615 
616 	switch(event.operation) {
617 		case ALPM_PACKAGE_INSTALL:
618 			alpm_logaction(handle, ALPM_CALLER_PREFIX, "installed %s (%s)\n",
619 					newpkg->name, newpkg->version);
620 			break;
621 		case ALPM_PACKAGE_DOWNGRADE:
622 			alpm_logaction(handle, ALPM_CALLER_PREFIX, "downgraded %s (%s -> %s)\n",
623 					newpkg->name, oldpkg->version, newpkg->version);
624 			break;
625 		case ALPM_PACKAGE_REINSTALL:
626 			alpm_logaction(handle, ALPM_CALLER_PREFIX, "reinstalled %s (%s)\n",
627 					newpkg->name, newpkg->version);
628 			break;
629 		case ALPM_PACKAGE_UPGRADE:
630 			alpm_logaction(handle, ALPM_CALLER_PREFIX, "upgraded %s (%s -> %s)\n",
631 					newpkg->name, oldpkg->version, newpkg->version);
632 			break;
633 		default:
634 			/* we should never reach here */
635 			break;
636 	}
637 
638 	/* run the post-install script if it exists */
639 	if(alpm_pkg_has_scriptlet(newpkg)
640 			&& !(trans->flags & ALPM_TRANS_FLAG_NOSCRIPTLET)) {
641 		char *scriptlet = _alpm_local_db_pkgpath(db, newpkg, "install");
642 		const char *scriptlet_name = is_upgrade ? "post_upgrade" : "post_install";
643 
644 		_alpm_runscriptlet(handle, scriptlet, scriptlet_name,
645 				newpkg->version, oldpkg ? oldpkg->version : NULL, 0);
646 		free(scriptlet);
647 	}
648 
649 	event.type = ALPM_EVENT_PACKAGE_OPERATION_DONE;
650 	EVENT(handle, &event);
651 
652 cleanup:
653 	return ret;
654 }
655 
_alpm_upgrade_packages(alpm_handle_t * handle)656 int _alpm_upgrade_packages(alpm_handle_t *handle)
657 {
658 	size_t pkg_count, pkg_current;
659 	int skip_ldconfig = 0, ret = 0;
660 	alpm_list_t *targ;
661 	alpm_trans_t *trans = handle->trans;
662 
663 	if(trans->add == NULL) {
664 		return 0;
665 	}
666 
667 	pkg_count = alpm_list_count(trans->add);
668 	pkg_current = 1;
669 
670 	/* loop through our package list adding/upgrading one at a time */
671 	for(targ = trans->add; targ; targ = targ->next) {
672 		alpm_pkg_t *newpkg = targ->data;
673 
674 		if(handle->trans->state == STATE_INTERRUPTED) {
675 			return ret;
676 		}
677 
678 		if(commit_single_pkg(handle, newpkg, pkg_current, pkg_count)) {
679 			/* something screwed up on the commit, abort the trans */
680 			trans->state = STATE_INTERRUPTED;
681 			handle->pm_errno = ALPM_ERR_TRANS_ABORT;
682 			/* running ldconfig at this point could possibly screw system */
683 			skip_ldconfig = 1;
684 			ret = -1;
685 		}
686 
687 		pkg_current++;
688 	}
689 
690 	if(!skip_ldconfig) {
691 		/* run ldconfig if it exists */
692 		_alpm_ldconfig(handle);
693 	}
694 
695 	return ret;
696 }
697