]> 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 4bacfe1dd586ac100ec0558de52574bc61d304b9..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/ui.h>
 #include <pakfire/util.h>
@@ -49,6 +55,14 @@ struct pakfire_transaction {
        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 {
@@ -257,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;
 }
@@ -268,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++)
@@ -329,7 +385,9 @@ static void pakfire_transaction_add_package(char*** lines, size_t width, struct
        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(lines, width,
                pakfire_package_get_name(pkg),
@@ -364,7 +422,10 @@ static void pakfire_transaction_add_separator(char*** lines, size_t width) {
 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);
+
+       int r = pakfire_format_size(buffer, size);
+       if (r < 0)
+               return;
 
        pakfire_transaction_append_line(lines,  "%-21s: %s\n", headline, buffer);
 }
@@ -572,33 +633,92 @@ PAKFIRE_EXPORT char* pakfire_transaction_dump(struct pakfire_transaction* transa
        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,
                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;
+
+       // 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;
+       }
 
-       pakfire_archive_verify_status_t status;
+       unsigned char computed_digest[EVP_MAX_MD_SIZE];
+       size_t digest_length = 0;
 
-       // Verify the archive
-       int r = pakfire_archive_verify(archive, &status, NULL);
-       if (r)
+       // 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;
+       }
 
-       // This function will return a binary status which is zero for success and
-       // anything else for errors, etc...
-       switch (status) {
-               // Good
-               case PAKFIRE_ARCHIVE_VERIFY_OK:
-               case PAKFIRE_ARCHIVE_VERIFY_KEY_EXPIRED:
-                       return 0;
-
-               // Bad
-               default:
-                       break;
+       // 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 1;
+       return 0;
 }
 
 static int pakfire_transaction_run_script(struct pakfire_transaction* transaction,
@@ -625,11 +745,16 @@ static int pakfire_transaction_run_script(struct pakfire_transaction* transactio
 
 static int pakfire_transaction_extract(struct pakfire_transaction* transaction,
                struct pakfire_package* pkg, struct pakfire_archive* archive) {
-       // Extract payload to the root of the Pakfire instance
-       int r = pakfire_archive_extract(archive, NULL);
+       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: %m\n",
-                       pakfire_package_get_nevra(pkg));
+                       nevra);
                return r;
        }
 
@@ -640,7 +765,7 @@ static int pakfire_transaction_extract(struct pakfire_transaction* transaction,
 
                // Update the runtime linker cache
                if (need_ldconfig)
-                       pakfire_execute_ldconfig(transaction->pakfire);
+                       pakfire_jail_ldconfig(transaction->pakfire);
 
                pakfire_filelist_unref(filelist);
        }
@@ -674,7 +799,7 @@ static int pakfire_transaction_erase(struct pakfire_transaction* transaction,
        }
 
        // Update the runtime linker cache after all files have been removed
-       pakfire_execute_ldconfig(transaction->pakfire);
+       pakfire_jail_ldconfig(transaction->pakfire);
 
 ERROR:
        if (filelist)
@@ -795,6 +920,12 @@ 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:
@@ -882,6 +1013,24 @@ static int pakfire_transaction_run_steps(struct pakfire_transaction* transaction
                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,
@@ -1003,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, PAKFIRE_DIGEST_NONE, NULL, 0, 0);
+               nevra, filename, path, digest_type, digest, digest_length, 0);
 
 ERROR:
        if (mirrorlist)
@@ -1080,8 +1239,12 @@ PAKFIRE_EXPORT int pakfire_transaction_download(struct pakfire_transaction* tran
 
                // 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
@@ -1093,7 +1256,8 @@ ERROR:
        return r;
 }
 
-PAKFIRE_EXPORT int pakfire_transaction_run(struct pakfire_transaction* transaction) {
+PAKFIRE_EXPORT int pakfire_transaction_run(
+               struct pakfire_transaction* transaction, int flags) {
        int r;
 
        // Skip running an empty transaction
@@ -1106,7 +1270,7 @@ PAKFIRE_EXPORT int pakfire_transaction_run(struct pakfire_transaction* transacti
        char* dump = pakfire_transaction_dump(transaction, 80);
 
        // Check if we should continue
-       r = pakfire_ui_confirm(transaction->pakfire, dump, _("Is this okay? [y/N]"));
+       r = pakfire_confirm(transaction->pakfire, dump, _("Is this okay? [y/N]"));
        if (r) {
                ERROR(transaction->pakfire, "Transaction aborted upon user request\n");
                goto ERROR;
@@ -1115,6 +1279,15 @@ PAKFIRE_EXPORT int pakfire_transaction_run(struct pakfire_transaction* transacti
        // 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)
@@ -1126,6 +1299,7 @@ PAKFIRE_EXPORT int pakfire_transaction_run(struct pakfire_transaction* transacti
                goto ERROR;
 
 ERROR:
+       // Cleanup
        free(dump);
 
        return r;