Attempt to stop installation when we encounter problems
[pacman-ng.git] / lib / libalpm / add.c
blobb0b5a534569d00fd5f21a0a99822c842c40dbf65
1 /*
2 * add.c
4 * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org>
5 * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
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.
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.
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/>.
21 #include "config.h"
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <time.h>
26 #include <string.h>
27 #include <limits.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 #include <inttypes.h> /* int64_t */
32 #include <stdint.h> /* intmax_t */
34 /* libarchive */
35 #include <archive.h>
36 #include <archive_entry.h>
38 /* libalpm */
39 #include "add.h"
40 #include "alpm_list.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 "conflict.h"
48 #include "deps.h"
49 #include "remove.h"
50 #include "handle.h"
52 /** Add a file target to the transaction.
53 * @param target the name of the file target to add
54 * @return 0 on success, -1 on error (pm_errno is set accordingly)
56 int SYMEXPORT alpm_add_target(char *target)
58 pmpkg_t *pkg = NULL;
59 const char *pkgname, *pkgver;
60 alpm_list_t *i;
61 pmtrans_t *trans;
63 ALPM_LOG_FUNC;
65 /* Sanity checks */
66 ASSERT(target != NULL && strlen(target) != 0, RET_ERR(PM_ERR_WRONG_ARGS, -1));
67 ASSERT(handle != NULL, RET_ERR(PM_ERR_HANDLE_NULL, -1));
68 trans = handle->trans;
69 ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
70 ASSERT(trans->state == STATE_INITIALIZED, RET_ERR(PM_ERR_TRANS_NOT_INITIALIZED, -1));
71 ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
73 _alpm_log(PM_LOG_DEBUG, "loading target '%s'\n", target);
75 if(alpm_pkg_load(target, 1, &pkg) != 0) {
76 goto error;
78 pkgname = alpm_pkg_get_name(pkg);
79 pkgver = alpm_pkg_get_version(pkg);
81 /* check if an older version of said package is already in transaction
82 * packages. if so, replace it in the list */
83 for(i = trans->add; i; i = i->next) {
84 pmpkg_t *transpkg = i->data;
85 if(strcmp(transpkg->name, pkgname) == 0) {
86 if(alpm_pkg_vercmp(transpkg->version, pkgver) < 0) {
87 _alpm_log(PM_LOG_WARNING,
88 _("replacing older version %s-%s by %s in target list\n"),
89 transpkg->name, transpkg->version, pkgver);
90 _alpm_pkg_free(i->data);
91 i->data = pkg;
92 } else {
93 _alpm_log(PM_LOG_WARNING,
94 _("skipping %s-%s because newer version %s is in target list\n"),
95 pkgname, pkgver, transpkg->version);
96 _alpm_pkg_free(pkg);
98 return(0);
102 /* add the package to the transaction */
103 trans->add = alpm_list_add(trans->add, pkg);
105 return(0);
107 error:
108 _alpm_pkg_free(pkg);
109 return(-1);
112 static int perform_extraction(struct archive *archive,
113 struct archive_entry *entry, const char *filename, const char *origname)
115 int ret;
116 const int archive_flags = ARCHIVE_EXTRACT_OWNER |
117 ARCHIVE_EXTRACT_PERM |
118 ARCHIVE_EXTRACT_TIME;
120 archive_entry_set_pathname(entry, filename);
122 ret = archive_read_extract(archive, entry, archive_flags);
123 if(ret == ARCHIVE_WARN && archive_errno(archive) != ENOSPC) {
124 /* operation succeeded but a "non-critical" error was encountered */
125 _alpm_log(PM_LOG_WARNING, _("warning given when extracting %s (%s)\n"),
126 origname, archive_error_string(archive));
127 } else if(ret != ARCHIVE_OK) {
128 _alpm_log(PM_LOG_ERROR, _("could not extract %s (%s)\n"),
129 origname, archive_error_string(archive));
130 alpm_logaction("error: could not extract %s (%s)\n",
131 origname, archive_error_string(archive));
132 return(1);
134 return(0);
137 static int extract_single_file(struct archive *archive,
138 struct archive_entry *entry, pmpkg_t *newpkg, pmpkg_t *oldpkg,
139 pmtrans_t *trans, pmdb_t *db)
141 const char *entryname;
142 mode_t entrymode;
143 char filename[PATH_MAX]; /* the actual file we're extracting */
144 int needbackup = 0, notouch = 0;
145 char *hash_orig = NULL;
146 char *entryname_orig = NULL;
147 int errors = 0;
149 entryname = archive_entry_pathname(entry);
150 entrymode = archive_entry_mode(entry);
152 memset(filename, 0, PATH_MAX); /* just to be sure */
154 if(strcmp(entryname, ".INSTALL") == 0) {
155 /* the install script goes inside the db */
156 snprintf(filename, PATH_MAX, "%s%s-%s/install",
157 _alpm_db_path(db), newpkg->name, newpkg->version);
158 archive_entry_set_perm(entry, 0644);
159 } else if(strcmp(entryname, ".CHANGELOG") == 0) {
160 /* the changelog goes inside the db */
161 snprintf(filename, PATH_MAX, "%s%s-%s/changelog",
162 _alpm_db_path(db), newpkg->name, newpkg->version);
163 archive_entry_set_perm(entry, 0644);
164 } else if(*entryname == '.') {
165 /* for now, ignore all files starting with '.' that haven't
166 * already been handled (for future possibilities) */
167 _alpm_log(PM_LOG_DEBUG, "skipping extraction of '%s'\n", entryname);
168 archive_read_data_skip(archive);
169 return(0);
170 } else {
171 /* build the new entryname relative to handle->root */
172 snprintf(filename, PATH_MAX, "%s%s", handle->root, entryname);
175 /* if a file is in NoExtract then we never extract it */
176 if(alpm_list_find_str(handle->noextract, entryname)) {
177 _alpm_log(PM_LOG_DEBUG, "%s is in NoExtract, skipping extraction\n",
178 entryname);
179 alpm_logaction("note: %s is in NoExtract, skipping extraction\n",
180 entryname);
181 archive_read_data_skip(archive);
182 return(0);
185 /* if a file is in the add skiplist we never extract it */
186 if(alpm_list_find_str(trans->skip_add, filename)) {
187 _alpm_log(PM_LOG_DEBUG, "%s is in trans->skip_add, skipping extraction\n",
188 entryname);
189 archive_read_data_skip(archive);
190 return(0);
193 /* Check for file existence. This is one of the more crucial parts
194 * to get 'right'. Here are the possibilities, with the filesystem
195 * on the left and the package on the top:
196 * (F=file, N=node, S=symlink, D=dir)
197 * | F/N | S | D
198 * non-existent | 1 | 2 | 3
199 * F/N | 4 | 5 | 6
200 * S | 7 | 8 | 9
201 * D | 10 | 11 | 12
203 * 1,2,3- extract, no magic necessary. lstat (_alpm_lstat) will fail here.
204 * 4,5,6,7,8- conflict checks should have caught this. either overwrite
205 * or backup the file.
206 * 9- follow the symlink, hopefully it is a directory, check it.
207 * 10- file replacing directory- don't allow it.
208 * 11- don't extract symlink- a dir exists here. we don't want links to
209 * links, etc.
210 * 12- skip extraction, dir already exists.
213 /* do both a lstat and a stat, so we can see what symlinks point to */
214 struct stat lsbuf, sbuf;
215 if(_alpm_lstat(filename, &lsbuf) != 0 || stat(filename, &sbuf) != 0) {
216 /* cases 1,2,3: couldn't stat an existing file, skip all backup checks */
217 } else {
218 if(S_ISDIR(lsbuf.st_mode)) {
219 if(S_ISDIR(entrymode)) {
220 /* case 12: existing dir, ignore it */
221 if(lsbuf.st_mode != entrymode) {
222 /* if filesystem perms are different than pkg perms, warn user */
223 mode_t mask = 07777;
224 _alpm_log(PM_LOG_WARNING, _("directory permissions differ on %s\n"
225 "filesystem: %o package: %o\n"), entryname, lsbuf.st_mode & mask,
226 entrymode & mask);
227 alpm_logaction("warning: directory permissions differ on %s\n"
228 "filesystem: %o package: %o\n", entryname, lsbuf.st_mode & mask,
229 entrymode & mask);
231 _alpm_log(PM_LOG_DEBUG, "extract: skipping dir extraction of %s\n",
232 entryname);
233 archive_read_data_skip(archive);
234 return(0);
235 } else {
236 /* case 10/11: trying to overwrite dir with file/symlink, don't allow it */
237 _alpm_log(PM_LOG_ERROR, _("extract: not overwriting dir with file %s\n"),
238 entryname);
239 archive_read_data_skip(archive);
240 return(1);
242 } else if(S_ISLNK(lsbuf.st_mode) && S_ISDIR(entrymode)) {
243 /* case 9: existing symlink, dir in package */
244 if(S_ISDIR(sbuf.st_mode)) {
245 /* the symlink on FS is to a directory, so we'll use it */
246 _alpm_log(PM_LOG_DEBUG, "extract: skipping symlink overwrite of %s\n",
247 entryname);
248 archive_read_data_skip(archive);
249 return(0);
250 } else {
251 /* this is BAD. symlink was not to a directory */
252 _alpm_log(PM_LOG_ERROR, _("extract: symlink %s does not point to dir\n"),
253 entryname);
254 archive_read_data_skip(archive);
255 return(1);
257 } else if(S_ISREG(lsbuf.st_mode) && S_ISDIR(entrymode)) {
258 /* case 6: trying to overwrite file with dir */
259 _alpm_log(PM_LOG_DEBUG, "extract: overwriting file with dir %s\n",
260 entryname);
261 } else if(S_ISREG(entrymode)) {
262 /* case 4,7: */
263 /* if file is in NoUpgrade, don't touch it */
264 if(alpm_list_find_str(handle->noupgrade, entryname)) {
265 notouch = 1;
266 } else {
267 /* go to the backup array and see if our conflict is there */
268 /* check newpkg first, so that adding backup files is retroactive */
269 if(alpm_list_find_str(alpm_pkg_get_backup(newpkg), entryname) != NULL) {
270 needbackup = 1;
273 /* check oldpkg for a backup entry, store the hash if available */
274 if(oldpkg) {
275 hash_orig = _alpm_needbackup(entryname, alpm_pkg_get_backup(oldpkg));
276 if(hash_orig) {
277 needbackup = 1;
281 /* if we force hash_orig to be non-NULL retroactive backup works */
282 if(needbackup && !hash_orig) {
283 STRDUP(hash_orig, "", RET_ERR(PM_ERR_MEMORY, -1));
287 /* else if(S_ISLNK(entrymode)) */
288 /* case 5,8: don't need to do anything special */
291 /* we need access to the original entryname later after calls to
292 * archive_entry_set_pathname(), so we need to dupe it and free() later */
293 STRDUP(entryname_orig, entryname, RET_ERR(PM_ERR_MEMORY, -1));
295 if(needbackup) {
296 char checkfile[PATH_MAX];
297 char *hash_local = NULL, *hash_pkg = NULL;
298 int ret;
300 snprintf(checkfile, PATH_MAX, "%s.paccheck", filename);
302 ret = perform_extraction(archive, entry, checkfile, entryname_orig);
303 if(ret == 1) {
304 /* error */
305 FREE(hash_orig);
306 FREE(entryname_orig);
307 return(1);
310 hash_local = alpm_compute_md5sum(filename);
311 hash_pkg = alpm_compute_md5sum(checkfile);
313 /* append the new md5 hash to it's respective entry
314 * in newpkg's backup (it will be the new orginal) */
315 alpm_list_t *backups;
316 for(backups = alpm_pkg_get_backup(newpkg); backups;
317 backups = alpm_list_next(backups)) {
318 char *oldbackup = alpm_list_getdata(backups);
319 if(!oldbackup || strcmp(oldbackup, entryname_orig) != 0) {
320 continue;
322 char *backup = NULL;
323 /* length is tab char, null byte and MD5 (32 char) */
324 size_t backup_len = strlen(oldbackup) + 34;
325 MALLOC(backup, backup_len, RET_ERR(PM_ERR_MEMORY, -1));
327 sprintf(backup, "%s\t%s", oldbackup, hash_pkg);
328 backup[backup_len-1] = '\0';
329 FREE(oldbackup);
330 backups->data = backup;
333 _alpm_log(PM_LOG_DEBUG, "checking hashes for %s\n", entryname_orig);
334 _alpm_log(PM_LOG_DEBUG, "current: %s\n", hash_local);
335 _alpm_log(PM_LOG_DEBUG, "new: %s\n", hash_pkg);
336 _alpm_log(PM_LOG_DEBUG, "original: %s\n", hash_orig);
338 if(!oldpkg) {
339 if(strcmp(hash_local, hash_pkg) != 0) {
340 /* looks like we have a local file that has a different hash as the
341 * file in the package, move it to a .pacorig */
342 char newpath[PATH_MAX];
343 snprintf(newpath, PATH_MAX, "%s.pacorig", filename);
345 /* move the existing file to the "pacorig" */
346 if(rename(filename, newpath)) {
347 _alpm_log(PM_LOG_ERROR, _("could not rename %s to %s (%s)\n"),
348 filename, newpath, strerror(errno));
349 alpm_logaction("error: could not rename %s to %s (%s)\n",
350 filename, newpath, strerror(errno));
351 errors++;
352 } else {
353 /* rename the file we extracted to the real name */
354 if(rename(checkfile, filename)) {
355 _alpm_log(PM_LOG_ERROR, _("could not rename %s to %s (%s)\n"),
356 checkfile, filename, strerror(errno));
357 alpm_logaction("error: could not rename %s to %s (%s)\n",
358 checkfile, filename, strerror(errno));
359 errors++;
360 } else {
361 _alpm_log(PM_LOG_WARNING, _("%s saved as %s\n"), filename, newpath);
362 alpm_logaction("warning: %s saved as %s\n", filename, newpath);
365 } else {
366 /* local file is identical to pkg one, so just remove pkg one */
367 unlink(checkfile);
369 } else if(hash_orig) {
370 /* the fun part */
372 if(strcmp(hash_orig, hash_local) == 0) {
373 /* installed file has NOT been changed by user */
374 if(strcmp(hash_orig, hash_pkg) != 0) {
375 _alpm_log(PM_LOG_DEBUG, "action: installing new file: %s\n",
376 entryname_orig);
378 if(rename(checkfile, filename)) {
379 _alpm_log(PM_LOG_ERROR, _("could not rename %s to %s (%s)\n"),
380 checkfile, filename, strerror(errno));
381 alpm_logaction("error: could not rename %s to %s (%s)\n",
382 checkfile, filename, strerror(errno));
383 errors++;
385 } else {
386 /* there's no sense in installing the same file twice, install
387 * ONLY is the original and package hashes differ */
388 _alpm_log(PM_LOG_DEBUG, "action: leaving existing file in place\n");
389 unlink(checkfile);
391 } else if(strcmp(hash_orig, hash_pkg) == 0) {
392 /* originally installed file and new file are the same - this
393 * implies the case above failed - i.e. the file was changed by a
394 * user */
395 _alpm_log(PM_LOG_DEBUG, "action: leaving existing file in place\n");
396 unlink(checkfile);
397 } else if(strcmp(hash_local, hash_pkg) == 0) {
398 /* this would be magical. The above two cases failed, but the
399 * user changes just so happened to make the new file exactly the
400 * same as the one in the package... skip it */
401 _alpm_log(PM_LOG_DEBUG, "action: leaving existing file in place\n");
402 unlink(checkfile);
403 } else {
404 char newpath[PATH_MAX];
405 _alpm_log(PM_LOG_DEBUG, "action: keeping current file and installing"
406 " new one with .pacnew ending\n");
407 snprintf(newpath, PATH_MAX, "%s.pacnew", filename);
408 if(rename(checkfile, newpath)) {
409 _alpm_log(PM_LOG_ERROR, _("could not install %s as %s (%s)\n"),
410 filename, newpath, strerror(errno));
411 alpm_logaction("error: could not install %s as %s (%s)\n",
412 filename, newpath, strerror(errno));
413 } else {
414 _alpm_log(PM_LOG_WARNING, _("%s installed as %s\n"),
415 filename, newpath);
416 alpm_logaction("warning: %s installed as %s\n",
417 filename, newpath);
422 FREE(hash_local);
423 FREE(hash_pkg);
424 FREE(hash_orig);
425 } else {
426 int ret;
428 /* we didn't need a backup */
429 if(notouch) {
430 /* change the path to a .pacnew extension */
431 _alpm_log(PM_LOG_DEBUG, "%s is in NoUpgrade -- skipping\n", filename);
432 _alpm_log(PM_LOG_WARNING, _("extracting %s as %s.pacnew\n"), filename, filename);
433 alpm_logaction("warning: extracting %s as %s.pacnew\n", filename, filename);
434 strncat(filename, ".pacnew", PATH_MAX - strlen(filename));
435 } else {
436 _alpm_log(PM_LOG_DEBUG, "extracting %s\n", filename);
439 if(trans->flags & PM_TRANS_FLAG_FORCE) {
440 /* if FORCE was used, unlink() each file (whether it's there
441 * or not) before extracting. This prevents the old "Text file busy"
442 * error that crops up if forcing a glibc or pacman upgrade. */
443 unlink(filename);
446 ret = perform_extraction(archive, entry, filename, entryname_orig);
447 if(ret == 1) {
448 /* error */
449 FREE(entryname_orig);
450 return(1);
453 /* calculate an hash if this is in newpkg's backup */
454 alpm_list_t *b;
455 for(b = alpm_pkg_get_backup(newpkg); b; b = b->next) {
456 char *backup = NULL, *hash = NULL;
457 char *oldbackup = alpm_list_getdata(b);
458 /* length is tab char, null byte and MD5 (32 char) */
459 size_t backup_len = strlen(oldbackup) + 34;
461 if(!oldbackup || strcmp(oldbackup, entryname_orig) != 0) {
462 continue;
464 _alpm_log(PM_LOG_DEBUG, "appending backup entry for %s\n", filename);
466 hash = alpm_compute_md5sum(filename);
467 MALLOC(backup, backup_len, RET_ERR(PM_ERR_MEMORY, -1));
469 sprintf(backup, "%s\t%s", oldbackup, hash);
470 backup[backup_len-1] = '\0';
471 FREE(hash);
472 FREE(oldbackup);
473 b->data = backup;
476 FREE(entryname_orig);
477 return(errors);
480 static int commit_single_pkg(pmpkg_t *newpkg, int pkg_current, int pkg_count,
481 pmtrans_t *trans, pmdb_t *db)
483 int i, ret = 0, errors = 0;
484 char scriptlet[PATH_MAX+1];
485 int is_upgrade = 0;
486 pmpkg_t *oldpkg = NULL;
488 ALPM_LOG_FUNC;
490 ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
492 snprintf(scriptlet, PATH_MAX, "%s%s-%s/install",
493 _alpm_db_path(db), alpm_pkg_get_name(newpkg),
494 alpm_pkg_get_version(newpkg));
496 /* see if this is an upgrade. if so, remove the old package first */
497 pmpkg_t *local = _alpm_db_get_pkgfromcache(db, newpkg->name);
498 if(local) {
499 is_upgrade = 1;
501 /* we'll need to save some record for backup checks later */
502 oldpkg = _alpm_pkg_dup(local);
503 /* make sure all infos are loaded because the database entry
504 * will be removed soon */
505 _alpm_local_db_read(oldpkg->origin_data.db, oldpkg, INFRQ_ALL);
507 EVENT(trans, PM_TRANS_EVT_UPGRADE_START, newpkg, oldpkg);
508 _alpm_log(PM_LOG_DEBUG, "upgrading package %s-%s\n",
509 newpkg->name, newpkg->version);
511 /* copy over the install reason */
512 newpkg->reason = alpm_pkg_get_reason(oldpkg);
514 /* pre_upgrade scriptlet */
515 if(alpm_pkg_has_scriptlet(newpkg) && !(trans->flags & PM_TRANS_FLAG_NOSCRIPTLET)) {
516 _alpm_runscriptlet(handle->root, newpkg->origin_data.file,
517 "pre_upgrade", newpkg->version, oldpkg->version, trans);
519 } else {
520 is_upgrade = 0;
522 EVENT(trans, PM_TRANS_EVT_ADD_START, newpkg, NULL);
523 _alpm_log(PM_LOG_DEBUG, "adding package %s-%s\n",
524 newpkg->name, newpkg->version);
526 /* pre_install scriptlet */
527 if(alpm_pkg_has_scriptlet(newpkg) && !(trans->flags & PM_TRANS_FLAG_NOSCRIPTLET)) {
528 _alpm_runscriptlet(handle->root, newpkg->origin_data.file,
529 "pre_install", newpkg->version, NULL, trans);
533 /* we override any pre-set reason if we have alldeps or allexplicit set */
534 if(trans->flags & PM_TRANS_FLAG_ALLDEPS) {
535 newpkg->reason = PM_PKG_REASON_DEPEND;
536 } else if(trans->flags & PM_TRANS_FLAG_ALLEXPLICIT) {
537 newpkg->reason = PM_PKG_REASON_EXPLICIT;
540 if(oldpkg) {
541 /* set up fake remove transaction */
542 if(_alpm_upgraderemove_package(oldpkg, newpkg, trans) == -1) {
543 pm_errno = PM_ERR_TRANS_ABORT;
544 ret = -1;
545 goto cleanup;
549 /* prepare directory for database entries so permission are correct after
550 changelog/install script installation (FS#12263) */
551 if(_alpm_local_db_prepare(db, newpkg)) {
552 alpm_logaction("error: could not create database entry %s-%s\n",
553 alpm_pkg_get_name(newpkg), alpm_pkg_get_version(newpkg));
554 pm_errno = PM_ERR_DB_WRITE;
555 ret = -1;
556 goto cleanup;
559 if(!(trans->flags & PM_TRANS_FLAG_DBONLY)) {
560 struct archive *archive;
561 struct archive_entry *entry;
562 char cwd[PATH_MAX] = "";
563 int restore_cwd = 0;
565 _alpm_log(PM_LOG_DEBUG, "extracting files\n");
567 if ((archive = archive_read_new()) == NULL) {
568 pm_errno = PM_ERR_LIBARCHIVE;
569 ret = -1;
570 goto cleanup;
573 archive_read_support_compression_all(archive);
574 archive_read_support_format_all(archive);
576 _alpm_log(PM_LOG_DEBUG, "archive: %s\n", newpkg->origin_data.file);
577 if(archive_read_open_filename(archive, newpkg->origin_data.file,
578 ARCHIVE_DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK) {
579 pm_errno = PM_ERR_PKG_OPEN;
580 ret = -1;
581 goto cleanup;
584 /* save the cwd so we can restore it later */
585 if(getcwd(cwd, PATH_MAX) == NULL) {
586 _alpm_log(PM_LOG_ERROR, _("could not get current working directory\n"));
587 } else {
588 restore_cwd = 1;
591 /* libarchive requires this for extracting hard links */
592 if(chdir(handle->root) != 0) {
593 _alpm_log(PM_LOG_ERROR, _("could not change directory to %s (%s)\n"), handle->root, strerror(errno));
594 ret = -1;
595 goto cleanup;
598 /* call PROGRESS once with 0 percent, as we sort-of skip that here */
599 if(is_upgrade) {
600 PROGRESS(trans, PM_TRANS_PROGRESS_UPGRADE_START,
601 alpm_pkg_get_name(newpkg), 0, pkg_count, pkg_current);
602 } else {
603 PROGRESS(trans, PM_TRANS_PROGRESS_ADD_START,
604 alpm_pkg_get_name(newpkg), 0, pkg_count, pkg_current);
607 for(i = 0; archive_read_next_header(archive, &entry) == ARCHIVE_OK; i++) {
608 double percent;
610 if(newpkg->size != 0) {
611 /* Using compressed size for calculations here, as newpkg->isize is not
612 * exact when it comes to comparing to the ACTUAL uncompressed size
613 * (missing metadata sizes) */
614 int64_t pos = archive_position_compressed(archive);
615 percent = (double)pos / (double)newpkg->size;
616 _alpm_log(PM_LOG_DEBUG, "decompression progress: "
617 "%f%% (%"PRId64" / %jd)\n",
618 percent*100.0, pos, (intmax_t)newpkg->size);
619 if(percent >= 1.0) {
620 percent = 1.0;
622 } else {
623 percent = 0.0;
626 if(is_upgrade) {
627 PROGRESS(trans, PM_TRANS_PROGRESS_UPGRADE_START,
628 alpm_pkg_get_name(newpkg), (int)(percent * 100), pkg_count,
629 pkg_current);
630 } else {
631 PROGRESS(trans, PM_TRANS_PROGRESS_ADD_START,
632 alpm_pkg_get_name(newpkg), (int)(percent * 100), pkg_count,
633 pkg_current);
636 /* extract the next file from the archive */
637 errors += extract_single_file(archive, entry, newpkg, oldpkg,
638 trans, db);
640 archive_read_finish(archive);
642 /* restore the old cwd if we have it */
643 if(restore_cwd && chdir(cwd) != 0) {
644 _alpm_log(PM_LOG_ERROR, _("could not change directory to %s (%s)\n"), cwd, strerror(errno));
647 if(errors) {
648 ret = -1;
649 if(is_upgrade) {
650 _alpm_log(PM_LOG_ERROR, _("problem occurred while upgrading %s\n"),
651 newpkg->name);
652 alpm_logaction("error: problem occurred while upgrading %s\n",
653 newpkg->name);
654 } else {
655 _alpm_log(PM_LOG_ERROR, _("problem occurred while installing %s\n"),
656 newpkg->name);
657 alpm_logaction("error: problem occurred while installing %s\n",
658 newpkg->name);
663 /* make an install date (in UTC) */
664 newpkg->installdate = time(NULL);
666 _alpm_log(PM_LOG_DEBUG, "updating database\n");
667 _alpm_log(PM_LOG_DEBUG, "adding database entry '%s'\n", newpkg->name);
669 if(_alpm_local_db_write(db, newpkg, INFRQ_ALL)) {
670 _alpm_log(PM_LOG_ERROR, _("could not update database entry %s-%s\n"),
671 alpm_pkg_get_name(newpkg), alpm_pkg_get_version(newpkg));
672 alpm_logaction("error: could not update database entry %s-%s\n",
673 alpm_pkg_get_name(newpkg), alpm_pkg_get_version(newpkg));
674 pm_errno = PM_ERR_DB_WRITE;
675 ret = -1;
676 goto cleanup;
679 if(_alpm_db_add_pkgincache(db, newpkg) == -1) {
680 _alpm_log(PM_LOG_ERROR, _("could not add entry '%s' in cache\n"),
681 alpm_pkg_get_name(newpkg));
684 if(is_upgrade) {
685 PROGRESS(trans, PM_TRANS_PROGRESS_UPGRADE_START,
686 alpm_pkg_get_name(newpkg), 100, pkg_count, pkg_current);
687 } else {
688 PROGRESS(trans, PM_TRANS_PROGRESS_ADD_START,
689 alpm_pkg_get_name(newpkg), 100, pkg_count, pkg_current);
692 /* run the post-install script if it exists */
693 if(alpm_pkg_has_scriptlet(newpkg)
694 && !(trans->flags & PM_TRANS_FLAG_NOSCRIPTLET)) {
695 if(is_upgrade) {
696 _alpm_runscriptlet(handle->root, scriptlet, "post_upgrade",
697 alpm_pkg_get_version(newpkg),
698 oldpkg ? alpm_pkg_get_version(oldpkg) : NULL, trans);
699 } else {
700 _alpm_runscriptlet(handle->root, scriptlet, "post_install",
701 alpm_pkg_get_version(newpkg), NULL, trans);
705 if(is_upgrade) {
706 EVENT(trans, PM_TRANS_EVT_UPGRADE_DONE, newpkg, oldpkg);
707 } else {
708 EVENT(trans, PM_TRANS_EVT_ADD_DONE, newpkg, oldpkg);
711 cleanup:
712 _alpm_pkg_free(oldpkg);
713 return(ret);
716 int _alpm_upgrade_packages(pmtrans_t *trans, pmdb_t *db)
718 int pkg_count, pkg_current;
719 int skip_ldconfig = 0, ret = 0;
720 alpm_list_t *targ;
722 ALPM_LOG_FUNC;
724 ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
725 ASSERT(db != NULL, RET_ERR(PM_ERR_DB_NULL, -1));
727 if(trans->add == NULL) {
728 return(0);
731 pkg_count = alpm_list_count(trans->add);
732 pkg_current = 1;
734 /* loop through our package list adding/upgrading one at a time */
735 for(targ = trans->add; targ; targ = targ->next) {
736 if(handle->trans->state == STATE_INTERRUPTED) {
737 return(ret);
740 pmpkg_t *newpkg = (pmpkg_t *)targ->data;
741 if(commit_single_pkg(newpkg, pkg_current, pkg_count, trans, db)) {
742 /* something screwed up on the commit, abort the trans */
743 trans->state = STATE_INTERRUPTED;
744 pm_errno = PM_ERR_TRANS_ABORT;
745 /* running ldconfig at this point could possibly screw system */
746 skip_ldconfig = 1;
747 ret = -1;
750 pkg_current++;
753 if(!skip_ldconfig) {
754 /* run ldconfig if it exists */
755 _alpm_ldconfig(handle->root);
758 return(ret);
761 /* vim: set ts=2 sw=2 noet: */