]> git.ipfire.org Git - people/stevee/pakfire.git/blobdiff - src/libpakfire/transaction.c
digests: Split off into a new set of files
[people/stevee/pakfire.git] / src / libpakfire / transaction.c
index 3cbab27b547bba01dea4bee2321883b92a5c428e..f2f1fbce0f1c87ba289eb590df5dced959eb103f 100644 (file)
 #include <errno.h>
 #include <stdlib.h>
 
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+
+#include <solv/pool_fileconflicts.h>
 #include <solv/transaction.h>
 
 #include <pakfire/archive.h>
 #include <pakfire/db.h>
+#include <pakfire/digest.h>
 #include <pakfire/downloader.h>
-#include <pakfire/execute.h>
+#include <pakfire/file.h>
+#include <pakfire/filelist.h>
 #include <pakfire/i18n.h>
+#include <pakfire/jail.h>
 #include <pakfire/logging.h>
 #include <pakfire/package.h>
 #include <pakfire/pakfire.h>
 #include <pakfire/private.h>
 #include <pakfire/repo.h>
+#include <pakfire/string.h>
 #include <pakfire/transaction.h>
-#include <pakfire/types.h>
+#include <pakfire/ui.h>
 #include <pakfire/util.h>
 
 struct pakfire_transaction {
-       Pakfire pakfire;
+       struct pakfire* pakfire;
        int nrefs;
 
        Transaction* transaction;
+       char** userinstalled;
 
-       PakfireArchive* archives;
-       PakfirePackage* packages;
+       struct pakfire_archive** archives;
+       struct pakfire_package** packages;
        size_t num;
+       size_t progress;
+
+       // Callbacks
+       struct pakfire_transaction_callbacks {
+               // Status
+               pakfire_status_callback status;
+               void* status_data;
+       } callbacks;
+};
+
+enum pakfire_actions {
+       PAKFIRE_ACTION_NOOP = 0,
+       PAKFIRE_ACTION_VERIFY,
+       PAKFIRE_ACTION_EXECUTE,
+       PAKFIRE_ACTION_PRETRANS,
+       PAKFIRE_ACTION_POSTTRANS,
+};
+
+enum pakfire_steps {
+       PAKFIRE_STEP_IGNORE = 0,
+       PAKFIRE_STEP_INSTALL,
+       PAKFIRE_STEP_REINSTALL,
+       PAKFIRE_STEP_ERASE,
+       PAKFIRE_STEP_UPGRADE,
+       PAKFIRE_STEP_DOWNGRADE,
+       PAKFIRE_STEP_OBSOLETE,
 };
 
-static pakfire_step_type_t pakfire_transaction_get_step_type(
-               struct pakfire_transaction* transaction, PakfirePackage pkg) {
-       int type = transaction_type(transaction->transaction, pakfire_package_id(pkg), 0);
+static enum pakfire_steps pakfire_transaction_get_step_type(
+               struct pakfire_transaction* transaction, struct pakfire_package* pkg) {
+       int type = transaction_type(transaction->transaction, pakfire_package_id(pkg),
+               SOLVER_TRANSACTION_SHOW_ACTIVE|SOLVER_TRANSACTION_CHANGE_IS_REINSTALL);
 
        // Translate solver types into our own types
        switch (type) {
@@ -107,6 +143,11 @@ static void pakfire_transaction_free_archives_and_packages(
 static void pakfire_transaction_free(struct pakfire_transaction* transaction) {
        pakfire_transaction_free_archives_and_packages(transaction);
 
+       if (transaction->userinstalled) {
+               for (char** userinstalled = transaction->userinstalled; *userinstalled; userinstalled++)
+                       free(*userinstalled);
+               free(transaction->userinstalled);
+       }
        transaction_free(transaction->transaction);
 
        pakfire_unref(transaction->pakfire);
@@ -114,13 +155,20 @@ static void pakfire_transaction_free(struct pakfire_transaction* transaction) {
 }
 
 static int pakfire_transaction_import_transaction(
-               struct pakfire_transaction* transaction, Transaction* t) {
-       // Free any previous content
-       pakfire_transaction_free_archives_and_packages(transaction);
+               struct pakfire_transaction* transaction, Solver* solver) {
+       // Clone the transaction to keep a copy of it
+       Transaction* t = solver_create_transaction(solver);
+       if (!t)
+               return 1;
+
+       transaction->transaction = t;
 
        // Order the transaction
        transaction_order(t, 0);
 
+       // Free any previous content
+       pakfire_transaction_free_archives_and_packages(transaction);
+
        // How many steps?
        transaction->num = t->steps.count;
 
@@ -143,8 +191,42 @@ static int pakfire_transaction_import_transaction(
        return 0;
 }
 
+static int pakfire_transaction_import_userinstalled(
+               struct pakfire_transaction* t, Solver* solver) {
+       if (t->userinstalled) {
+               free(t->userinstalled);
+               t->userinstalled = NULL;
+       }
+
+       Queue userinstalled;
+       queue_init(&userinstalled);
+
+       // Fetch a list of all packages that are installed by the user
+       solver_get_userinstalled(solver, &userinstalled, GET_USERINSTALLED_NAMES);
+
+       // Skip everything if the queue is empty
+       if (!userinstalled.count)
+               return 0;
+
+       t->userinstalled = calloc(userinstalled.count + 1, sizeof(*t->userinstalled));
+       if (!t->userinstalled) {
+               ERROR(t->pakfire, "Could not allocate userinstalled\n");
+               return 1;
+       }
+
+       Pool* pool = pakfire_get_solv_pool(t->pakfire);
+
+       // Store the names of all userinstalled packages
+       for (int i = 0; i < userinstalled.count; i++) {
+               const char* package = pool_id2str(pool, userinstalled.elements[i]);
+               t->userinstalled[i] = strdup(package);
+       }
+
+       return 0;
+}
+
 int pakfire_transaction_create(struct pakfire_transaction** transaction,
-               Pakfire pakfire, Transaction* trans) {
+               struct pakfire* pakfire, Solver* solver) {
        struct pakfire_transaction* t = calloc(1, sizeof(*t));
        if (!t)
                return ENOMEM;
@@ -155,13 +237,13 @@ int pakfire_transaction_create(struct pakfire_transaction** transaction,
        // Initialize the reference counter
        t->nrefs = 1;
 
-       // Clone the transaction to keep a copy of it
-       t->transaction = transaction_create_clone(trans);
-       if (!t->transaction)
+       // Import transaction
+       int r = pakfire_transaction_import_transaction(t, solver);
+       if (r)
                goto ERROR;
 
-       // Import transaction
-       int r = pakfire_transaction_import_transaction(t, t->transaction);
+       // Import userinstalled
+       r = pakfire_transaction_import_userinstalled(t, solver);
        if (r)
                goto ERROR;
 
@@ -170,7 +252,6 @@ int pakfire_transaction_create(struct pakfire_transaction** transaction,
 
 ERROR:
        pakfire_transaction_free(t);
-
        return 1;
 }
 
@@ -190,6 +271,48 @@ PAKFIRE_EXPORT struct pakfire_transaction* pakfire_transaction_unref(
        return NULL;
 }
 
+void pakfire_transaction_set_status_callback(struct pakfire_transaction* transaction,
+               pakfire_status_callback callback, void* data) {
+       transaction->callbacks.status = callback;
+       transaction->callbacks.status_data = data;
+}
+
+static int pakfire_transaction_get_progress(struct pakfire_transaction* transaction) {
+       return transaction->progress * 100 / transaction->num;
+}
+
+static void pakfire_transaction_status(struct pakfire_transaction* transaction,
+               const char* message, ...) {
+       char* buffer = NULL;
+       va_list args;
+       int r;
+
+       // Do nothing if callback isn't set
+       if (!transaction->callbacks.status)
+               return;
+
+       // Format the message
+       if (message) {
+               va_start(args, message);
+               r = vasprintf(&buffer, message, args);
+               va_end(args);
+
+               if (r < 0)
+                       return;
+       }
+
+       // Fetch progress
+       const int progress = pakfire_transaction_get_progress(transaction);
+
+       // Call the callback
+       transaction->callbacks.status(transaction->pakfire,
+               transaction->callbacks.status_data, progress, buffer);
+
+       // Cleanup
+       if (buffer)
+               free(buffer);
+}
+
 PAKFIRE_EXPORT size_t pakfire_transaction_count(struct pakfire_transaction* transaction) {
        return transaction->num;
 }
@@ -201,7 +324,7 @@ static ssize_t pakfire_transaction_installsizechange(struct pakfire_transaction*
        return sizechange * 1024;
 }
 
-PAKFIRE_EXPORT ssize_t pakfire_transaction_downloadsize(struct pakfire_transaction* transaction) {
+static ssize_t pakfire_transaction_downloadsize(struct pakfire_transaction* transaction) {
        ssize_t size = 0;
 
        for (unsigned int i = 0; i < transaction->num; i++)
@@ -210,29 +333,63 @@ PAKFIRE_EXPORT ssize_t pakfire_transaction_downloadsize(struct pakfire_transacti
        return size;
 }
 
-static void pakfire_transaction_add_headline(char** str, size_t width, const char* headline) {
-       asprintf(str, "%s%s\n", *str, headline);
+static void pakfire_transaction_append_line(char*** lines, const char* format, ...) {
+       if (!lines)
+               return;
+
+       char* buffer = NULL;
+       va_list args;
+       int r;
+
+       va_start(args, format);
+       r = vasprintf(&buffer, format, args);
+       va_end(args);
+
+       if (r < 0)
+               return;
+
+       // Count lines
+       unsigned int count = 0;
+       if (*lines) {
+               for (char** l = *lines; *l; l++)
+                       count++;
+       }
+
+       // Increase size of array
+       *lines = reallocarray(*lines, count + 2, sizeof(**lines));
+       if (!*lines)
+               return;
+
+       // Append line and terminate lines
+       (*lines)[count] = buffer;
+       (*lines)[count + 1] = NULL;
+}
+
+static void pakfire_transaction_add_headline(char*** lines, size_t width, const char* headline) {
+       pakfire_transaction_append_line(lines, "%s\n", headline);
 }
 
-static void pakfire_transaction_add_newline(char** str, size_t width) {
-       asprintf(str, "%s\n", *str);
+static void pakfire_transaction_add_newline(char*** lines, size_t width) {
+       pakfire_transaction_append_line(lines, "\n");
 }
 
-static void pakfire_transaction_add_line(char** str, size_t width, const char* name,
+static void pakfire_transaction_add_line(char*** lines, size_t width, const char* name,
                const char* arch, const char* version, const char* repo, const char* size) {
-       // XXX need to adapt to size
-       asprintf(str, "%s %-21s %-8s %-21s %-18s %6s \n", *str, name, arch, version, repo, size);
+       pakfire_transaction_append_line(lines, " %-21s %-8s %-21s %-18s %6s \n",
+               name, arch, version, repo, size);
 }
 
-static void pakfire_transaction_add_package(char** str, size_t width, PakfirePackage pkg) {
+static void pakfire_transaction_add_package(char*** lines, size_t width, struct pakfire_package* pkg) {
        char size[128];
 
-       PakfireRepo repo = pakfire_package_get_repo(pkg);
+       struct pakfire_repo* repo = pakfire_package_get_repo(pkg);
 
        // Format size
-       pakfire_format_size(size, sizeof(size) - 1, pakfire_package_get_size(pkg));
+       int r = pakfire_format_size(size, pakfire_package_get_size(pkg));
+       if (r < 0)
+               return;
 
-       pakfire_transaction_add_line(str, width,
+       pakfire_transaction_add_line(lines, width,
                pakfire_package_get_name(pkg),
                pakfire_package_get_arch(pkg),
                pakfire_package_get_evr(pkg),
@@ -243,48 +400,80 @@ static void pakfire_transaction_add_package(char** str, size_t width, PakfirePac
        pakfire_repo_unref(repo);
 }
 
-static void pakfire_transaction_add_package_change(char** str, size_t width,
-               PakfirePackage pkg1, PakfirePackage pkg2) {
+static void pakfire_transaction_add_package_change(char*** lines, size_t width,
+               struct pakfire_package* old_pkg, struct pakfire_package* new_pkg) {
        // Print the new package first
-       pakfire_transaction_add_package(str, width, pkg1);
+       pakfire_transaction_add_package(lines, width, new_pkg);
 
-       asprintf(str, "%s   --> %s\n", *str, pakfire_package_get_nevra(pkg2));
+       pakfire_transaction_append_line(lines,
+               "   --> %s\n", pakfire_package_get_nevra(old_pkg));
 }
 
-static void pakfire_transaction_add_separator(char** str, size_t width) {
-       while (width-- > 0)
-               asprintf(str, "%s=", *str);
+static void pakfire_transaction_add_separator(char*** lines, size_t width) {
+       char* separator = alloca(width + 1);
+
+       for (unsigned int i = 0; i < width; i++)
+               separator[i] = '=';
+       separator[width] = '\0';
 
-       // newline
-       asprintf(str, "%s\n", *str);
+       pakfire_transaction_append_line(lines, "%s\n", separator);
 }
 
-static void pakfire_transaction_add_usage_line(char** str, size_t width, const char* headline, ssize_t size) {
+static void pakfire_transaction_add_usage_line(char*** lines, size_t width,
+               const char* headline, ssize_t size) {
        char buffer[128];
-       pakfire_format_size(buffer, sizeof(buffer) - 1, size);
 
-       asprintf(str, "%s%-21s: %s\n", *str, headline, buffer);
+       int r = pakfire_format_size(buffer, size);
+       if (r < 0)
+               return;
+
+       pakfire_transaction_append_line(lines,  "%-21s: %s\n", headline, buffer);
+}
+
+static char* pakfire_transaction_join_lines(char** lines) {
+       if (!lines)
+               return NULL;
+
+       size_t size = 0;
+
+       // Determine total length
+       for (char** line = lines; *line; line++)
+               size += strlen(*line);
+
+       // Allocate memory large enough to hold the result
+       char* s = calloc(1, size + 1);
+       if (!s)
+               return s;
+
+       char* p = s;
+
+       for (char** line = lines; *line; line++) {
+               p += snprintf(p, s - p - 1, "%s", *line);
+       }
+
+       return s;
 }
 
 PAKFIRE_EXPORT char* pakfire_transaction_dump(struct pakfire_transaction* transaction, size_t width) {
        char headline[1024];
-       char* string = "";
 
        Pool* pool = transaction->transaction->pool;
        const int mode =
                SOLVER_TRANSACTION_SHOW_OBSOLETES |
                SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE;
 
+       char** lines = NULL;
+
        // Header
-       pakfire_transaction_add_separator(&string, width);
-       pakfire_transaction_add_line(&string, width,
+       pakfire_transaction_add_separator(&lines, width);
+       pakfire_transaction_add_line(&lines, width,
                _("Package"),
                _("Arch"),
                _("Version"),
                _("Repository"),
                _("Size")
        );
-       pakfire_transaction_add_separator(&string, width);
+       pakfire_transaction_add_separator(&lines, width);
 
        Queue classes;
        queue_init(&classes);
@@ -375,7 +564,7 @@ PAKFIRE_EXPORT char* pakfire_transaction_dump(struct pakfire_transaction* transa
                }
 
                // Show what we are doing
-               pakfire_transaction_add_headline(&string, width, headline);
+               pakfire_transaction_add_headline(&lines, width, headline);
 
                // Fetch packages in this class
                transaction_classify_pkgs(transaction->transaction, mode, class,
@@ -383,80 +572,157 @@ PAKFIRE_EXPORT char* pakfire_transaction_dump(struct pakfire_transaction* transa
 
                // List all packages
                for (int j = 0; j < pkgs.count; j++) {
-                       PakfirePackage pkg1 = pakfire_package_create_from_solvable(
+                       struct pakfire_package* old_pkg = pakfire_package_create_from_solvable(
                                transaction->pakfire, pkgs.elements[j]);
-                       PakfirePackage pkg2 = NULL;
+                       struct pakfire_package* new_pkg = NULL;
 
                        switch (class) {
                                case SOLVER_TRANSACTION_UPGRADED:
                                case SOLVER_TRANSACTION_DOWNGRADED:
-                                       pkg2 = pakfire_package_create_from_solvable(transaction->pakfire,
+                                       new_pkg = pakfire_package_create_from_solvable(transaction->pakfire,
                                                transaction_obs_pkg(transaction->transaction, pkgs.elements[j]));
 
-                                       pakfire_transaction_add_package_change(&string, width, pkg1, pkg2);
+                                       pakfire_transaction_add_package_change(&lines, width, old_pkg, new_pkg);
                                        break;
 
                                default:
-                                       pakfire_transaction_add_package(&string, width, pkg1);
+                                       pakfire_transaction_add_package(&lines, width, old_pkg);
                                        break;
                        }
 
-                       pakfire_package_unref(pkg1);
-                       if (pkg2)
-                               pakfire_package_unref(pkg2);
+                       pakfire_package_unref(old_pkg);
+                       if (new_pkg)
+                               pakfire_package_unref(new_pkg);
                }
 
                // Newline
-               pakfire_transaction_add_newline(&string, width);
+               pakfire_transaction_add_newline(&lines, width);
        }
 
        queue_free(&classes);
        queue_free(&pkgs);
 
        // Summary
-       pakfire_transaction_add_headline(&string, width, _("Transaction Summary"));
-       pakfire_transaction_add_separator(&string, width);
+       pakfire_transaction_add_headline(&lines, width, _("Transaction Summary"));
+       pakfire_transaction_add_separator(&lines, width);
 
        // How much do we need to download?
        size_t downloadsize = pakfire_transaction_downloadsize(transaction);
 
        if (downloadsize > 0)
-               pakfire_transaction_add_usage_line(&string, width,
+               pakfire_transaction_add_usage_line(&lines, width,
                        _("Total Download Size"), downloadsize);
 
        // How much more space do we need?
        ssize_t sizechange = pakfire_transaction_installsizechange(transaction);
-       pakfire_transaction_add_usage_line(&string, width,
+       pakfire_transaction_add_usage_line(&lines, width,
                (sizechange >= 0) ? _("Installed Size") : _("Freed Size"), sizechange);
 
-       // Remove trailing newline
-       pakfire_remove_trailing_newline(string);
+       // Join all lines together
+       char* string = pakfire_transaction_join_lines(lines);
 
        DEBUG(transaction->pakfire, "Transaction: %s\n", string);
 
+       // Free lines
+       if (lines) {
+               for (char** line = lines; *line; line++)
+                       free(*line);
+               free(lines);
+       }
+
        return string;
 }
 
+static void* pakfire_transaction_fileconflict_callback(
+               Pool* pool, Id p, void* data) {
+       return NULL;
+}
+
+static int pakfire_transaction_check_fileconflicts(
+               struct pakfire_transaction* transaction) {
+       Pool* pool = pakfire_get_solv_pool(transaction->pakfire);
+
+       const int flags =
+               FINDFILECONFLICTS_USE_SOLVABLEFILELIST |
+               FINDFILECONFLICTS_CHECK_DIRALIASING |
+               FINDFILECONFLICTS_USE_ROOTDIR;
+
+       Queue pkgs;
+       queue_init(&pkgs);
+
+       Queue conflicts;
+       queue_init(&conflicts);
+
+       // Fetch all installed packages
+       int newpkgs = transaction_installedresult(transaction->transaction, &pkgs);
+
+       // Perform check
+       int count = pool_findfileconflicts(pool, &pkgs, newpkgs, &conflicts, flags,
+               pakfire_transaction_fileconflict_callback, NULL);
+
+       DEBUG(transaction->pakfire, "Found %d file conflict(s)\n", count);
+
+       // Free everything
+       queue_free(&pkgs);
+       queue_free(&conflicts);
+
+       return (count > 0);
+}
+
+static int pakfire_transaction_check(struct pakfire_transaction* transaction) {
+       int r;
+
+       // Check for any file conflicts
+       r = pakfire_transaction_check_fileconflicts(transaction);
+       if (r)
+               return r;
+
+       return 0;
+}
+
 static int pakfire_transaction_verify(struct pakfire_transaction* transaction,
-               PakfirePackage pkg, PakfireArchive archive) {
+               struct pakfire_package* pkg, struct pakfire_archive* archive) {
+       int r;
+
+       const char* nevra = pakfire_package_get_nevra(pkg);
+
        // Nothing to do if this step does not have an archive
-       if (!archive)
+       if (!archive) {
+               DEBUG(transaction->pakfire, "Package %s requires no archive\n", nevra);
                return 0;
+       }
+
+       enum pakfire_digest_types digest_type = 0;
 
-       // Verify the archive
-       pakfire_archive_verify_status_t status = pakfire_archive_verify(archive);
+       // Fetch digest from package
+       const unsigned char* expected_digest = pakfire_package_get_digest(pkg, &digest_type);
+       if (!expected_digest) {
+               DEBUG(transaction->pakfire, "Package %s has no digest\n", nevra);
+               return 0;
+       }
+
+       unsigned char computed_digest[EVP_MAX_MD_SIZE];
+       size_t digest_length = 0;
+
+       // Compute digest of the archive
+       r = pakfire_archive_digest(archive, digest_type, computed_digest, &digest_length);
+       if (r) {
+               ERROR(transaction->pakfire, "Could not compute digest for %s: %m\n", nevra);
+               return r;
+       }
 
-       // Log error
-       if (status) {
-               const char* error = pakfire_archive_verify_strerror(status);
-               ERROR(transaction->pakfire, "Archive verification failed: %s\n", error);
+       // Compare digests
+       r = CRYPTO_memcmp(computed_digest, expected_digest, digest_length);
+       if (r) {
+               ERROR(transaction->pakfire, "Digests of %s do not match\n", nevra);
+               return 1;
        }
 
-       return status;
+       return 0;
 }
 
 static int pakfire_transaction_run_script(struct pakfire_transaction* transaction,
-               struct pakfire_db* db, pakfire_scriptlet_type type, PakfirePackage pkg, PakfireArchive archive) {
+               struct pakfire_db* db, const char* type, struct pakfire_package* pkg, struct pakfire_archive* archive) {
        struct pakfire_scriptlet* scriptlet = NULL;
 
        // Fetch scriptlet from archive if possible
@@ -469,39 +735,37 @@ static int pakfire_transaction_run_script(struct pakfire_transaction* transactio
        if (!scriptlet)
                return 0;
 
-       // Found a script!
-       DEBUG(transaction->pakfire, "Found scriptlet:\n%.*s",
-               (int)scriptlet->size, (const char*)scriptlet->data);
+       // Execute the scriptlet
+       pakfire_scriptlet_execute(scriptlet);
 
-       // Detect what kind of script this is and run it
-       if (pakfire_scriptlet_is_shell_script(scriptlet)) {
-               pakfire_execute_script(transaction->pakfire, scriptlet->data, scriptlet->size,
-                       NULL, 0, NULL, NULL);
-       } else {
-               ERROR(transaction->pakfire, "Scriptlet is of an unknown kind\n");
-       }
+       pakfire_scriptlet_unref(scriptlet);
 
        return 0;
 }
 
 static int pakfire_transaction_extract(struct pakfire_transaction* transaction,
-               PakfirePackage pkg, PakfireArchive archive) {
-       // Extract payload to the root of the Pakfire instance
-       int r = pakfire_archive_extract(archive, NULL);
+               struct pakfire_package* pkg, struct pakfire_archive* archive) {
+       const char* nevra = pakfire_package_get_nevra(pkg);
+
+       // Update status
+       pakfire_transaction_status(transaction, _("Installing %s..."), nevra);
+
+       // Extract payload
+       int r = pakfire_archive_extract(archive);
        if (r) {
-               ERROR(transaction->pakfire, "Could not extract package %s: %s\n",
-                       pakfire_package_get_nevra(pkg), strerror(errno));
+               ERROR(transaction->pakfire, "Could not extract package %s: %m\n",
+                       nevra);
                return r;
        }
 
        // Is it necessary to call ldconfig?
-       PakfireFilelist filelist = pakfire_archive_get_filelist(archive);
+       struct pakfire_filelist* filelist = pakfire_archive_get_filelist(archive);
        if (filelist) {
                int need_ldconfig = pakfire_filelist_contains(filelist, "*/lib*.so.?");
 
                // Update the runtime linker cache
                if (need_ldconfig)
-                       pakfire_execute_ldconfig(transaction->pakfire);
+                       pakfire_jail_ldconfig(transaction->pakfire);
 
                pakfire_filelist_unref(filelist);
        }
@@ -510,14 +774,41 @@ static int pakfire_transaction_extract(struct pakfire_transaction* transaction,
 }
 
 static int pakfire_transaction_erase(struct pakfire_transaction* transaction,
-               PakfirePackage pkg, PakfireArchive archive) {
+               struct pakfire_db* db, struct pakfire_package* pkg) {
+       struct pakfire_filelist* filelist = NULL;
+       int r;
+
+       // Fetch filelist
+       r = pakfire_db_package_filelist(db, &filelist, pkg);
+       if (r)
+               goto ERROR;
+
+       const size_t length = pakfire_filelist_size(filelist);
+
+       // Delete all files
+       for (unsigned int i = 0; i < length; i++) {
+               struct pakfire_file* file = pakfire_filelist_get(filelist, i);
+
+               // Remove the file
+               r = pakfire_file_remove(file);
+               pakfire_file_unref(file);
+
+               // Break on any errors
+               if (r)
+                       goto ERROR;
+       }
+
        // Update the runtime linker cache after all files have been removed
-       pakfire_execute_ldconfig(transaction->pakfire);
+       pakfire_jail_ldconfig(transaction->pakfire);
 
-       return 0; // TODO
+ERROR:
+       if (filelist)
+               pakfire_filelist_unref(filelist);
+
+       return r;
 }
 
-static const char* pakfire_action_type_string(pakfire_action_type_t type) {
+static const char* pakfire_action_type_string(enum pakfire_actions type) {
        switch (type) {
                case PAKFIRE_ACTION_NOOP:
                        return "NOOP";
@@ -538,8 +829,26 @@ static const char* pakfire_action_type_string(pakfire_action_type_t type) {
        return NULL;
 }
 
+static int pakfire_transaction_package_is_userinstalled(
+               struct pakfire_transaction* transaction, struct pakfire_package* pkg) {
+       // No packages on the list
+       if (!transaction->userinstalled)
+               return 0;
+
+       const char* name = pakfire_package_get_name(pkg);
+
+       // Check if the package is on the list
+       for (char** elem = transaction->userinstalled; *elem; elem++) {
+               if (strcmp(name, *elem) == 0)
+                       return 1;
+       }
+
+       // Not found
+       return 0;
+}
+
 static int pakfire_transaction_run_step(struct pakfire_transaction* transaction,
-               struct pakfire_db* db, const pakfire_action_type_t action, PakfirePackage pkg, PakfireArchive archive) {
+               struct pakfire_db* db, const enum pakfire_actions action, struct pakfire_package* pkg, struct pakfire_archive* archive) {
        if (!pkg) {
                errno = EINVAL;
                return 1;
@@ -548,7 +857,7 @@ static int pakfire_transaction_run_step(struct pakfire_transaction* transaction,
        DEBUG(transaction->pakfire, "Running %s for %s\n",
                pakfire_action_type_string(action), pakfire_package_get_nevra(pkg));
 
-       pakfire_step_type_t type = pakfire_transaction_get_step_type(transaction, pkg);
+       enum pakfire_steps type = pakfire_transaction_get_step_type(transaction, pkg);
 
        int r = 0;
        switch (action) {
@@ -563,19 +872,19 @@ static int pakfire_transaction_run_step(struct pakfire_transaction* transaction,
                                case PAKFIRE_STEP_INSTALL:
                                case PAKFIRE_STEP_REINSTALL:
                                        r = pakfire_transaction_run_script(transaction, db,
-                                               PAKFIRE_SCRIPTLET_PRETRANSIN, pkg, archive);
+                                               "pretransin", pkg, archive);
                                        break;
 
                                case PAKFIRE_STEP_UPGRADE:
                                case PAKFIRE_STEP_DOWNGRADE:
                                        r = pakfire_transaction_run_script(transaction, db,
-                                               PAKFIRE_SCRIPTLET_PRETRANSUP, pkg, archive);
+                                               "pretransup", pkg, archive);
                                        break;
 
                                case PAKFIRE_STEP_ERASE:
                                case PAKFIRE_STEP_OBSOLETE:
                                        r = pakfire_transaction_run_script(transaction, db,
-                                               PAKFIRE_SCRIPTLET_PRETRANSUN, pkg, archive);
+                                               "pretransun", pkg, archive);
                                        break;
 
                                case PAKFIRE_STEP_IGNORE:
@@ -589,19 +898,19 @@ static int pakfire_transaction_run_step(struct pakfire_transaction* transaction,
                                case PAKFIRE_STEP_INSTALL:
                                case PAKFIRE_STEP_REINSTALL:
                                        r = pakfire_transaction_run_script(transaction, db,
-                                               PAKFIRE_SCRIPTLET_POSTTRANSIN, pkg, archive);
+                                               "posttransin", pkg, archive);
                                        break;
 
                                case PAKFIRE_STEP_UPGRADE:
                                case PAKFIRE_STEP_DOWNGRADE:
                                        r = pakfire_transaction_run_script(transaction, db,
-                                               PAKFIRE_SCRIPTLET_POSTTRANSUP, pkg, archive);
+                                               "posttransup", pkg, archive);
                                        break;
 
                                case PAKFIRE_STEP_ERASE:
                                case PAKFIRE_STEP_OBSOLETE:
                                        r = pakfire_transaction_run_script(transaction, db,
-                                               PAKFIRE_SCRIPTLET_POSTTRANSUN, pkg, archive);
+                                               "posttransun", pkg, archive);
                                        break;
 
                                case PAKFIRE_STEP_IGNORE:
@@ -611,11 +920,17 @@ static int pakfire_transaction_run_step(struct pakfire_transaction* transaction,
 
                // Execute the action of this script
                case PAKFIRE_ACTION_EXECUTE:
+                       // Increment progress
+                       transaction->progress++;
+
+                       // Update progress callback
+                       pakfire_transaction_status(transaction, NULL);
+
                        switch (type) {
                                case PAKFIRE_STEP_INSTALL:
                                case PAKFIRE_STEP_REINSTALL:
                                        r = pakfire_transaction_run_script(transaction, db,
-                                               PAKFIRE_SCRIPTLET_PREIN, pkg, archive);
+                                               "prein", pkg, archive);
                                        if (r)
                                                break;
 
@@ -630,18 +945,19 @@ static int pakfire_transaction_run_step(struct pakfire_transaction* transaction,
                                                        break;
                                        }
 
-                                       r = pakfire_db_add_package(db, pkg, archive);
+                                       r = pakfire_db_add_package(db, pkg, archive,
+                                                       pakfire_transaction_package_is_userinstalled(transaction, pkg));
                                        if (r)
                                                break;
 
                                        r = pakfire_transaction_run_script(transaction, db,
-                                               PAKFIRE_SCRIPTLET_POSTIN, pkg, archive);
+                                               "postin", pkg, archive);
                                        break;
 
                                case PAKFIRE_STEP_UPGRADE:
                                case PAKFIRE_STEP_DOWNGRADE:
                                        r = pakfire_transaction_run_script(transaction, db,
-                                               PAKFIRE_SCRIPTLET_PREUP, pkg, archive);
+                                               "preup", pkg, archive);
                                        if (r)
                                                break;
 
@@ -649,22 +965,23 @@ static int pakfire_transaction_run_step(struct pakfire_transaction* transaction,
                                        if (r)
                                                break;
 
-                                       r = pakfire_db_add_package(db, pkg, archive);
+                                       r = pakfire_db_add_package(db, pkg, archive,
+                                                       pakfire_transaction_package_is_userinstalled(transaction, pkg));
                                        if (r)
                                                break;
 
                                        r = pakfire_transaction_run_script(transaction, db,
-                                               PAKFIRE_SCRIPTLET_POSTUP, pkg, archive);
+                                               "postup", pkg, archive);
                                        break;
 
                                case PAKFIRE_STEP_ERASE:
                                case PAKFIRE_STEP_OBSOLETE:
                                        r = pakfire_transaction_run_script(transaction, db,
-                                               PAKFIRE_SCRIPTLET_PREUN, pkg, archive);
+                                               "preun", pkg, archive);
                                        if (r)
                                                break;
 
-                                       r = pakfire_transaction_erase(transaction, pkg, archive);
+                                       r = pakfire_transaction_erase(transaction, db, pkg);
                                        if (r)
                                                break;
 
@@ -673,7 +990,7 @@ static int pakfire_transaction_run_step(struct pakfire_transaction* transaction,
                                                break;
 
                                        r = pakfire_transaction_run_script(transaction, db,
-                                               PAKFIRE_SCRIPTLET_POSTUN, pkg, archive);
+                                               "postun", pkg, archive);
                                        break;
 
                                case PAKFIRE_STEP_IGNORE:
@@ -693,9 +1010,27 @@ static int pakfire_transaction_run_step(struct pakfire_transaction* transaction,
 }
 
 static int pakfire_transaction_run_steps(struct pakfire_transaction* transaction,
-               struct pakfire_db* db, const pakfire_action_type_t action) {
+               struct pakfire_db* db, enum pakfire_actions action) {
        int r = 0;
 
+       // Update status
+       switch (action) {
+               case PAKFIRE_ACTION_VERIFY:
+                       pakfire_transaction_status(transaction, _("Verifying packages..."));
+                       break;
+
+               case PAKFIRE_ACTION_PRETRANS:
+                       pakfire_transaction_status(transaction, _("Preparing installation..."));
+                       break;
+
+               case PAKFIRE_ACTION_POSTTRANS:
+                       pakfire_transaction_status(transaction, _("Finishing up..."));
+                       break;
+
+               default:
+                       break;
+       }
+
        // Walk through all steps
        for (unsigned int i = 0; i < transaction->num; i++) {
                r = pakfire_transaction_run_step(transaction, db, action,
@@ -703,7 +1038,7 @@ static int pakfire_transaction_run_steps(struct pakfire_transaction* transaction
 
                // End loop if action was unsuccessful
                if (r) {
-                       DEBUG(transaction->pakfire, "Step %d failed: %s\n", i, strerror(errno));
+                       DEBUG(transaction->pakfire, "Step %d failed: %m\n", i);
                        break;
                }
        }
@@ -713,10 +1048,10 @@ static int pakfire_transaction_run_steps(struct pakfire_transaction* transaction
 
 static int pakfire_transaction_open_archives(struct pakfire_transaction* transaction) {
        for (unsigned int i = 0; i < transaction->num; i++) {
-               PakfirePackage pkg = transaction->packages[i];
+               struct pakfire_package* pkg = transaction->packages[i];
 
                // Fetch the type
-               pakfire_step_type_t type = pakfire_transaction_get_step_type(transaction, pkg);
+               enum pakfire_steps type = pakfire_transaction_get_step_type(transaction, pkg);
 
                // Do we need the archive?
                switch (type) {
@@ -734,8 +1069,8 @@ static int pakfire_transaction_open_archives(struct pakfire_transaction* transac
 
                transaction->archives[i] = pakfire_package_get_archive(pkg);
                if (!transaction->archives[i]) {
-                       ERROR(transaction->pakfire, "Could not open archive for %s: %s\n",
-                               pakfire_package_get_nevra(pkg), strerror(errno));
+                       ERROR(transaction->pakfire, "Could not open archive for %s: %m\n",
+                               pakfire_package_get_nevra(pkg));
                        return 1;
                }
        }
@@ -743,7 +1078,8 @@ static int pakfire_transaction_open_archives(struct pakfire_transaction* transac
        return 0;
 }
 
-PAKFIRE_EXPORT int pakfire_transaction_run(struct pakfire_transaction* transaction) {
+static int pakfire_transaction_perform(struct pakfire_transaction* transaction) {
+       struct pakfire_repo* repo = NULL;
        struct pakfire_db* db;
        int r;
 
@@ -782,17 +1118,27 @@ PAKFIRE_EXPORT int pakfire_transaction_run(struct pakfire_transaction* transacti
 
        DEBUG(transaction->pakfire, "The transaction has finished successfully\n");
 
+       // Reload database for next transaction
+
+       repo = pakfire_get_installed_repo(transaction->pakfire);
+       if (!repo)
+               goto ERROR;
+
+       // Reload the database
+       r = pakfire_db_load(db, repo);
+
 ERROR:
-       // Free the database
+       if (repo)
+               pakfire_repo_unref(repo);
        pakfire_db_unref(db);
 
        return r;
 }
 
 static int pakfire_transaction_download_package(struct pakfire_transaction* transaction,
-               struct pakfire_downloader* downloader, PakfirePackage pkg) {
+               struct pakfire_downloader* downloader, struct pakfire_package* pkg) {
        int r = 1;
-       PakfireRepo repo = NULL;
+       struct pakfire_repo* repo = NULL;
        struct pakfire_mirrorlist* mirrorlist = NULL;
 
        // Fetch the repository to download from
@@ -806,23 +1152,33 @@ static int pakfire_transaction_download_package(struct pakfire_transaction* tran
        // Fetch mirrorlist
        mirrorlist = pakfire_repo_get_mirrorlist(repo);
 
+       const char* nevra = pakfire_package_get_nevra(pkg);
+       if (!nevra)
+               goto ERROR;
+
        // Where to store the package?
        const char* path = pakfire_package_get_path(pkg);
-       if (!path)
+       if (!path) {
+               ERROR(transaction->pakfire, "Could not retrieve package path for %s: %m\n", nevra);
                goto ERROR;
+       }
 
        // What file to download?
        const char* filename = pakfire_package_get_filename(pkg);
-       if (!filename)
+       if (!filename) {
+               ERROR(transaction->pakfire, "Could not retrieve filename for package %s: %m\n", nevra);
                goto ERROR;
+       }
 
-       const char* nevra = pakfire_package_get_nevra(pkg);
-       if (!nevra)
-               goto ERROR;
+       enum pakfire_digest_types digest_type = 0;
+
+       // Retrieve package digest
+       const unsigned char* digest = pakfire_package_get_digest(pkg, &digest_type);
+       const size_t digest_length = pakfire_digest_length(digest_type);
 
        // Add transfer to downloader
        r = pakfire_downloader_add_transfer(downloader, baseurl, mirrorlist,
-               nevra, filename, path, 0);
+               nevra, filename, path, digest_type, digest, digest_length, 0);
 
 ERROR:
        if (mirrorlist)
@@ -833,6 +1189,36 @@ ERROR:
        return r;
 }
 
+static int pakfire_transaction_package_needs_download(
+               struct pakfire_transaction* transaction, struct pakfire_package* pkg) {
+       enum pakfire_steps type = pakfire_transaction_get_step_type(transaction, pkg);
+       switch (type) {
+               case PAKFIRE_STEP_INSTALL:
+               case PAKFIRE_STEP_REINSTALL:
+               case PAKFIRE_STEP_DOWNGRADE:
+               case PAKFIRE_STEP_UPGRADE:
+                       break;
+
+               // No need to download for these steps
+               default:
+                       return 0;
+       }
+
+       // No download required if this package is already installed
+       if (pakfire_package_is_installed(pkg))
+               return 0;
+
+       const char* path = pakfire_package_get_path(pkg);
+
+       // Does the file exist?
+       int r = access(path, R_OK);
+       if (r == 0)
+               return 0;
+
+       // This package needs to be downloaded
+       return 1;
+}
+
 PAKFIRE_EXPORT int pakfire_transaction_download(struct pakfire_transaction* transaction) {
        struct pakfire_downloader* downloader;
        int r;
@@ -840,31 +1226,25 @@ PAKFIRE_EXPORT int pakfire_transaction_download(struct pakfire_transaction* tran
        // Initialize the downloader
        r = pakfire_downloader_create(&downloader, transaction->pakfire);
        if (r) {
-               ERROR(transaction->pakfire, "Could not initialize downloader: %s\n",
-                       strerror(errno));
+               ERROR(transaction->pakfire, "Could not initialize downloader: %m\n");
                return 1;
        }
 
        // Add all packages that need to be downloaded
        for (unsigned int i = 0; i < transaction->num; i++) {
-               PakfirePackage pkg = transaction->packages[i];
-
-               pakfire_step_type_t type = pakfire_transaction_get_step_type(transaction, pkg);
-               switch (type) {
-                       case PAKFIRE_STEP_INSTALL:
-                       case PAKFIRE_STEP_REINSTALL:
-                       case PAKFIRE_STEP_DOWNGRADE:
-                       case PAKFIRE_STEP_UPGRADE:
-                               break;
+               struct pakfire_package* pkg = transaction->packages[i];
 
-                       default:
-                               continue;
-               }
+               if (!pakfire_transaction_package_needs_download(transaction, pkg))
+                       continue;
 
                // Enqueue download
                r = pakfire_transaction_download_package(transaction, downloader, pkg);
-               if (r)
+               if (r) {
+                       const char* nevra = pakfire_package_get_nevra(pkg);
+
+                       ERROR(transaction->pakfire, "Could not add download to queue: %s: %m\n", nevra);
                        goto ERROR;
+               }
        }
 
        // Run the downloader
@@ -875,3 +1255,52 @@ ERROR:
 
        return r;
 }
+
+PAKFIRE_EXPORT int pakfire_transaction_run(
+               struct pakfire_transaction* transaction, int flags) {
+       int r;
+
+       // Skip running an empty transaction
+       if (!transaction->num) {
+               DEBUG(transaction->pakfire, "Empty transaction. Skipping...\n");
+               return 0;
+       }
+
+       // Show what would be done
+       char* dump = pakfire_transaction_dump(transaction, 80);
+
+       // Check if we should continue
+       r = pakfire_confirm(transaction->pakfire, dump, _("Is this okay? [y/N]"));
+       if (r) {
+               ERROR(transaction->pakfire, "Transaction aborted upon user request\n");
+               goto ERROR;
+       }
+
+       // Write transaction dump to log
+       INFO(transaction->pakfire, "%s\n", dump);
+
+       // Perform a check if this can actually be run
+       r = pakfire_transaction_check(transaction);
+       if (r)
+               goto ERROR;
+
+       // End here for a dry run
+       if (flags & PAKFIRE_TRANSACTION_DRY_RUN)
+               goto ERROR;
+
+       // Download what we need
+       r = pakfire_transaction_download(transaction);
+       if (r)
+               goto ERROR;
+
+       // Perform all steps
+       r = pakfire_transaction_perform(transaction);
+       if (r)
+               goto ERROR;
+
+ERROR:
+       // Cleanup
+       free(dump);
+
+       return r;
+}