/** * @brief Remove needed packages from the removal transaction. * * @param handle the context handle * @param lp list of missing dependencies caused by the removal transaction */ static void remove_prepare_keep_needed(alpm_handle_t *handle, alpm_list_t *lp) { alpm_trans_t *trans = handle->trans; /* Remove needed packages (which break dependencies) from target list */ while(lp != NULL) { alpm_list_t *i; for(i = lp; i; i = i->next) { alpm_depmissing_t *miss = i->data; void *vpkg; alpm_pkg_t *pkg = alpm_pkg_find(trans->remove, miss->causingpkg); if(pkg == NULL) { continue; } trans->remove = alpm_list_remove(trans->remove, pkg, _alpm_pkg_cmp, &vpkg); pkg = vpkg; if(pkg) { _alpm_log(handle, ALPM_LOG_WARNING, _("removing %s from target list\n"), pkg->name); _alpm_pkg_free(pkg); } } alpm_list_free_inner(lp, (alpm_list_fn_free)_alpm_depmiss_free); alpm_list_free(lp); lp = alpm_checkdeps(handle, _alpm_db_get_pkgcache(handle->db_local), trans->remove, NULL, 1); } }
static int local_db_populate(alpm_db_t *db) { size_t est_count; int count = 0; struct stat buf; struct dirent *ent = NULL; const char *dbpath; DIR *dbdir; if(db->status & DB_STATUS_INVALID) { RET_ERR(db->handle, ALPM_ERR_DB_INVALID, -1); } /* note: DB_STATUS_MISSING is not fatal for local database */ dbpath = _alpm_db_path(db); if(dbpath == NULL) { /* pm_errno set in _alpm_db_path() */ return -1; } dbdir = opendir(dbpath); if(dbdir == NULL) { if(errno == ENOENT) { /* no database existing yet is not an error */ db->status &= ~DB_STATUS_EXISTS; db->status |= DB_STATUS_MISSING; return 0; } RET_ERR(db->handle, ALPM_ERR_DB_OPEN, -1); } if(fstat(dirfd(dbdir), &buf) != 0) { RET_ERR(db->handle, ALPM_ERR_DB_OPEN, -1); } db->status |= DB_STATUS_EXISTS; db->status &= ~DB_STATUS_MISSING; if(buf.st_nlink >= 2) { est_count = buf.st_nlink; } else { /* Some filesystems don't subscribe to the two-implicit links school of * thought, e.g. BTRFS, HFS+. See * http://kerneltrap.org/mailarchive/linux-btrfs/2010/1/23/6723483/thread */ est_count = 0; while(readdir(dbdir) != NULL) { est_count++; } rewinddir(dbdir); } if(est_count >= 2) { /* subtract the two extra pointers to get # of children */ est_count -= 2; } /* initialize hash at 50% full */ db->pkgcache = _alpm_pkghash_create(est_count * 2); if(db->pkgcache == NULL){ closedir(dbdir); RET_ERR(db->handle, ALPM_ERR_MEMORY, -1); } while((ent = readdir(dbdir)) != NULL) { const char *name = ent->d_name; alpm_pkg_t *pkg; if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { continue; } if(!is_dir(dbpath, ent)) { continue; } pkg = _alpm_pkg_new(); if(pkg == NULL) { closedir(dbdir); RET_ERR(db->handle, ALPM_ERR_MEMORY, -1); } /* split the db entry name */ if(_alpm_splitname(name, &(pkg->name), &(pkg->version), &(pkg->name_hash)) != 0) { _alpm_log(db->handle, ALPM_LOG_ERROR, _("invalid name for database entry '%s'\n"), name); _alpm_pkg_free(pkg); continue; } /* duplicated database entries are not allowed */ if(_alpm_pkghash_find(db->pkgcache, pkg->name)) { _alpm_log(db->handle, ALPM_LOG_ERROR, _("duplicated database entry '%s'\n"), pkg->name); _alpm_pkg_free(pkg); continue; } pkg->origin = PKG_FROM_LOCALDB; pkg->origin_data.db = db; pkg->ops = &local_pkg_ops; pkg->handle = db->handle; /* explicitly read with only 'BASE' data, accessors will handle the rest */ if(local_db_read(pkg, INFRQ_BASE) == -1) { _alpm_log(db->handle, ALPM_LOG_ERROR, _("corrupted database entry '%s'\n"), name); _alpm_pkg_free(pkg); continue; } /* add to the collection */ _alpm_log(db->handle, ALPM_LOG_FUNCTION, "adding '%s' to package cache for db '%s'\n", pkg->name, db->treename); db->pkgcache = _alpm_pkghash_add(db->pkgcache, pkg); count++; } closedir(dbdir); if(count > 0) { db->pkgcache->list = alpm_list_msort(db->pkgcache->list, (size_t)count, _alpm_pkg_cmp); } _alpm_log(db->handle, ALPM_LOG_DEBUG, "added %d packages to package cache for db '%s'\n", count, db->treename); return count; }
static int extract_single_file(alpm_handle_t *handle, struct archive *archive, struct archive_entry *entry, alpm_pkg_t *newpkg, alpm_pkg_t *oldpkg) { const char *entryname = archive_entry_pathname(entry); mode_t entrymode = archive_entry_mode(entry); alpm_backup_t *backup = _alpm_needbackup(entryname, newpkg); char filename[PATH_MAX]; /* the actual file we're extracting */ int needbackup = 0, notouch = 0; const char *hash_orig = NULL; int errors = 0; struct stat lsbuf; size_t filename_len; if(*entryname == '.') { return extract_db_file(handle, archive, entry, newpkg, entryname); } if (!alpm_filelist_contains(&newpkg->files, entryname)) { _alpm_log(handle, ALPM_LOG_WARNING, _("file not found in file list for package %s. skipping extraction of %s\n"), newpkg->name, entryname); return 0; } /* build the new entryname relative to handle->root */ filename_len = snprintf(filename, PATH_MAX, "%s%s", handle->root, entryname); if(filename_len >= PATH_MAX) { _alpm_log(handle, ALPM_LOG_ERROR, _("unable to extract %s%s: path too long"), handle->root, entryname); return 1; } /* if a file is in NoExtract then we never extract it */ if(_alpm_fnmatch_patterns(handle->noextract, entryname) == 0) { _alpm_log(handle, ALPM_LOG_DEBUG, "%s is in NoExtract," " skipping extraction of %s\n", entryname, filename); archive_read_data_skip(archive); return 0; } /* Check for file existence. This is one of the more crucial parts * to get 'right'. Here are the possibilities, with the filesystem * on the left and the package on the top: * (F=file, N=node, S=symlink, D=dir) * | F/N | D * non-existent | 1 | 2 * F/N | 3 | 4 * D | 5 | 6 * * 1,2- extract, no magic necessary. lstat (llstat) will fail here. * 3,4- conflict checks should have caught this. either overwrite * or backup the file. * 5- file replacing directory- don't allow it. * 6- skip extraction, dir already exists. */ if(llstat(filename, &lsbuf) != 0) { /* cases 1,2: file doesn't exist, skip all backup checks */ } else if(S_ISDIR(lsbuf.st_mode) && S_ISDIR(entrymode)) { #if 0 uid_t entryuid = archive_entry_uid(entry); gid_t entrygid = archive_entry_gid(entry); #endif /* case 6: existing dir, ignore it */ if(lsbuf.st_mode != entrymode) { /* if filesystem perms are different than pkg perms, warn user */ mode_t mask = 07777; _alpm_log(handle, ALPM_LOG_WARNING, _("directory permissions differ on %s\n" "filesystem: %o package: %o\n"), filename, lsbuf.st_mode & mask, entrymode & mask); alpm_logaction(handle, ALPM_CALLER_PREFIX, "warning: directory permissions differ on %s\n" "filesystem: %o package: %o\n", filename, lsbuf.st_mode & mask, entrymode & mask); } #if 0 /* Disable this warning until our user management in packages has improved. Currently many packages have to create users in post_install and chown the directories. These all resulted in "false-positive" warnings. */ if((entryuid != lsbuf.st_uid) || (entrygid != lsbuf.st_gid)) { _alpm_log(handle, ALPM_LOG_WARNING, _("directory ownership differs on %s\n" "filesystem: %u:%u package: %u:%u\n"), filename, lsbuf.st_uid, lsbuf.st_gid, entryuid, entrygid); alpm_logaction(handle, ALPM_CALLER_PREFIX, "warning: directory ownership differs on %s\n" "filesystem: %u:%u package: %u:%u\n", filename, lsbuf.st_uid, lsbuf.st_gid, entryuid, entrygid); } #endif _alpm_log(handle, ALPM_LOG_DEBUG, "extract: skipping dir extraction of %s\n", filename); archive_read_data_skip(archive); return 0; } else if(S_ISDIR(lsbuf.st_mode)) { /* case 5: trying to overwrite dir with file, don't allow it */ _alpm_log(handle, ALPM_LOG_ERROR, _("extract: not overwriting dir with file %s\n"), filename); archive_read_data_skip(archive); return 1; } else if(S_ISDIR(entrymode)) { /* case 4: trying to overwrite file with dir */ _alpm_log(handle, ALPM_LOG_DEBUG, "extract: overwriting file with dir %s\n", filename); } else { /* case 3: trying to overwrite file with file */ /* if file is in NoUpgrade, don't touch it */ if(_alpm_fnmatch_patterns(handle->noupgrade, entryname) == 0) { notouch = 1; } else { alpm_backup_t *oldbackup; if(oldpkg && (oldbackup = _alpm_needbackup(entryname, oldpkg))) { hash_orig = oldbackup->hash; needbackup = 1; } else if(backup) { /* allow adding backup files retroactively */ needbackup = 1; } } } if(notouch || needbackup) { if(filename_len + strlen(".pacnew") >= PATH_MAX) { _alpm_log(handle, ALPM_LOG_ERROR, _("unable to extract %s.pacnew: path too long"), filename); return 1; } strcpy(filename + filename_len, ".pacnew"); } if(handle->trans->flags & ALPM_TRANS_FLAG_FORCE) { /* if FORCE was used, unlink() each file (whether it's there * or not) before extracting. This prevents the old "Text file busy" * error that crops up if forcing a glibc or pacman upgrade. */ unlink(filename); } _alpm_log(handle, ALPM_LOG_DEBUG, "extracting %s\n", filename); if(perform_extraction(handle, archive, entry, filename)) { errors++; return errors; } if(backup) { FREE(backup->hash); backup->hash = alpm_compute_md5sum(filename); } if(notouch) { alpm_event_pacnew_created_t event = { .type = ALPM_EVENT_PACNEW_CREATED, .from_noupgrade = 1, .oldpkg = oldpkg, .newpkg = newpkg, .file = filename }; /* "remove" the .pacnew suffix */ filename[filename_len] = '\0'; EVENT(handle, &event); alpm_logaction(handle, ALPM_CALLER_PREFIX, "warning: %s installed as %s.pacnew\n", filename, filename); } else if(needbackup) { char *hash_local = NULL, *hash_pkg = NULL; char origfile[PATH_MAX] = ""; strncat(origfile, filename, filename_len); hash_local = alpm_compute_md5sum(origfile); hash_pkg = backup ? backup->hash : alpm_compute_md5sum(filename); _alpm_log(handle, ALPM_LOG_DEBUG, "checking hashes for %s\n", origfile); _alpm_log(handle, ALPM_LOG_DEBUG, "current: %s\n", hash_local); _alpm_log(handle, ALPM_LOG_DEBUG, "new: %s\n", hash_pkg); _alpm_log(handle, ALPM_LOG_DEBUG, "original: %s\n", hash_orig); if(hash_local && hash_pkg && strcmp(hash_local, hash_pkg) == 0) { /* local and new files are the same, updating anyway to get * correct timestamps */ _alpm_log(handle, ALPM_LOG_DEBUG, "action: installing new file: %s\n", origfile); if(try_rename(handle, filename, origfile)) { errors++; } } else if(hash_orig && hash_pkg && strcmp(hash_orig, hash_pkg) == 0) { /* original and new files are the same, leave the local version alone, * including any user changes */ _alpm_log(handle, ALPM_LOG_DEBUG, "action: leaving existing file in place\n"); unlink(filename); } else if(hash_orig && hash_local && strcmp(hash_orig, hash_local) == 0) { /* installed file has NOT been changed by user, * update to the new version */ _alpm_log(handle, ALPM_LOG_DEBUG, "action: installing new file: %s\n", origfile); if(try_rename(handle, filename, origfile)) { errors++; } } else { /* none of the three files matched another, leave the unpacked * file alongside the local file */ alpm_event_pacnew_created_t event = { .type = ALPM_EVENT_PACNEW_CREATED, .from_noupgrade = 0, .oldpkg = oldpkg, .newpkg = newpkg, .file = origfile }; _alpm_log(handle, ALPM_LOG_DEBUG, "action: keeping current file and installing" " new one with .pacnew ending\n"); EVENT(handle, &event); alpm_logaction(handle, ALPM_CALLER_PREFIX, "warning: %s installed as %s\n", origfile, filename); } free(hash_local); if(!backup) { free(hash_pkg); } } return errors; } static int commit_single_pkg(alpm_handle_t *handle, alpm_pkg_t *newpkg, size_t pkg_current, size_t pkg_count) { int i, ret = 0, errors = 0; int is_upgrade = 0; alpm_pkg_t *oldpkg = NULL; alpm_db_t *db = handle->db_local; alpm_trans_t *trans = handle->trans; alpm_progress_t progress = ALPM_PROGRESS_ADD_START; alpm_event_package_operation_t event; const char *log_msg = "adding"; const char *pkgfile; ASSERT(trans != NULL, return -1); /* see if this is an upgrade. if so, remove the old package first */ alpm_pkg_t *local = _alpm_db_get_pkgfromcache(db, newpkg->name); if(local) { int cmp = _alpm_pkg_compare_versions(newpkg, local); if(cmp < 0) { log_msg = "downgrading"; progress = ALPM_PROGRESS_DOWNGRADE_START; event.operation = ALPM_PACKAGE_DOWNGRADE; } else if(cmp == 0) { log_msg = "reinstalling"; progress = ALPM_PROGRESS_REINSTALL_START; event.operation = ALPM_PACKAGE_REINSTALL; } else { log_msg = "upgrading"; progress = ALPM_PROGRESS_UPGRADE_START; event.operation = ALPM_PACKAGE_UPGRADE; } is_upgrade = 1; /* we'll need to save some record for backup checks later */ if(_alpm_pkg_dup(local, &oldpkg) == -1) { ret = -1; goto cleanup; } /* copy over the install reason */ newpkg->reason = alpm_pkg_get_reason(local); } else { event.operation = ALPM_PACKAGE_INSTALL; } event.type = ALPM_EVENT_PACKAGE_OPERATION_START; event.oldpkg = oldpkg; event.newpkg = newpkg; EVENT(handle, &event); pkgfile = newpkg->origin_data.file; _alpm_log(handle, ALPM_LOG_DEBUG, "%s package %s-%s\n", log_msg, newpkg->name, newpkg->version); /* pre_install/pre_upgrade scriptlet */ if(alpm_pkg_has_scriptlet(newpkg) && !(trans->flags & ALPM_TRANS_FLAG_NOSCRIPTLET)) { const char *scriptlet_name = is_upgrade ? "pre_upgrade" : "pre_install"; _alpm_runscriptlet(handle, pkgfile, scriptlet_name, newpkg->version, oldpkg ? oldpkg->version : NULL, 1); } /* we override any pre-set reason if we have alldeps or allexplicit set */ if(trans->flags & ALPM_TRANS_FLAG_ALLDEPS) { newpkg->reason = ALPM_PKG_REASON_DEPEND; } else if(trans->flags & ALPM_TRANS_FLAG_ALLEXPLICIT) { newpkg->reason = ALPM_PKG_REASON_EXPLICIT; } if(oldpkg) { /* set up fake remove transaction */ if(_alpm_remove_single_package(handle, oldpkg, newpkg, 0, 0) == -1) { handle->pm_errno = ALPM_ERR_TRANS_ABORT; ret = -1; goto cleanup; } } /* prepare directory for database entries so permission are correct after changelog/install script installation */ if(_alpm_local_db_prepare(db, newpkg)) { alpm_logaction(handle, ALPM_CALLER_PREFIX, "error: could not create database entry %s-%s\n", newpkg->name, newpkg->version); handle->pm_errno = ALPM_ERR_DB_WRITE; ret = -1; goto cleanup; } if(!(trans->flags & ALPM_TRANS_FLAG_DBONLY)) { struct archive *archive; struct archive_entry *entry; struct stat buf; int fd, cwdfd; _alpm_log(handle, ALPM_LOG_DEBUG, "extracting files\n"); fd = _alpm_open_archive(db->handle, pkgfile, &buf, &archive, ALPM_ERR_PKG_OPEN); if(fd < 0) { ret = -1; goto cleanup; } /* save the cwd so we can restore it later */ OPEN(cwdfd, ".", O_RDONLY | O_CLOEXEC); if(cwdfd < 0) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not get current working directory\n")); } /* libarchive requires this for extracting hard links */ if(chdir(handle->root) != 0) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not change directory to %s (%s)\n"), handle->root, strerror(errno)); _alpm_archive_read_free(archive); close(fd); ret = -1; goto cleanup; } /* call PROGRESS once with 0 percent, as we sort-of skip that here */ PROGRESS(handle, progress, newpkg->name, 0, pkg_count, pkg_current); for(i = 0; archive_read_next_header(archive, &entry) == ARCHIVE_OK; i++) { int percent; if(newpkg->size != 0) { /* Using compressed size for calculations here, as newpkg->isize is not * exact when it comes to comparing to the ACTUAL uncompressed size * (missing metadata sizes) */ int64_t pos = _alpm_archive_compressed_ftell(archive); percent = (pos * 100) / newpkg->size; if(percent >= 100) { percent = 100; } } else { percent = 0; } PROGRESS(handle, progress, newpkg->name, percent, pkg_count, pkg_current); /* extract the next file from the archive */ errors += extract_single_file(handle, archive, entry, newpkg, oldpkg); } _alpm_archive_read_free(archive); close(fd); /* restore the old cwd if we have it */ if(cwdfd >= 0) { if(fchdir(cwdfd) != 0) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not restore working directory (%s)\n"), strerror(errno)); } close(cwdfd); } if(errors) { ret = -1; if(is_upgrade) { _alpm_log(handle, ALPM_LOG_ERROR, _("problem occurred while upgrading %s\n"), newpkg->name); alpm_logaction(handle, ALPM_CALLER_PREFIX, "error: problem occurred while upgrading %s\n", newpkg->name); } else { _alpm_log(handle, ALPM_LOG_ERROR, _("problem occurred while installing %s\n"), newpkg->name); alpm_logaction(handle, ALPM_CALLER_PREFIX, "error: problem occurred while installing %s\n", newpkg->name); } } } /* make an install date (in UTC) */ newpkg->installdate = time(NULL); _alpm_log(handle, ALPM_LOG_DEBUG, "updating database\n"); _alpm_log(handle, ALPM_LOG_DEBUG, "adding database entry '%s'\n", newpkg->name); if(_alpm_local_db_write(db, newpkg, INFRQ_ALL)) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not update database entry %s-%s\n"), newpkg->name, newpkg->version); alpm_logaction(handle, ALPM_CALLER_PREFIX, "error: could not update database entry %s-%s\n", newpkg->name, newpkg->version); handle->pm_errno = ALPM_ERR_DB_WRITE; ret = -1; goto cleanup; } if(_alpm_db_add_pkgincache(db, newpkg) == -1) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not add entry '%s' in cache\n"), newpkg->name); } PROGRESS(handle, progress, newpkg->name, 100, pkg_count, pkg_current); switch(event.operation) { case ALPM_PACKAGE_INSTALL: alpm_logaction(handle, ALPM_CALLER_PREFIX, "installed %s (%s)\n", newpkg->name, newpkg->version); break; case ALPM_PACKAGE_DOWNGRADE: alpm_logaction(handle, ALPM_CALLER_PREFIX, "downgraded %s (%s -> %s)\n", newpkg->name, oldpkg->version, newpkg->version); break; case ALPM_PACKAGE_REINSTALL: alpm_logaction(handle, ALPM_CALLER_PREFIX, "reinstalled %s (%s)\n", newpkg->name, newpkg->version); break; case ALPM_PACKAGE_UPGRADE: alpm_logaction(handle, ALPM_CALLER_PREFIX, "upgraded %s (%s -> %s)\n", newpkg->name, oldpkg->version, newpkg->version); break; default: /* we should never reach here */ break; } /* run the post-install script if it exists */ if(alpm_pkg_has_scriptlet(newpkg) && !(trans->flags & ALPM_TRANS_FLAG_NOSCRIPTLET)) { char *scriptlet = _alpm_local_db_pkgpath(db, newpkg, "install"); const char *scriptlet_name = is_upgrade ? "post_upgrade" : "post_install"; _alpm_runscriptlet(handle, scriptlet, scriptlet_name, newpkg->version, oldpkg ? oldpkg->version : NULL, 0); free(scriptlet); } event.type = ALPM_EVENT_PACKAGE_OPERATION_DONE; EVENT(handle, &event); cleanup: _alpm_pkg_free(oldpkg); return ret; }