Prefix _alpm_errno_t members with ALPM
[pacman-ng.git] / lib / libalpm / add.c
blob9ed4d818651e3dfc9c8ba559231aa2f531c4d6df
1 /*
2 * add.c
4 * Copyright (c) 2006-2011 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.h"
41 #include "alpm_list.h"
42 #include "handle.h"
43 #include "trans.h"
44 #include "util.h"
45 #include "log.h"
46 #include "backup.h"
47 #include "package.h"
48 #include "db.h"
49 #include "remove.h"
50 #include "handle.h"
52 /** Add a package to the transaction. */
53 int SYMEXPORT alpm_add_pkg(alpm_handle_t *handle, alpm_pkg_t *pkg)
55 const char *pkgname, *pkgver;
56 alpm_trans_t *trans;
57 alpm_pkg_t *local;
59 /* Sanity checks */
60 CHECK_HANDLE(handle, return -1);
61 ASSERT(pkg != NULL, 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));
68 pkgname = pkg->name;
69 pkgver = pkg->version;
71 _alpm_log(handle, ALPM_LOG_DEBUG, "adding package '%s'\n", pkgname);
73 if(_alpm_pkg_find(trans->add, pkgname)) {
74 RET_ERR(handle, ALPM_ERR_TRANS_DUP_TARGET, -1);
77 local = _alpm_db_get_pkgfromcache(handle->db_local, pkgname);
78 if(local) {
79 const char *localpkgname = alpm_pkg_get_name(local);
80 const char *localpkgver = alpm_pkg_get_version(local);
81 int cmp = _alpm_pkg_compare_versions(pkg, local);
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);
93 } else if(cmp < 0) {
94 /* local version is newer */
95 _alpm_log(handle, ALPM_LOG_WARNING, _("downgrading package %s (%s => %s)\n"),
96 localpkgname, localpkgver, pkgver);
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);
106 return 0;
109 static int perform_extraction(alpm_handle_t *handle, struct archive *archive,
110 struct archive_entry *entry, const char *filename, const char *origname)
112 int ret;
113 const int archive_flags = ARCHIVE_EXTRACT_OWNER |
114 ARCHIVE_EXTRACT_PERM |
115 ARCHIVE_EXTRACT_TIME;
117 archive_entry_set_pathname(entry, filename);
119 ret = archive_read_extract(archive, entry, archive_flags);
120 if(ret == ARCHIVE_WARN && archive_errno(archive) != ENOSPC) {
121 /* operation succeeded but a "non-critical" error was encountered */
122 _alpm_log(handle, ALPM_LOG_WARNING, _("warning given when extracting %s (%s)\n"),
123 origname, archive_error_string(archive));
124 } else if(ret != ARCHIVE_OK) {
125 _alpm_log(handle, ALPM_LOG_ERROR, _("could not extract %s (%s)\n"),
126 origname, archive_error_string(archive));
127 alpm_logaction(handle, "error: could not extract %s (%s)\n",
128 origname, archive_error_string(archive));
129 return 1;
131 return 0;
134 static int extract_single_file(alpm_handle_t *handle, struct archive *archive,
135 struct archive_entry *entry, alpm_pkg_t *newpkg, alpm_pkg_t *oldpkg)
137 const char *entryname;
138 mode_t entrymode;
139 char filename[PATH_MAX]; /* the actual file we're extracting */
140 int needbackup = 0, notouch = 0;
141 const char *hash_orig = NULL;
142 char *entryname_orig = NULL;
143 int errors = 0;
145 entryname = archive_entry_pathname(entry);
146 entrymode = archive_entry_mode(entry);
148 memset(filename, 0, PATH_MAX); /* just to be sure */
150 if(strcmp(entryname, ".INSTALL") == 0) {
151 /* the install script goes inside the db */
152 snprintf(filename, PATH_MAX, "%s%s-%s/install",
153 _alpm_db_path(handle->db_local), newpkg->name, newpkg->version);
154 archive_entry_set_perm(entry, 0644);
155 } else if(strcmp(entryname, ".CHANGELOG") == 0) {
156 /* the changelog goes inside the db */
157 snprintf(filename, PATH_MAX, "%s%s-%s/changelog",
158 _alpm_db_path(handle->db_local), newpkg->name, newpkg->version);
159 archive_entry_set_perm(entry, 0644);
160 } else if(*entryname == '.') {
161 /* for now, ignore all files starting with '.' that haven't
162 * already been handled (for future possibilities) */
163 _alpm_log(handle, ALPM_LOG_DEBUG, "skipping extraction of '%s'\n", entryname);
164 archive_read_data_skip(archive);
165 return 0;
166 } else {
167 /* build the new entryname relative to handle->root */
168 snprintf(filename, PATH_MAX, "%s%s", handle->root, entryname);
171 /* if a file is in NoExtract then we never extract it */
172 if(alpm_list_find_str(handle->noextract, entryname)) {
173 _alpm_log(handle, ALPM_LOG_DEBUG, "%s is in NoExtract, skipping extraction\n",
174 entryname);
175 alpm_logaction(handle, "note: %s is in NoExtract, skipping extraction\n",
176 entryname);
177 archive_read_data_skip(archive);
178 return 0;
181 /* Check for file existence. This is one of the more crucial parts
182 * to get 'right'. Here are the possibilities, with the filesystem
183 * on the left and the package on the top:
184 * (F=file, N=node, S=symlink, D=dir)
185 * | F/N | S | D
186 * non-existent | 1 | 2 | 3
187 * F/N | 4 | 5 | 6
188 * S | 7 | 8 | 9
189 * D | 10 | 11 | 12
191 * 1,2,3- extract, no magic necessary. lstat (_alpm_lstat) will fail here.
192 * 4,5,6,7,8- conflict checks should have caught this. either overwrite
193 * or backup the file.
194 * 9- follow the symlink, hopefully it is a directory, check it.
195 * 10- file replacing directory- don't allow it.
196 * 11- don't extract symlink- a dir exists here. we don't want links to
197 * links, etc.
198 * 12- skip extraction, dir already exists.
201 /* do both a lstat and a stat, so we can see what symlinks point to */
202 struct stat lsbuf, sbuf;
203 if(_alpm_lstat(filename, &lsbuf) != 0 || stat(filename, &sbuf) != 0) {
204 /* cases 1,2,3: couldn't stat an existing file, skip all backup checks */
205 } else {
206 if(S_ISDIR(lsbuf.st_mode)) {
207 if(S_ISDIR(entrymode)) {
208 /* case 12: existing dir, ignore it */
209 if(lsbuf.st_mode != entrymode) {
210 /* if filesystem perms are different than pkg perms, warn user */
211 mode_t mask = 07777;
212 _alpm_log(handle, ALPM_LOG_WARNING, _("directory permissions differ on %s\n"
213 "filesystem: %o package: %o\n"), entryname, lsbuf.st_mode & mask,
214 entrymode & mask);
215 alpm_logaction(handle, "warning: directory permissions differ on %s\n"
216 "filesystem: %o package: %o\n", entryname, lsbuf.st_mode & mask,
217 entrymode & mask);
219 _alpm_log(handle, ALPM_LOG_DEBUG, "extract: skipping dir extraction of %s\n",
220 entryname);
221 archive_read_data_skip(archive);
222 return 0;
223 } else {
224 /* case 10/11: trying to overwrite dir with file/symlink, don't allow it */
225 _alpm_log(handle, ALPM_LOG_ERROR, _("extract: not overwriting dir with file %s\n"),
226 entryname);
227 archive_read_data_skip(archive);
228 return 1;
230 } else if(S_ISLNK(lsbuf.st_mode) && S_ISDIR(entrymode)) {
231 /* case 9: existing symlink, dir in package */
232 if(S_ISDIR(sbuf.st_mode)) {
233 /* the symlink on FS is to a directory, so we'll use it */
234 _alpm_log(handle, ALPM_LOG_DEBUG, "extract: skipping symlink overwrite of %s\n",
235 entryname);
236 archive_read_data_skip(archive);
237 return 0;
238 } else {
239 /* this is BAD. symlink was not to a directory */
240 _alpm_log(handle, ALPM_LOG_ERROR, _("extract: symlink %s does not point to dir\n"),
241 entryname);
242 archive_read_data_skip(archive);
243 return 1;
245 } else if(S_ISREG(lsbuf.st_mode) && S_ISDIR(entrymode)) {
246 /* case 6: trying to overwrite file with dir */
247 _alpm_log(handle, ALPM_LOG_DEBUG, "extract: overwriting file with dir %s\n",
248 entryname);
249 } else if(S_ISREG(entrymode)) {
250 /* case 4,7: */
251 /* if file is in NoUpgrade, don't touch it */
252 if(alpm_list_find_str(handle->noupgrade, entryname)) {
253 notouch = 1;
254 } else {
255 alpm_backup_t *backup;
256 /* go to the backup array and see if our conflict is there */
257 /* check newpkg first, so that adding backup files is retroactive */
258 backup = _alpm_needbackup(entryname, alpm_pkg_get_backup(newpkg));
259 if(backup) {
260 /* if we force hash_orig to be non-NULL retroactive backup works */
261 hash_orig = "";
262 needbackup = 1;
265 /* check oldpkg for a backup entry, store the hash if available */
266 if(oldpkg) {
267 backup = _alpm_needbackup(entryname, alpm_pkg_get_backup(oldpkg));
268 if(backup) {
269 hash_orig = backup->hash;
270 needbackup = 1;
275 /* else if(S_ISLNK(entrymode)) */
276 /* case 5,8: don't need to do anything special */
279 /* we need access to the original entryname later after calls to
280 * archive_entry_set_pathname(), so we need to dupe it and free() later */
281 STRDUP(entryname_orig, entryname, RET_ERR(handle, ALPM_ERR_MEMORY, -1));
283 if(needbackup) {
284 char checkfile[PATH_MAX];
285 char *hash_local = NULL, *hash_pkg = NULL;
286 int ret;
288 snprintf(checkfile, PATH_MAX, "%s.paccheck", filename);
290 ret = perform_extraction(handle, archive, entry, checkfile, entryname_orig);
291 if(ret == 1) {
292 /* error */
293 FREE(entryname_orig);
294 return 1;
297 hash_local = alpm_compute_md5sum(filename);
298 hash_pkg = alpm_compute_md5sum(checkfile);
300 /* update the md5 hash in newpkg's backup (it will be the new orginal) */
301 alpm_list_t *i;
302 for(i = alpm_pkg_get_backup(newpkg); i; i = i->next) {
303 alpm_backup_t *backup = i->data;
304 char *newhash;
305 if(!backup->name || strcmp(backup->name, entryname_orig) != 0) {
306 continue;
308 STRDUP(newhash, hash_pkg, RET_ERR(handle, ALPM_ERR_MEMORY, -1));
309 FREE(backup->hash);
310 backup->hash = newhash;
313 _alpm_log(handle, ALPM_LOG_DEBUG, "checking hashes for %s\n", entryname_orig);
314 _alpm_log(handle, ALPM_LOG_DEBUG, "current: %s\n", hash_local);
315 _alpm_log(handle, ALPM_LOG_DEBUG, "new: %s\n", hash_pkg);
316 _alpm_log(handle, ALPM_LOG_DEBUG, "original: %s\n", hash_orig);
318 if(!oldpkg) {
319 if(hash_local && hash_pkg && strcmp(hash_local, hash_pkg) != 0) {
320 /* looks like we have a local file that has a different hash as the
321 * file in the package, move it to a .pacorig */
322 char newpath[PATH_MAX];
323 snprintf(newpath, PATH_MAX, "%s.pacorig", filename);
325 /* move the existing file to the "pacorig" */
326 if(rename(filename, newpath)) {
327 _alpm_log(handle, ALPM_LOG_ERROR, _("could not rename %s to %s (%s)\n"),
328 filename, newpath, strerror(errno));
329 alpm_logaction(handle, "error: could not rename %s to %s (%s)\n",
330 filename, newpath, strerror(errno));
331 errors++;
332 } else {
333 /* rename the file we extracted to the real name */
334 if(rename(checkfile, filename)) {
335 _alpm_log(handle, ALPM_LOG_ERROR, _("could not rename %s to %s (%s)\n"),
336 checkfile, filename, strerror(errno));
337 alpm_logaction(handle, "error: could not rename %s to %s (%s)\n",
338 checkfile, filename, strerror(errno));
339 errors++;
340 } else {
341 _alpm_log(handle, ALPM_LOG_WARNING, _("%s saved as %s\n"), filename, newpath);
342 alpm_logaction(handle, "warning: %s saved as %s\n", filename, newpath);
345 } else {
346 /* local file is identical to pkg one, so just remove pkg one */
347 unlink(checkfile);
349 } else if(hash_orig) {
350 /* the fun part */
352 if(hash_local && strcmp(hash_orig, hash_local) == 0) {
353 /* installed file has NOT been changed by user */
354 if(hash_pkg && strcmp(hash_orig, hash_pkg) != 0) {
355 _alpm_log(handle, ALPM_LOG_DEBUG, "action: installing new file: %s\n",
356 entryname_orig);
358 if(rename(checkfile, filename)) {
359 _alpm_log(handle, ALPM_LOG_ERROR, _("could not rename %s to %s (%s)\n"),
360 checkfile, filename, strerror(errno));
361 alpm_logaction(handle, "error: could not rename %s to %s (%s)\n",
362 checkfile, filename, strerror(errno));
363 errors++;
365 } else {
366 /* no sense in installing the same file twice, install
367 * ONLY if the original and package hashes differ */
368 _alpm_log(handle, ALPM_LOG_DEBUG, "action: leaving existing file in place\n");
369 unlink(checkfile);
371 } else if(hash_pkg && strcmp(hash_orig, hash_pkg) == 0) {
372 /* originally installed file and new file are the same - this
373 * implies the case above failed - i.e. the file was changed by a
374 * user */
375 _alpm_log(handle, ALPM_LOG_DEBUG, "action: leaving existing file in place\n");
376 unlink(checkfile);
377 } else if(hash_local && hash_pkg && strcmp(hash_local, hash_pkg) == 0) {
378 /* this would be magical. The above two cases failed, but the
379 * user changes just so happened to make the new file exactly the
380 * same as the one in the package... skip it */
381 _alpm_log(handle, ALPM_LOG_DEBUG, "action: leaving existing file in place\n");
382 unlink(checkfile);
383 } else {
384 char newpath[PATH_MAX];
385 _alpm_log(handle, ALPM_LOG_DEBUG, "action: keeping current file and installing"
386 " new one with .pacnew ending\n");
387 snprintf(newpath, PATH_MAX, "%s.pacnew", filename);
388 if(rename(checkfile, newpath)) {
389 _alpm_log(handle, ALPM_LOG_ERROR, _("could not install %s as %s (%s)\n"),
390 filename, newpath, strerror(errno));
391 alpm_logaction(handle, "error: could not install %s as %s (%s)\n",
392 filename, newpath, strerror(errno));
393 } else {
394 _alpm_log(handle, ALPM_LOG_WARNING, _("%s installed as %s\n"),
395 filename, newpath);
396 alpm_logaction(handle, "warning: %s installed as %s\n",
397 filename, newpath);
402 FREE(hash_local);
403 FREE(hash_pkg);
404 } else {
405 int ret;
407 /* we didn't need a backup */
408 if(notouch) {
409 /* change the path to a .pacnew extension */
410 _alpm_log(handle, ALPM_LOG_DEBUG, "%s is in NoUpgrade -- skipping\n", filename);
411 _alpm_log(handle, ALPM_LOG_WARNING, _("extracting %s as %s.pacnew\n"), filename, filename);
412 alpm_logaction(handle, "warning: extracting %s as %s.pacnew\n", filename, filename);
413 strncat(filename, ".pacnew", PATH_MAX - strlen(filename));
414 } else {
415 _alpm_log(handle, ALPM_LOG_DEBUG, "extracting %s\n", filename);
418 if(handle->trans->flags & ALPM_TRANS_FLAG_FORCE) {
419 /* if FORCE was used, unlink() each file (whether it's there
420 * or not) before extracting. This prevents the old "Text file busy"
421 * error that crops up if forcing a glibc or pacman upgrade. */
422 unlink(filename);
425 ret = perform_extraction(handle, archive, entry, filename, entryname_orig);
426 if(ret == 1) {
427 /* error */
428 FREE(entryname_orig);
429 return 1;
432 /* calculate an hash if this is in newpkg's backup */
433 alpm_list_t *i;
434 for(i = alpm_pkg_get_backup(newpkg); i; i = i->next) {
435 alpm_backup_t *backup = i->data;
436 char *newhash;
437 if(!backup->name || strcmp(backup->name, entryname_orig) != 0) {
438 continue;
440 _alpm_log(handle, ALPM_LOG_DEBUG, "appending backup entry for %s\n", entryname_orig);
441 newhash = alpm_compute_md5sum(filename);
442 FREE(backup->hash);
443 backup->hash = newhash;
446 FREE(entryname_orig);
447 return errors;
450 static int commit_single_pkg(alpm_handle_t *handle, alpm_pkg_t *newpkg,
451 size_t pkg_current, size_t pkg_count)
453 int i, ret = 0, errors = 0;
454 char scriptlet[PATH_MAX];
455 int is_upgrade = 0;
456 alpm_pkg_t *oldpkg = NULL;
457 alpm_db_t *db = handle->db_local;
458 alpm_trans_t *trans = handle->trans;
460 ASSERT(trans != NULL, return -1);
462 snprintf(scriptlet, PATH_MAX, "%s%s-%s/install",
463 _alpm_db_path(db), alpm_pkg_get_name(newpkg),
464 alpm_pkg_get_version(newpkg));
466 /* see if this is an upgrade. if so, remove the old package first */
467 alpm_pkg_t *local = _alpm_db_get_pkgfromcache(db, newpkg->name);
468 if(local) {
469 is_upgrade = 1;
471 /* we'll need to save some record for backup checks later */
472 oldpkg = _alpm_pkg_dup(local);
474 EVENT(trans, ALPM_TRANS_EVT_UPGRADE_START, newpkg, oldpkg);
475 _alpm_log(handle, ALPM_LOG_DEBUG, "upgrading package %s-%s\n",
476 newpkg->name, newpkg->version);
478 /* copy over the install reason */
479 newpkg->reason = alpm_pkg_get_reason(oldpkg);
481 /* pre_upgrade scriptlet */
482 if(alpm_pkg_has_scriptlet(newpkg) && !(trans->flags & ALPM_TRANS_FLAG_NOSCRIPTLET)) {
483 _alpm_runscriptlet(handle, newpkg->origin_data.file,
484 "pre_upgrade", newpkg->version, oldpkg->version);
486 } else {
487 is_upgrade = 0;
489 EVENT(trans, ALPM_TRANS_EVT_ADD_START, newpkg, NULL);
490 _alpm_log(handle, ALPM_LOG_DEBUG, "adding package %s-%s\n",
491 newpkg->name, newpkg->version);
493 /* pre_install scriptlet */
494 if(alpm_pkg_has_scriptlet(newpkg) && !(trans->flags & ALPM_TRANS_FLAG_NOSCRIPTLET)) {
495 _alpm_runscriptlet(handle, newpkg->origin_data.file,
496 "pre_install", newpkg->version, NULL);
500 /* we override any pre-set reason if we have alldeps or allexplicit set */
501 if(trans->flags & ALPM_TRANS_FLAG_ALLDEPS) {
502 newpkg->reason = ALPM_PKG_REASON_DEPEND;
503 } else if(trans->flags & ALPM_TRANS_FLAG_ALLEXPLICIT) {
504 newpkg->reason = ALPM_PKG_REASON_EXPLICIT;
507 if(oldpkg) {
508 /* set up fake remove transaction */
509 if(_alpm_upgraderemove_package(handle, oldpkg, newpkg) == -1) {
510 handle->pm_errno = ALPM_ERR_TRANS_ABORT;
511 ret = -1;
512 goto cleanup;
516 /* prepare directory for database entries so permission are correct after
517 changelog/install script installation (FS#12263) */
518 if(_alpm_local_db_prepare(db, newpkg)) {
519 alpm_logaction(handle, "error: could not create database entry %s-%s\n",
520 alpm_pkg_get_name(newpkg), alpm_pkg_get_version(newpkg));
521 handle->pm_errno = ALPM_ERR_DB_WRITE;
522 ret = -1;
523 goto cleanup;
526 if(!(trans->flags & ALPM_TRANS_FLAG_DBONLY)) {
527 struct archive *archive;
528 struct archive_entry *entry;
529 char cwd[PATH_MAX] = "";
530 int restore_cwd = 0;
532 _alpm_log(handle, ALPM_LOG_DEBUG, "extracting files\n");
534 if((archive = archive_read_new()) == NULL) {
535 handle->pm_errno = ALPM_ERR_LIBARCHIVE;
536 ret = -1;
537 goto cleanup;
540 archive_read_support_compression_all(archive);
541 archive_read_support_format_all(archive);
543 _alpm_log(handle, ALPM_LOG_DEBUG, "archive: %s\n", newpkg->origin_data.file);
544 if(archive_read_open_filename(archive, newpkg->origin_data.file,
545 ARCHIVE_DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK) {
546 handle->pm_errno = ALPM_ERR_PKG_OPEN;
547 ret = -1;
548 goto cleanup;
551 /* save the cwd so we can restore it later */
552 if(getcwd(cwd, PATH_MAX) == NULL) {
553 _alpm_log(handle, ALPM_LOG_ERROR, _("could not get current working directory\n"));
554 } else {
555 restore_cwd = 1;
558 /* libarchive requires this for extracting hard links */
559 if(chdir(handle->root) != 0) {
560 _alpm_log(handle, ALPM_LOG_ERROR, _("could not change directory to %s (%s)\n"),
561 handle->root, strerror(errno));
562 ret = -1;
563 goto cleanup;
566 /* call PROGRESS once with 0 percent, as we sort-of skip that here */
567 if(is_upgrade) {
568 PROGRESS(trans, ALPM_TRANS_PROGRESS_UPGRADE_START,
569 alpm_pkg_get_name(newpkg), 0, pkg_count, pkg_current);
570 } else {
571 PROGRESS(trans, ALPM_TRANS_PROGRESS_ADD_START,
572 alpm_pkg_get_name(newpkg), 0, pkg_count, pkg_current);
575 for(i = 0; archive_read_next_header(archive, &entry) == ARCHIVE_OK; i++) {
576 int percent;
578 if(newpkg->size != 0) {
579 /* Using compressed size for calculations here, as newpkg->isize is not
580 * exact when it comes to comparing to the ACTUAL uncompressed size
581 * (missing metadata sizes) */
582 int64_t pos = archive_position_compressed(archive);
583 percent = (pos * 100) / newpkg->size;
584 _alpm_log(handle, ALPM_LOG_DEBUG, "decompression progress: "
585 "%d%% (%"PRId64" / %jd)\n",
586 percent, pos, (intmax_t)newpkg->size);
587 if(percent >= 100) {
588 percent = 100;
590 } else {
591 percent = 0;
594 if(is_upgrade) {
595 PROGRESS(trans, ALPM_TRANS_PROGRESS_UPGRADE_START,
596 alpm_pkg_get_name(newpkg), percent, pkg_count,
597 pkg_current);
598 } else {
599 PROGRESS(trans, ALPM_TRANS_PROGRESS_ADD_START,
600 alpm_pkg_get_name(newpkg), percent, pkg_count,
601 pkg_current);
604 /* extract the next file from the archive */
605 errors += extract_single_file(handle, archive, entry, newpkg, oldpkg);
607 archive_read_finish(archive);
609 /* restore the old cwd if we have it */
610 if(restore_cwd && chdir(cwd) != 0) {
611 _alpm_log(handle, ALPM_LOG_ERROR, _("could not change directory to %s (%s)\n"), cwd, strerror(errno));
614 if(errors) {
615 ret = -1;
616 if(is_upgrade) {
617 _alpm_log(handle, ALPM_LOG_ERROR, _("problem occurred while upgrading %s\n"),
618 newpkg->name);
619 alpm_logaction(handle, "error: problem occurred while upgrading %s\n",
620 newpkg->name);
621 } else {
622 _alpm_log(handle, ALPM_LOG_ERROR, _("problem occurred while installing %s\n"),
623 newpkg->name);
624 alpm_logaction(handle, "error: problem occurred while installing %s\n",
625 newpkg->name);
630 /* make an install date (in UTC) */
631 newpkg->installdate = time(NULL);
633 _alpm_log(handle, ALPM_LOG_DEBUG, "updating database\n");
634 _alpm_log(handle, ALPM_LOG_DEBUG, "adding database entry '%s'\n", newpkg->name);
636 if(_alpm_local_db_write(db, newpkg, INFRQ_ALL)) {
637 _alpm_log(handle, ALPM_LOG_ERROR, _("could not update database entry %s-%s\n"),
638 alpm_pkg_get_name(newpkg), alpm_pkg_get_version(newpkg));
639 alpm_logaction(handle, "error: could not update database entry %s-%s\n",
640 alpm_pkg_get_name(newpkg), alpm_pkg_get_version(newpkg));
641 handle->pm_errno = ALPM_ERR_DB_WRITE;
642 ret = -1;
643 goto cleanup;
646 if(_alpm_db_add_pkgincache(db, newpkg) == -1) {
647 _alpm_log(handle, ALPM_LOG_ERROR, _("could not add entry '%s' in cache\n"),
648 alpm_pkg_get_name(newpkg));
651 if(is_upgrade) {
652 PROGRESS(trans, ALPM_TRANS_PROGRESS_UPGRADE_START,
653 alpm_pkg_get_name(newpkg), 100, pkg_count, pkg_current);
654 } else {
655 PROGRESS(trans, ALPM_TRANS_PROGRESS_ADD_START,
656 alpm_pkg_get_name(newpkg), 100, pkg_count, pkg_current);
659 /* run the post-install script if it exists */
660 if(alpm_pkg_has_scriptlet(newpkg)
661 && !(trans->flags & ALPM_TRANS_FLAG_NOSCRIPTLET)) {
662 if(is_upgrade) {
663 _alpm_runscriptlet(handle, scriptlet, "post_upgrade",
664 alpm_pkg_get_version(newpkg),
665 oldpkg ? alpm_pkg_get_version(oldpkg) : NULL);
666 } else {
667 _alpm_runscriptlet(handle, scriptlet, "post_install",
668 alpm_pkg_get_version(newpkg), NULL);
672 if(is_upgrade) {
673 EVENT(trans, ALPM_TRANS_EVT_UPGRADE_DONE, newpkg, oldpkg);
674 } else {
675 EVENT(trans, ALPM_TRANS_EVT_ADD_DONE, newpkg, oldpkg);
678 cleanup:
679 _alpm_pkg_free(oldpkg);
680 return ret;
683 int _alpm_upgrade_packages(alpm_handle_t *handle)
685 size_t pkg_count, pkg_current;
686 int skip_ldconfig = 0, ret = 0;
687 alpm_list_t *targ;
688 alpm_trans_t *trans = handle->trans;
690 if(trans->add == NULL) {
691 return 0;
694 pkg_count = alpm_list_count(trans->add);
695 pkg_current = 1;
697 /* loop through our package list adding/upgrading one at a time */
698 for(targ = trans->add; targ; targ = targ->next) {
699 alpm_pkg_t *newpkg = targ->data;
701 if(handle->trans->state == STATE_INTERRUPTED) {
702 return ret;
705 if(commit_single_pkg(handle, newpkg, pkg_current, pkg_count)) {
706 /* something screwed up on the commit, abort the trans */
707 trans->state = STATE_INTERRUPTED;
708 handle->pm_errno = ALPM_ERR_TRANS_ABORT;
709 /* running ldconfig at this point could possibly screw system */
710 skip_ldconfig = 1;
711 ret = -1;
714 pkg_current++;
717 if(!skip_ldconfig) {
718 /* run ldconfig if it exists */
719 _alpm_ldconfig(handle);
722 return ret;
725 /* vim: set ts=2 sw=2 noet: */