add.c

Go to the documentation of this file.
00001 /*
00002  *  add.c
00003  *
00004  *  Copyright (c) 2002-2007 by Judd Vinet <jvinet@zeroflux.org>
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00018  */
00019 
00020 #include "config.h"
00021 
00022 #include <stdlib.h>
00023 #include <errno.h>
00024 #include <time.h>
00025 #include <string.h>
00026 #include <limits.h>
00027 #include <sys/types.h>
00028 #include <sys/stat.h>
00029 #include <unistd.h>
00030 
00031 /* libarchive */
00032 #include <archive.h>
00033 #include <archive_entry.h>
00034 
00035 /* libalpm */
00036 #include "add.h"
00037 #include "alpm_list.h"
00038 #include "trans.h"
00039 #include "util.h"
00040 #include "error.h"
00041 #include "cache.h"
00042 #include "log.h"
00043 #include "backup.h"
00044 #include "package.h"
00045 #include "db.h"
00046 #include "conflict.h"
00047 #include "deps.h"
00048 #include "remove.h"
00049 #include "handle.h"
00050 
00051 int _alpm_add_loadtarget(pmtrans_t *trans, pmdb_t *db, char *name)
00052 {
00053     pmpkg_t *pkg = NULL;
00054     const char *pkgname, *pkgver;
00055     alpm_list_t *i;
00056 
00057     ALPM_LOG_FUNC;
00058 
00059     ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
00060     ASSERT(db != NULL, RET_ERR(PM_ERR_DB_NULL, -1));
00061     ASSERT(name != NULL && strlen(name) != 0, RET_ERR(PM_ERR_WRONG_ARGS, -1));
00062 
00063     _alpm_log(PM_LOG_DEBUG, "loading target '%s'\n", name);
00064 
00065     if(alpm_pkg_load(name, 1, &pkg) != 0) {
00066         goto error;
00067     }
00068     pkgname = alpm_pkg_get_name(pkg);
00069     pkgver = alpm_pkg_get_version(pkg);
00070 
00071     if(trans->type != PM_TRANS_TYPE_UPGRADE) {
00072         /* only install this package if it is not already installed */
00073         if(_alpm_db_get_pkgfromcache(db, pkgname)) {
00074             pm_errno = PM_ERR_PKG_INSTALLED;
00075             goto error;
00076         }
00077     }
00078 
00079     /* check if an older version of said package is already in transaction
00080      * packages.  if so, replace it in the list */
00081     for(i = trans->packages; i; i = i->next) {
00082         pmpkg_t *pkg = i->data;
00083         if(strcmp(pkg->name, pkgname) == 0) {
00084             if(_alpm_versioncmp(pkg->version, pkgver) < 0) {
00085                 pmpkg_t *newpkg;
00086                 _alpm_log(PM_LOG_WARNING, _("replacing older version %s-%s by %s in target list\n"),
00087                           pkg->name, pkg->version, pkgver);
00088                 if((newpkg = _alpm_pkg_load(name, 1)) == NULL) {
00089                     /* pm_errno is already set by pkg_load() */
00090                     goto error;
00091                 }
00092                 _alpm_pkg_free(i->data);
00093                 i->data = newpkg;
00094             } else {
00095                 _alpm_log(PM_LOG_WARNING, _("newer version %s-%s is in the target list -- skipping\n"),
00096                           pkg->name, pkg->version);
00097             }
00098             return(0);
00099         }
00100     }
00101 
00102     if(trans->flags & PM_TRANS_FLAG_ALLDEPS) {
00103         pkg->reason = PM_PKG_REASON_DEPEND;
00104     }
00105 
00106     /* add the package to the transaction */
00107     trans->packages = alpm_list_add(trans->packages, pkg);
00108 
00109     return(0);
00110 
00111 error:
00112     _alpm_pkg_free(pkg);
00113     return(-1);
00114 }
00115 
00116 
00117 /* This is still messy. We have a lot of compare functions, and we should
00118  * try to consolidate them as much as we can (between add and sync) */
00119 /*static int deppkg_cmp(const void *p1, const void *p2)
00120 {
00121     return(strcmp(((pmdepmissing_t *)p1)->target,
00122                         ((pmdepmissing_t *)p2)->target));
00123 }*/
00124 
00125 int _alpm_add_prepare(pmtrans_t *trans, pmdb_t *db, alpm_list_t **data)
00126 {
00127     alpm_list_t *lp = NULL;
00128 
00129     ALPM_LOG_FUNC;
00130 
00131     ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
00132     ASSERT(db != NULL, RET_ERR(PM_ERR_DB_NULL, -1));
00133 
00134     /* Check dependencies
00135      */
00136     if(!(trans->flags & PM_TRANS_FLAG_NODEPS)) {
00137         EVENT(trans, PM_TRANS_EVT_CHECKDEPS_START, NULL, NULL);
00138 
00139         /* look for unsatisfied dependencies */
00140         _alpm_log(PM_LOG_DEBUG, "looking for unsatisfied dependencies\n");
00141         lp = alpm_checkdeps(db, trans->type == PM_TRANS_TYPE_UPGRADE, NULL, trans->packages);
00142         if(lp != NULL) {
00143             if(data) {
00144                 *data = lp;
00145             } else {
00146                 alpm_list_free_inner(lp, (alpm_list_fn_free)_alpm_depmiss_free);
00147                 alpm_list_free(lp);
00148             }
00149             RET_ERR(PM_ERR_UNSATISFIED_DEPS, -1);
00150         }
00151 
00152         /* no unsatisfied deps, so look for conflicts */
00153         _alpm_log(PM_LOG_DEBUG, "looking for conflicts\n");
00154         alpm_list_t *inner = _alpm_innerconflicts(trans->packages);
00155         alpm_list_t *outer = _alpm_outerconflicts(db, trans->packages);
00156         lp = alpm_list_join(inner, outer);
00157 
00158         /* TODO : factorize the conflict resolving code from sync.c to use it here (FS#3492) */
00159 
00160         if(lp != NULL) {
00161             if(data) {
00162                 *data = lp;
00163             } else {
00164                 FREELIST(lp);
00165             }
00166             if(inner) {
00167                 _alpm_log(PM_LOG_ERROR, _("conflicting packages were found in the target list\n"));
00168                 _alpm_log(PM_LOG_ERROR, _("you cannot install two conflicting packages at the same time\n"));
00169             }
00170             if(outer) {
00171                 _alpm_log(PM_LOG_ERROR, _("replacing packages with -A and -U is not supported yet\n"));
00172                 _alpm_log(PM_LOG_ERROR, _("you can replace packages manually using -Rd and -U\n"));
00173             }
00174             RET_ERR(PM_ERR_CONFLICTING_DEPS, -1);
00175         }
00176 
00177         /* re-order w.r.t. dependencies */
00178         _alpm_log(PM_LOG_DEBUG, "sorting by dependencies\n");
00179         lp = _alpm_sortbydeps(trans->packages, PM_TRANS_TYPE_ADD);
00180         /* free the old alltargs */
00181         alpm_list_free(trans->packages);
00182         trans->packages = lp;
00183 
00184         EVENT(trans, PM_TRANS_EVT_CHECKDEPS_DONE, NULL, NULL);
00185     }
00186 
00187     /* Check for file conflicts */
00188     if(!(trans->flags & PM_TRANS_FLAG_FORCE)) {
00189         EVENT(trans, PM_TRANS_EVT_FILECONFLICTS_START, NULL, NULL);
00190 
00191         _alpm_log(PM_LOG_DEBUG, "looking for file conflicts\n");
00192         lp = _alpm_db_find_fileconflicts(db, trans, handle->root);
00193         if(lp != NULL) {
00194             if(data) {
00195                 *data = lp;
00196             } else {
00197                 alpm_list_free_inner(lp, (alpm_list_fn_free)_alpm_fileconflict_free);
00198                 alpm_list_free(lp);
00199             }
00200             RET_ERR(PM_ERR_FILE_CONFLICTS, -1);
00201         }
00202 
00203         EVENT(trans, PM_TRANS_EVT_FILECONFLICTS_DONE, NULL, NULL);
00204     }
00205 
00206     return(0);
00207 }
00208 
00209 static int upgrade_remove(pmpkg_t *oldpkg, pmpkg_t *newpkg, pmtrans_t *trans, pmdb_t *db) {
00210     /* this is kinda odd.  If the old package exists, at this point we make a
00211      * NEW transaction, unrelated to handle->trans, and instantiate a "remove"
00212      * with the type PM_TRANS_TYPE_REMOVEUPGRADE. TODO: kill this weird
00213      * behavior. */
00214     pmtrans_t *tr = _alpm_trans_new();
00215     _alpm_log(PM_LOG_DEBUG, "removing old package first (%s-%s)\n",
00216             oldpkg->name, oldpkg->version);
00217 
00218     if(!tr) {
00219         RET_ERR(PM_ERR_TRANS_ABORT, -1);
00220     }
00221 
00222     if(_alpm_trans_init(tr, PM_TRANS_TYPE_REMOVEUPGRADE, trans->flags,
00223                 NULL, NULL, NULL) == -1) {
00224         _alpm_trans_free(tr);
00225         tr = NULL;
00226         RET_ERR(PM_ERR_TRANS_ABORT, -1);
00227     }
00228 
00229     if(_alpm_remove_loadtarget(tr, db, newpkg->name) == -1) {
00230         _alpm_trans_free(tr);
00231         tr = NULL;
00232         RET_ERR(PM_ERR_TRANS_ABORT, -1);
00233     }
00234 
00235     /* copy the remove skiplist over */
00236     tr->skip_remove = alpm_list_strdup(trans->skip_remove);
00237     const alpm_list_t *b;
00238 
00239     /* Add files in the NEW backup array to the NoUpgrade array
00240      * so this removal operation doesn't kill them */
00241     alpm_list_t *old_noupgrade = alpm_list_strdup(handle->noupgrade);
00242     /* old package backup list */
00243     alpm_list_t *filelist = alpm_pkg_get_files(newpkg);
00244     for(b = alpm_pkg_get_backup(newpkg); b; b = b->next) {
00245         char *backup = _alpm_backup_file(b->data);
00246         /* safety check (fix the upgrade026 pactest) */
00247         if(!alpm_list_find_str(filelist, backup)) {
00248             continue;
00249         }
00250         _alpm_log(PM_LOG_DEBUG, "adding %s to the NoUpgrade array temporarily\n",
00251                 backup);
00252         handle->noupgrade = alpm_list_add(handle->noupgrade,
00253                 backup);
00254     }
00255 
00256     /* TODO: we could also add files in the OLD backup array, but this would
00257      * change the backup handling behavior, and break several pactests, and we
00258      * can't do this just before 3.1 release.
00259      * The unlink_file function in remove.c would also need to be reviewed. */
00260 #if 0
00261     /* new package backup list */
00262     for(b = alpm_pkg_get_backup(oldpkg); b; b = b->next) {
00263         char *backup = _alpm_backup_file(b->data);
00264         /* make sure we don't add duplicate entries */
00265         if(!alpm_list_find_ptr(handle->noupgrade, backup)) {
00266             _alpm_log(PM_LOG_DEBUG, "adding %s to the NoUpgrade array temporarily\n",
00267                     backup);
00268             handle->noupgrade = alpm_list_add(handle->noupgrade,
00269                     backup);
00270         }
00271     }
00272 #endif
00273 
00274     int ret = _alpm_remove_commit(tr, db);
00275 
00276     _alpm_trans_free(tr);
00277     tr = NULL;
00278 
00279     /* restore our "NoUpgrade" list to previous state */
00280     FREELIST(handle->noupgrade);
00281     handle->noupgrade = old_noupgrade;
00282 
00283     if(ret == -1) {
00284         RET_ERR(PM_ERR_TRANS_ABORT, -1);
00285     }
00286 
00287     return(0);
00288 }
00289 
00290 static int extract_single_file(struct archive *archive,
00291         struct archive_entry *entry, pmpkg_t *newpkg, pmpkg_t *oldpkg,
00292         pmtrans_t *trans, pmdb_t *db)
00293 {
00294     const char *entryname; /* the name of the file in the archive */
00295     mode_t entrymode;
00296     char filename[PATH_MAX]; /* the actual file we're extracting */
00297     int needbackup = 0, notouch = 0;
00298     char *hash_orig = NULL;
00299     const int archive_flags = ARCHIVE_EXTRACT_OWNER |
00300                               ARCHIVE_EXTRACT_PERM |
00301                               ARCHIVE_EXTRACT_TIME;
00302     int errors = 0;
00303 
00304     entryname = archive_entry_pathname(entry);
00305     entrymode = archive_entry_mode(entry);
00306 
00307     memset(filename, 0, PATH_MAX); /* just to be sure */
00308 
00309     if(strcmp(entryname, ".INSTALL") == 0) {
00310         /* the install script goes inside the db */
00311         snprintf(filename, PATH_MAX, "%s/%s-%s/install", db->path,
00312                 newpkg->name, newpkg->version);
00313         archive_entry_set_mode(entry, 0644);
00314     } else if(strcmp(entryname, ".CHANGELOG") == 0) {
00315         /* the changelog goes inside the db */
00316         snprintf(filename, PATH_MAX, "%s/%s-%s/changelog", db->path,
00317                 newpkg->name, newpkg->version);
00318         archive_entry_set_mode(entry, 0644);
00319     } else if(*entryname == '.') {
00320         /* for now, ignore all files starting with '.' that haven't
00321          * already been handled (for future possibilities) */
00322         _alpm_log(PM_LOG_DEBUG, "skipping extraction of '%s'\n", entryname);
00323         archive_read_data_skip(archive);
00324         return(0);
00325     } else {
00326         /* build the new entryname relative to handle->root */
00327         snprintf(filename, PATH_MAX, "%s%s", handle->root, entryname);
00328     }
00329 
00330     /* if a file is in NoExtract then we never extract it */
00331     if(alpm_list_find_str(handle->noextract, entryname)) {
00332         _alpm_log(PM_LOG_DEBUG, "%s is in NoExtract, skipping extraction\n",
00333                 entryname);
00334         alpm_logaction("note: %s is in NoExtract, skipping extraction\n",
00335                 entryname);
00336         archive_read_data_skip(archive);
00337         return(0);
00338     }
00339 
00340     /* if a file is in the add skiplist we never extract it */
00341     if(alpm_list_find_str(trans->skip_add, filename)) {
00342         _alpm_log(PM_LOG_DEBUG, "%s is in trans->skip_add, skipping extraction\n", entryname);
00343         archive_read_data_skip(archive);
00344         return(0);
00345     }
00346 
00347     /* Check for file existence. This is one of the more crucial parts
00348      * to get 'right'. Here are the possibilities, with the filesystem
00349      * on the left and the package on the top:
00350      * (F=file, N=node, S=symlink, D=dir)
00351      *               |  F/N  |  S  |  D
00352      *  non-existent |   1   |  2  |  3
00353      *  F/N          |   4   |  5  |  6
00354      *  S            |   7   |  8  |  9
00355      *  D            |   10  |  11 |  12
00356      *
00357      *  1,2,3- extract, no magic necessary. lstat (_alpm_lstat) will fail here.
00358      *  4,5,6,7,8- conflict checks should have caught this. either overwrite
00359      *      or backup the file.
00360      *  9- follow the symlink, hopefully it is a directory, check it.
00361      *  10- file replacing directory- don't allow it.
00362      *  11- don't extract symlink- a dir exists here. we don't want links to
00363      *      links, etc.
00364      *  12- skip extraction, dir already exists.
00365      */
00366     struct stat lsbuf;
00367     if(_alpm_lstat(filename, &lsbuf) != 0) {
00368         /* cases 1,2,3: couldn't stat an existing file, skip all backup checks */
00369     } else {
00370         /* do a stat as well, so we can see what symlinks point to */
00371         struct stat sbuf;
00372         stat(filename, &sbuf);
00373 
00374         if(S_ISDIR(lsbuf.st_mode) && S_ISDIR(entrymode)) {
00375             /* case 12: existing dir, ignore it */
00376             if(lsbuf.st_mode != entrymode) {
00377                 /* if filesystem perms are different than pkg perms, warn user */
00378                 int mask = 07777;
00379                 _alpm_log(PM_LOG_WARNING, _("directory permissions differ on %s\n"
00380                             "filesystem: %o  package: %o\n"), entryname, lsbuf.st_mode & mask,
00381                         entrymode & mask);
00382                 alpm_logaction("warning: directory permissions differ on %s\n"
00383                             "filesystem: %o  package: %o\n", entryname, lsbuf.st_mode & mask,
00384                         entrymode & mask);
00385             }
00386             _alpm_log(PM_LOG_DEBUG, "extract: skipping dir extraction of %s\n",
00387                     entryname);
00388             archive_read_data_skip(archive);
00389             return(0);
00390         } else if(S_ISDIR(lsbuf.st_mode) && S_ISLNK(entrymode)) {
00391             /* case 11: existing dir, symlink in package, ignore it */
00392             _alpm_log(PM_LOG_DEBUG, "extract: skipping symlink extraction of %s\n",
00393                     entryname);
00394             archive_read_data_skip(archive);
00395             return(0);
00396         } else if(S_ISLNK(lsbuf.st_mode) && S_ISDIR(entrymode)) {
00397             /* case 9: existing symlink, dir in package */
00398             if(S_ISDIR(sbuf.st_mode)) {
00399                 /* the symlink on FS is to a directory, so we'll use it */
00400                 _alpm_log(PM_LOG_DEBUG, "extract: skipping symlink overwrite of %s\n",
00401                         entryname);
00402                 archive_read_data_skip(archive);
00403                 return(0);
00404             } else {
00405                 /* this is BAD. symlink was not to a directory */
00406                 _alpm_log(PM_LOG_ERROR, _("extract: symlink %s does not point to dir\n"),
00407                         entryname);
00408                 archive_read_data_skip(archive);
00409                 return(1);
00410             }
00411         } else if(S_ISDIR(lsbuf.st_mode) && S_ISREG(entrymode)) {
00412             /* case 10: trying to overwrite dir tree with file, don't allow it */
00413             _alpm_log(PM_LOG_ERROR, _("extract: not overwriting dir with file %s\n"),
00414                     entryname);
00415             archive_read_data_skip(archive);
00416             return(1);
00417         } else if(S_ISREG(lsbuf.st_mode) && S_ISDIR(entrymode)) {
00418             /* case 6: trying to overwrite file with dir */
00419             _alpm_log(PM_LOG_DEBUG, "extract: overwriting file with dir %s\n",
00420                     entryname);
00421         } else if(S_ISREG(entrymode)) {
00422             /* case 4,7: */
00423             /* if file is in NoUpgrade, don't touch it */
00424             if(alpm_list_find_str(handle->noupgrade, entryname)) {
00425                 notouch = 1;
00426             } else {
00427                 /* go to the backup array and see if our conflict is there */
00428                 /* check newpkg first, so that adding backup files is retroactive */
00429                 if(alpm_list_find_str(alpm_pkg_get_backup(newpkg), entryname) != NULL) {
00430                     needbackup = 1;
00431                 }
00432 
00433                 /* check oldpkg for a backup entry, store the hash if available */
00434                 if(oldpkg) {
00435                     hash_orig = _alpm_needbackup(entryname, alpm_pkg_get_backup(oldpkg));
00436                     if(hash_orig) {
00437                         needbackup = 1;
00438                     }
00439                 }
00440 
00441                 /* if we force hash_orig to be non-NULL retroactive backup works */
00442                 if(needbackup && !hash_orig) {
00443                     hash_orig = strdup("");
00444                 }
00445             }
00446         }
00447         /* else if(S_ISLNK(entrymode)) */
00448         /* case 5,8: don't need to do anything special */
00449     }
00450 
00451     if(needbackup) {
00452         char *tempfile = NULL;
00453         char *hash_local = NULL, *hash_pkg = NULL;
00454         int fd;
00455 
00456         /* extract the package's version to a temporary file and checksum it */
00457         tempfile = strdup("/tmp/alpm_XXXXXX");
00458         fd = mkstemp(tempfile);
00459 
00460         archive_entry_set_pathname(entry, tempfile);
00461 
00462         int ret = archive_read_extract(archive, entry, archive_flags);
00463         if(ret == ARCHIVE_WARN) {
00464             /* operation succeeded but a non-critical error was encountered */
00465             _alpm_log(PM_LOG_DEBUG, "warning extracting %s (%s)\n",
00466                     entryname, archive_error_string(archive));
00467         } else if(ret != ARCHIVE_OK) {
00468             _alpm_log(PM_LOG_ERROR, _("could not extract %s (%s)\n"),
00469                     entryname, archive_error_string(archive));
00470             alpm_logaction("error: could not extract %s (%s)\n",
00471                     entryname, archive_error_string(archive));
00472             unlink(tempfile);
00473             FREE(hash_orig);
00474             close(fd);
00475             return(1);
00476         }
00477 
00478         hash_local = alpm_get_md5sum(filename);
00479         hash_pkg = alpm_get_md5sum(tempfile);
00480 
00481         /* append the new md5 hash to it's respective entry
00482          * in newpkg's backup (it will be the new orginal) */
00483         alpm_list_t *backups;
00484         for(backups = alpm_pkg_get_backup(newpkg); backups;
00485                 backups = alpm_list_next(backups)) {
00486             char *oldbackup = alpm_list_getdata(backups);
00487             if(!oldbackup || strcmp(oldbackup, entryname) != 0) {
00488                 continue;
00489             }
00490             char *backup = NULL;
00491             /* length is tab char, null byte and MD5 (32 char) */
00492             int backup_len = strlen(oldbackup) + 34;
00493             backup = malloc(backup_len);
00494             if(!backup) {
00495                 RET_ERR(PM_ERR_MEMORY, -1);
00496             }
00497 
00498             sprintf(backup, "%s\t%s", oldbackup, hash_pkg);
00499             backup[backup_len-1] = '\0';
00500             FREE(oldbackup);
00501             backups->data = backup;
00502         }
00503 
00504         _alpm_log(PM_LOG_DEBUG, "checking hashes for %s\n", entryname);
00505         _alpm_log(PM_LOG_DEBUG, "current:  %s\n", hash_local);
00506         _alpm_log(PM_LOG_DEBUG, "new:      %s\n", hash_pkg);
00507         _alpm_log(PM_LOG_DEBUG, "original: %s\n", hash_orig);
00508 
00509         if(!oldpkg) {
00510             /* looks like we have a local file that has a different hash as the
00511              * file in the package, move it to a .pacorig */
00512             if(strcmp(hash_local, hash_pkg) != 0) {
00513                 char newpath[PATH_MAX];
00514                 snprintf(newpath, PATH_MAX, "%s.pacorig", filename);
00515 
00516                 /* move the existing file to the "pacorig" */
00517                 if(rename(filename, newpath)) {
00518                     archive_entry_set_pathname(entry, filename);
00519                     _alpm_log(PM_LOG_ERROR, _("could not rename %s (%s)\n"), filename, strerror(errno));
00520                     alpm_logaction("error: could not rename %s (%s)\n", filename, strerror(errno));
00521                     errors++;
00522                 } else {
00523                     /* copy the tempfile we extracted to the real path */
00524                     if(_alpm_copyfile(tempfile, filename)) {
00525                         archive_entry_set_pathname(entry, filename);
00526                         _alpm_log(PM_LOG_ERROR, _("could not copy tempfile to %s (%s)\n"), filename, strerror(errno));
00527                         alpm_logaction("error: could not copy tempfile to %s (%s)\n", filename, strerror(errno));
00528                         errors++;
00529                     } else {
00530                         archive_entry_set_pathname(entry, filename);
00531                         _alpm_log(PM_LOG_WARNING, _("%s saved as %s\n"), filename, newpath);
00532                         alpm_logaction("warning: %s saved as %s\n", filename, newpath);
00533                     }
00534                 }
00535             }
00536         } else if(hash_orig) {
00537             /* the fun part */
00538 
00539             if(strcmp(hash_orig, hash_local) == 0) {
00540                 /* installed file has NOT been changed by user */
00541                 if(strcmp(hash_orig, hash_pkg) != 0) {
00542                     _alpm_log(PM_LOG_DEBUG, "action: installing new file: %s\n",
00543                             entryname);
00544 
00545                     if(_alpm_copyfile(tempfile, filename)) {
00546                         _alpm_log(PM_LOG_ERROR, _("could not copy tempfile to %s (%s)\n"), filename, strerror(errno));
00547                         errors++;
00548                     }
00549                     archive_entry_set_pathname(entry, filename);
00550                 } else {
00551                     /* there's no sense in installing the same file twice, install
00552                      * ONLY is the original and package hashes differ */
00553                     _alpm_log(PM_LOG_DEBUG, "action: leaving existing file in place\n");
00554                 }
00555             } else if(strcmp(hash_orig, hash_pkg) == 0) {
00556                 /* originally installed file and new file are the same - this
00557                  * implies the case above failed - i.e. the file was changed by a
00558                  * user */
00559                 _alpm_log(PM_LOG_DEBUG, "action: leaving existing file in place\n");
00560             } else if(strcmp(hash_local, hash_pkg) == 0) {
00561                 /* this would be magical.  The above two cases failed, but the
00562                  * user changes just so happened to make the new file exactly the
00563                  * same as the one in the package... skip it */
00564                 _alpm_log(PM_LOG_DEBUG, "action: leaving existing file in place\n");
00565             } else {
00566                 char newpath[PATH_MAX];
00567                 _alpm_log(PM_LOG_DEBUG, "action: keeping current file and installing new one with .pacnew ending\n");
00568                 snprintf(newpath, PATH_MAX, "%s.pacnew", filename);
00569                 if(_alpm_copyfile(tempfile, newpath)) {
00570                     _alpm_log(PM_LOG_ERROR, _("could not install %s as %s: %s\n"), filename, newpath, strerror(errno));
00571                     alpm_logaction("error: could not install %s as %s: %s\n", filename, newpath, strerror(errno));
00572                 } else {
00573                     _alpm_log(PM_LOG_WARNING, _("%s installed as %s\n"), filename, newpath);
00574                     alpm_logaction("warning: %s installed as %s\n", filename, newpath);
00575                 }
00576             }
00577         }
00578 
00579         FREE(hash_local);
00580         FREE(hash_pkg);
00581         FREE(hash_orig);
00582         unlink(tempfile);
00583         FREE(tempfile);
00584         close(fd);
00585     } else {
00586         /* we didn't need a backup */
00587         if(notouch) {
00588             /* change the path to a .pacnew extension */
00589             _alpm_log(PM_LOG_DEBUG, "%s is in NoUpgrade -- skipping\n", filename);
00590             _alpm_log(PM_LOG_WARNING, _("extracting %s as %s.pacnew\n"), filename, filename);
00591             alpm_logaction("warning: extracting %s as %s.pacnew\n", filename, filename);
00592             strncat(filename, ".pacnew", PATH_MAX - strlen(filename));
00593         } else {
00594             _alpm_log(PM_LOG_DEBUG, "extracting %s\n", filename);
00595         }
00596 
00597         if(trans->flags & PM_TRANS_FLAG_FORCE) {
00598             /* if FORCE was used, unlink() each file (whether it's there
00599              * or not) before extracting. This prevents the old "Text file busy"
00600              * error that crops up if forcing a glibc or pacman upgrade. */
00601             unlink(filename);
00602         }
00603 
00604         archive_entry_set_pathname(entry, filename);
00605 
00606         int ret = archive_read_extract(archive, entry, archive_flags);
00607         if(ret == ARCHIVE_WARN) {
00608             /* operation succeeded but a non-critical error was encountered */
00609             _alpm_log(PM_LOG_DEBUG, "warning extracting %s (%s)\n",
00610                     entryname, archive_error_string(archive));
00611         } else if(ret != ARCHIVE_OK) {
00612             _alpm_log(PM_LOG_ERROR, _("could not extract %s (%s)\n"),
00613                     entryname, archive_error_string(archive));
00614             alpm_logaction("error: could not extract %s (%s)\n",
00615                     entryname, archive_error_string(archive));
00616             return(1);
00617         }
00618 
00619         /* calculate an hash if this is in newpkg's backup */
00620         alpm_list_t *b;
00621         for(b = alpm_pkg_get_backup(newpkg); b; b = b->next) {
00622             char *backup = NULL, *hash = NULL;
00623             char *oldbackup = alpm_list_getdata(b);
00624             /* length is tab char, null byte and MD5 (32 char) */
00625             int backup_len = strlen(oldbackup) + 34;
00626 
00627             if(!oldbackup || strcmp(oldbackup, entryname) != 0) {
00628                 continue;
00629             }
00630             _alpm_log(PM_LOG_DEBUG, "appending backup entry for %s\n", filename);
00631 
00632             hash = alpm_get_md5sum(filename);
00633             backup = malloc(backup_len);
00634             if(!backup) {
00635                 RET_ERR(PM_ERR_MEMORY, -1);
00636             }
00637 
00638             sprintf(backup, "%s\t%s", oldbackup, hash);
00639             backup[backup_len-1] = '\0';
00640             FREE(hash);
00641             FREE(oldbackup);
00642             b->data = backup;
00643         }
00644     }
00645     return(errors);
00646 }
00647 
00648 static int commit_single_pkg(pmpkg_t *newpkg, int pkg_current, int pkg_count,
00649         pmtrans_t *trans, pmdb_t *db)
00650 {
00651     int i, ret = 0, errors = 0;
00652     struct archive *archive;
00653     struct archive_entry *entry;
00654     char cwd[PATH_MAX] = "";
00655     char scriptlet[PATH_MAX+1];
00656     int is_upgrade = 0;
00657     double percent = 0.0;
00658     pmpkg_t *oldpkg = NULL;
00659 
00660     snprintf(scriptlet, PATH_MAX, "%s%s-%s/install", db->path,
00661             alpm_pkg_get_name(newpkg), alpm_pkg_get_version(newpkg));
00662 
00663     /* see if this is an upgrade. if so, remove the old package first */
00664     pmpkg_t *local = _alpm_db_get_pkgfromcache(db, newpkg->name);
00665     if(local) {
00666         is_upgrade = 1;
00667 
00668         EVENT(trans, PM_TRANS_EVT_UPGRADE_START, newpkg, NULL);
00669         _alpm_log(PM_LOG_DEBUG, "upgrading package %s-%s\n",
00670                 newpkg->name, newpkg->version);
00671 
00672         /* we'll need to save some record for backup checks later */
00673         oldpkg = _alpm_pkg_dup(local);
00674         /* copy over the install reason (unless alldeps is set) */
00675     if(trans->flags & PM_TRANS_FLAG_ALLDEPS) {
00676         newpkg->reason = PM_PKG_REASON_DEPEND;
00677     } else {
00678         newpkg->reason = alpm_pkg_get_reason(local);
00679     }
00680 
00681         /* pre_upgrade scriptlet */
00682         if(alpm_pkg_has_scriptlet(newpkg) && !(trans->flags & PM_TRANS_FLAG_NOSCRIPTLET)) {
00683             _alpm_runscriptlet(handle->root, newpkg->origin_data.file,
00684                     "pre_upgrade", newpkg->version, oldpkg->version, trans);
00685         }
00686     } else {
00687         is_upgrade = 0;
00688 
00689         EVENT(trans, PM_TRANS_EVT_ADD_START, newpkg, NULL);
00690         _alpm_log(PM_LOG_DEBUG, "adding package %s-%s\n",
00691                 newpkg->name, newpkg->version);
00692 
00693         /* pre_install scriptlet */
00694         if(alpm_pkg_has_scriptlet(newpkg) && !(trans->flags & PM_TRANS_FLAG_NOSCRIPTLET)) {
00695             _alpm_runscriptlet(handle->root, newpkg->origin_data.file,
00696                     "pre_install", newpkg->version, NULL, trans);
00697         }
00698     }
00699 
00700     if(oldpkg) {
00701         /* set up fake remove transaction */
00702         int ret = upgrade_remove(oldpkg, newpkg, trans, db);
00703         if(ret != 0) {
00704             return(ret);
00705         }
00706     }
00707 
00708     if(!(trans->flags & PM_TRANS_FLAG_DBONLY)) {
00709         _alpm_log(PM_LOG_DEBUG, "extracting files\n");
00710 
00711         if ((archive = archive_read_new()) == NULL) {
00712             RET_ERR(PM_ERR_LIBARCHIVE_ERROR, -1);
00713         }
00714 
00715         archive_read_support_compression_all(archive);
00716         archive_read_support_format_all(archive);
00717 
00718         if(archive_read_open_filename(archive, newpkg->origin_data.file,
00719                     ARCHIVE_DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK) {
00720             RET_ERR(PM_ERR_PKG_OPEN, -1);
00721         }
00722 
00723         /* save the cwd so we can restore it later */
00724         if(getcwd(cwd, PATH_MAX) == NULL) {
00725             _alpm_log(PM_LOG_ERROR, _("could not get current working directory\n"));
00726             cwd[0] = 0;
00727         }
00728 
00729         /* libarchive requires this for extracting hard links */
00730         chdir(handle->root);
00731 
00732         /* call PROGRESS once with 0 percent, as we sort-of skip that here */
00733         if(is_upgrade) {
00734             PROGRESS(trans, PM_TRANS_PROGRESS_UPGRADE_START,
00735                     alpm_pkg_get_name(newpkg), 0, pkg_count, pkg_current);
00736         } else {
00737             PROGRESS(trans, PM_TRANS_PROGRESS_ADD_START,
00738                     alpm_pkg_get_name(newpkg), 0, pkg_count, pkg_current);
00739         }
00740 
00741         for(i = 0; archive_read_next_header(archive, &entry) == ARCHIVE_OK; i++) {
00742             if(newpkg->size != 0) {
00743                 /* Using compressed size for calculations here, as newpkg->isize is not
00744                  * exact when it comes to comparing to the ACTUAL uncompressed size
00745                  * (missing metadata sizes) */
00746                 unsigned long pos = archive_position_compressed(archive);
00747                 percent = (double)pos / (double)newpkg->size;
00748                 _alpm_log(PM_LOG_DEBUG, "decompression progress: %f%% (%ld / %ld)\n",
00749                         percent*100.0, pos, newpkg->size);
00750                 if(percent >= 1.0) {
00751                     percent = 1.0;
00752                 }
00753             }
00754 
00755             if(is_upgrade) {
00756                 PROGRESS(trans, PM_TRANS_PROGRESS_UPGRADE_START,
00757                         alpm_pkg_get_name(newpkg), (int)(percent * 100), pkg_count,
00758                         pkg_current);
00759             } else {
00760                 PROGRESS(trans, PM_TRANS_PROGRESS_ADD_START,
00761                         alpm_pkg_get_name(newpkg), (int)(percent * 100), pkg_count,
00762                         pkg_current);
00763             }
00764 
00765             /* extract the next file from the archive */
00766             errors += extract_single_file(archive, entry, newpkg, oldpkg,
00767                     trans, db);
00768         }
00769         archive_read_finish(archive);
00770 
00771         /* restore the old cwd is we have it */
00772         if(strlen(cwd)) {
00773             chdir(cwd);
00774         }
00775 
00776         if(errors) {
00777             ret = 1;
00778             if(is_upgrade) {
00779                 _alpm_log(PM_LOG_ERROR, _("problem occurred while upgrading %s\n"),
00780                         newpkg->name);
00781                 alpm_logaction("error: problem occurred while upgrading %s\n",
00782                         newpkg->name);
00783             } else {
00784                 _alpm_log(PM_LOG_ERROR, _("problem occurred while installing %s\n"),
00785                         newpkg->name);
00786                 alpm_logaction("error: problem occurred while installing %s\n",
00787                         newpkg->name);
00788             }
00789         }
00790     }
00791 
00792     /* make an install date (in UTC) */
00793     newpkg->installdate = time(NULL);
00794 
00795     _alpm_log(PM_LOG_DEBUG, "updating database\n");
00796     _alpm_log(PM_LOG_DEBUG, "adding database entry '%s'\n", newpkg->name);
00797 
00798     if(_alpm_db_write(db, newpkg, INFRQ_ALL)) {
00799         _alpm_log(PM_LOG_ERROR, _("could not update database entry %s-%s\n"),
00800                 alpm_pkg_get_name(newpkg), alpm_pkg_get_version(newpkg));
00801         alpm_logaction("error: could not update database entry %s-%s\n",
00802                 alpm_pkg_get_name(newpkg), alpm_pkg_get_version(newpkg));
00803         RET_ERR(PM_ERR_DB_WRITE, -1);
00804     }
00805 
00806     if(_alpm_db_add_pkgincache(db, newpkg) == -1) {
00807         _alpm_log(PM_LOG_ERROR, _("could not add entry '%s' in cache\n"),
00808                 alpm_pkg_get_name(newpkg));
00809     }
00810 
00811     if(is_upgrade) {
00812         PROGRESS(trans, PM_TRANS_PROGRESS_UPGRADE_START,
00813                 alpm_pkg_get_name(newpkg), 100, pkg_count, pkg_current);
00814     } else {
00815         PROGRESS(trans, PM_TRANS_PROGRESS_ADD_START,
00816                 alpm_pkg_get_name(newpkg), 100, pkg_count, pkg_current);
00817     }
00818     EVENT(trans, PM_TRANS_EVT_EXTRACT_DONE, NULL, NULL);
00819 
00820     /* run the post-install script if it exists  */
00821     if(alpm_pkg_has_scriptlet(newpkg)
00822             && !(trans->flags & PM_TRANS_FLAG_NOSCRIPTLET)) {
00823         if(is_upgrade) {
00824             _alpm_runscriptlet(handle->root, scriptlet, "post_upgrade",
00825                     alpm_pkg_get_version(newpkg),
00826                     oldpkg ? alpm_pkg_get_version(oldpkg) : NULL, trans);
00827         } else {
00828             _alpm_runscriptlet(handle->root, scriptlet, "post_install",
00829                     alpm_pkg_get_version(newpkg), NULL, trans);
00830         }
00831     }
00832 
00833     if(is_upgrade) {
00834         EVENT(trans, PM_TRANS_EVT_UPGRADE_DONE, newpkg, oldpkg);
00835     } else {
00836         EVENT(trans, PM_TRANS_EVT_ADD_DONE, newpkg, oldpkg);
00837     }
00838 
00839     _alpm_pkg_free(oldpkg);
00840 
00841     return(0);
00842 }
00843 
00844 int _alpm_add_commit(pmtrans_t *trans, pmdb_t *db)
00845 {
00846     int pkg_count, pkg_current;
00847     alpm_list_t *targ;
00848 
00849     ALPM_LOG_FUNC;
00850 
00851     ASSERT(trans != NULL, RET_ERR(PM_ERR_TRANS_NULL, -1));
00852     ASSERT(db != NULL, RET_ERR(PM_ERR_DB_NULL, -1));
00853 
00854     if(trans->packages == NULL) {
00855         return(0);
00856     }
00857 
00858     pkg_count = alpm_list_count(trans->targets);
00859     pkg_current = 1;
00860 
00861     /* loop through our package list adding/upgrading one at a time */
00862     for(targ = trans->packages; targ; targ = targ->next) {
00863         if(handle->trans->state == STATE_INTERRUPTED) {
00864             return(0);
00865         }
00866 
00867         pmpkg_t *newpkg = (pmpkg_t *)targ->data;
00868         commit_single_pkg(newpkg, pkg_current, pkg_count, trans, db);
00869         pkg_current++;
00870     }
00871 
00872     /* run ldconfig if it exists */
00873     _alpm_log(PM_LOG_DEBUG, "running \"ldconfig -r %s\"\n", handle->root);
00874     _alpm_ldconfig(handle->root);
00875 
00876     return(0);
00877 }
00878 
00879 /* vim: set ts=2 sw=2 noet: */

Generated on Mon Jan 14 23:53:39 2008 for libalpm by  doxygen 1.5.4