Exemple #1
0
int
lr_yum_download_remote(lr_Handle handle, lr_Result result)
{
    int rc = LRE_OK;
    int fd;
    int create_repodata_dir = 1;
    char *path_to_repodata;
    lr_YumRepo repo;
    lr_YumRepoMd repomd;

    DPRINTF("%s: Downloading/Copying repo..\n", __func__);

    rc = lr_handle_prepare_internal_mirrorlist(handle, "repodata/repomd.xml");
    if (rc != LRE_OK)
        return rc;

    repo   = result->yum_repo;
    repomd = result->yum_repomd;

    path_to_repodata = lr_pathconcat(handle->destdir, "repodata", NULL);

    if (handle->update) {  /* Check if should create repodata/ subdir */
        struct stat buf;
        if (stat(path_to_repodata, &buf) != -1)
            if (S_ISDIR(buf.st_mode))
                create_repodata_dir = 0;
    }

    if (create_repodata_dir) {
        /* Prepare repodata/ subdir */
        rc = mkdir(path_to_repodata, S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH);
        if (rc == -1) {
            DPRINTF("%s: Cannot create dir: %s (%s)\n",
                    __func__, path_to_repodata, strerror(errno));
            lr_free(path_to_repodata);
            return LRE_CANNOTCREATEDIR;
        }
    }
    lr_free(path_to_repodata);

    if (!handle->update) {
        /* Prepare repomd.xml file */
        char *path;
        path = lr_pathconcat(handle->destdir, "/repodata/repomd.xml", NULL);
        fd = open(path, O_CREAT|O_TRUNC|O_RDWR, 0660);
        if (fd == -1) {
            lr_free(path);
            return LRE_IO;
        }

        /* Download repomd.xml */
        rc = lr_yum_download_repomd(handle, handle->metalink, fd);
        if (rc != LRE_OK) {
            close(fd);
            lr_free(path);
            return rc;
        }

        /* Check repomd.xml.asc if available.
         * Try to download and verify GPG signature (repomd.xml.asc).
         * Try to download only from the mirror where repomd.xml iself was
         * downloaded. It is because most of yum repositories are not signed
         * and try every mirror for signature is non effective.
         * Every mirror would be tried because mirrorded_download function have
         * no clue if 404 for repomd.xml.asc means that no signature exists or
         * it is just error on the mirror and should try the next one.
         **/
        if (handle->checks & LR_CHECK_GPG) {
            int fd_sig;
            char *url, *signature;

            signature = lr_pathconcat(handle->destdir, "repodata/repomd.xml.asc", NULL);
            fd_sig = open(signature, O_CREAT|O_TRUNC|O_RDWR, 0660);
            if (fd_sig == -1) {
                DPRINTF("%s: Cannot open: %s\n", __func__, signature);
                close(fd);
                lr_free(path);
                lr_free(signature);
                return LRE_IO;
            }

            url = lr_pathconcat(handle->used_mirror, "repodata/repomd.xml.asc", NULL);
            rc = lr_curl_single_download(handle, url, fd_sig);
            lr_free(url);
            close(fd_sig);
            if (rc != LRE_OK) {
                // Signature doesn't exist
                DPRINTF("%s: GPG signature doesn't exists\n", __func__);
                unlink(signature);
                lr_free(signature);
            } else {
                // Signature downloaded
                repo->signature = lr_strdup(signature);
                rc = lr_gpg_check_signature(signature, path, NULL);
                if (rc != LRE_OK) {
                    DPRINTF("%s: GPG signature verification failed\n", __func__);
                    close(fd);
                    lr_free(path);
                    lr_free(signature);
                    return rc;
                }
                DPRINTF("%s: GPG signature successfully verified\n", __func__);
            }
        }

        lseek(fd, 0, SEEK_SET);

        /* Parse repomd */
        DPRINTF("%s: Parsing repomd.xml\n", __func__);
        rc = lr_yum_repomd_parse_file(repomd, fd);
        close(fd);
        if (rc != LRE_OK) {
            DPRINTF("%s: Parsing unsuccessful (%d)\n", __func__, rc);
            lr_free(path);
            return rc;
        }

        /* Fill result object */
        result->destdir = lr_strdup(handle->destdir);
        repo->destdir = lr_strdup(handle->destdir);
        repo->repomd = path;
        if (handle->used_mirror)
            repo->url = lr_strdup(handle->used_mirror);
        else
            repo->url = lr_strdup(handle->baseurl);

        DPRINTF("%s: Repomd revision: %s\n", repomd->revision, __func__);
    }

    /* Download rest of metadata files */
    if ((rc = lr_yum_download_repo(handle, repo, repomd)) != LRE_OK)
        return rc;

    DPRINTF("%s: Repository was successfully downloaded\n", __func__);
    return LRE_OK;
}
gboolean
lr_download_packages(GSList *targets,
                     LrPackageDownloadFlag flags,
                     GError **err)
{
    gboolean ret;
    gboolean failfast = flags & LR_PACKAGEDOWNLOAD_FAILFAST;
    struct sigaction old_sigact;
    GSList *downloadtargets = NULL;
    gboolean interruptible = FALSE;

    assert(!err || *err == NULL);

    if (!targets)
        return TRUE;

    // Check targets
    for (GSList *elem = targets; elem; elem = g_slist_next(elem)) {
        LrPackageTarget *packagetarget = elem->data;

        if (!packagetarget->handle) {
            continue;
            /*
            g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_BADFUNCARG,
                        "Package target %s doesn't have specified a handle",
                        packagetarget->relative_url);
            return FALSE;
            */
        }

        if (packagetarget->handle->interruptible)
            interruptible = TRUE;

        // Check repotype
        // Note: Checked because lr_handle_prepare_internal_mirrorlist
        // support only LR_YUMREPO yet
        if (packagetarget->handle->repotype != LR_YUMREPO) {
            g_debug("%s: Bad repo type", __func__);
            g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_BADFUNCARG,
                        "Bad repo type");
            return FALSE;
        }
    }

    // Setup sighandler
    if (interruptible) {
        struct sigaction sigact;
        g_debug("%s: Using own SIGINT handler", __func__);
        memset(&sigact, 0, sizeof(old_sigact));
        memset(&sigact, 0, sizeof(sigact));
        sigemptyset(&sigact.sa_mask);
        sigact.sa_handler = lr_sigint_handler;
        sigaddset(&sigact.sa_mask, SIGINT);
        sigact.sa_flags = SA_RESTART;
        if (sigaction(SIGINT, &sigact, &old_sigact) == -1) {
            g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_SIGACTION,
                        "Cannot set Librepo SIGINT handler");
            return FALSE;
        }
    }

    // List of handles for fastest mirror resolving
    GSList *fmr_handles = NULL;

    // Prepare targets
    for (GSList *elem = targets; elem; elem = g_slist_next(elem)) {
        gchar *local_path;
        LrPackageTarget *packagetarget = elem->data;
        LrDownloadTarget *downloadtarget;
        gint64 realsize = -1;
        gboolean doresume = packagetarget->resume;

        // Reset output attributes of the handle
        lr_packagetarget_reset(packagetarget);

        // Prepare destination filename
        if (packagetarget->dest) {
            if (g_file_test(packagetarget->dest, G_FILE_TEST_IS_DIR)) {
                // Dir specified
                gchar *file_basename = g_path_get_basename(packagetarget->relative_url);
                local_path = g_build_filename(packagetarget->dest,
                                              file_basename,
                                              NULL);
                g_free(file_basename);
            } else {
                local_path = g_strdup(packagetarget->dest);
            }
        } else {
            // No destination path specified
            local_path = g_path_get_basename(packagetarget->relative_url);
        }

        packagetarget->local_path = g_string_chunk_insert(packagetarget->chunk,
                                                          local_path);
        g_free(local_path);

        // Check expected size and real size if the file exists
        if (doresume
            && g_access(packagetarget->local_path, R_OK) == 0
            && packagetarget->expectedsize > 0)
        {
            struct stat buf;
            if (stat(packagetarget->local_path, &buf)) {
                g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_IO,
                        "Cannot stat %s: %s", packagetarget->local_path,
                        g_strerror(errno));
                return FALSE;
            }

            realsize = buf.st_size;

            if (packagetarget->expectedsize < realsize)
                // Existing file is bigger then the one that is expected,
                // disable resuming
                doresume = FALSE;
        }

        if (g_access(packagetarget->local_path, R_OK) == 0
            && packagetarget->checksum
            && packagetarget->checksum_type != LR_CHECKSUM_UNKNOWN)
        {
            /* If the file exists and checksum is ok, then is pointless to
             * download the file again.
             * Moreover, if the resume is enabled and the file is already
             * completely downloaded, then the download is going to fail.
             */
            int fd_r = open(packagetarget->local_path, O_RDONLY);
            if (fd_r != -1) {
                gboolean matches;
                ret = lr_checksum_fd_cmp(packagetarget->checksum_type,
                                         fd_r,
                                         packagetarget->checksum,
                                         1,
                                         &matches,
                                         NULL);
                close(fd_r);
                if (ret && matches) {
                    // Checksum calculation was ok and checksum matches
                    g_debug("%s: Package %s is already downloaded (checksum matches)",
                            __func__, packagetarget->local_path);

                    packagetarget->err = g_string_chunk_insert(
                                                packagetarget->chunk,
                                                "Already downloaded");

                    // Call end callback
                    LrEndCb end_cb = packagetarget->endcb;
                    if (end_cb)
                        end_cb(packagetarget->cbdata,
                               LR_TRANSFER_ALREADYEXISTS,
                               "Already downloaded");

                    continue;
                } else if (ret) {
                    // Checksum calculation was ok but checksum doesn't match
                    if (realsize != -1 && realsize == packagetarget->expectedsize)
                        // File size is the same as the expected one
                        // Don't try to resume
                        doresume = FALSE;
                }
            }
        }

        if (doresume && realsize != -1 && realsize == packagetarget->expectedsize) {
            // File's size matches the expected one, the resume is enabled and
            // no checksum is known => expect that the file is
            // the one the user wants
            g_debug("%s: Package %s is already downloaded (size matches)",
                    __func__, packagetarget->local_path);

            packagetarget->err = g_string_chunk_insert(
                                        packagetarget->chunk,
                                        "Already downloaded");

            // Call end callback
            LrEndCb end_cb = packagetarget->endcb;
            if (end_cb)
                end_cb(packagetarget->cbdata,
                       LR_TRANSFER_ALREADYEXISTS,
                       "Already downloaded");

            continue;
        }

        if (packagetarget->handle) {
            ret = lr_handle_prepare_internal_mirrorlist(packagetarget->handle,
                                                        FALSE,
                                                        err);
            if (!ret)
                goto cleanup;

            if (packagetarget->handle->fastestmirror) {
                if (!g_slist_find(fmr_handles, packagetarget->handle))
                    fmr_handles = g_slist_prepend(fmr_handles,
                                                  packagetarget->handle);
            }
        }

        GSList *checksums = NULL;
        LrDownloadTargetChecksum *checksum;
        checksum = lr_downloadtargetchecksum_new(packagetarget->checksum_type,
                                                 packagetarget->checksum);
        checksums = g_slist_prepend(checksums, checksum);

        downloadtarget = lr_downloadtarget_new(packagetarget->handle,
                                               packagetarget->relative_url,
                                               packagetarget->base_url,
                                               -1,
                                               packagetarget->local_path,
                                               checksums,
                                               packagetarget->expectedsize,
                                               doresume,
                                               packagetarget->progresscb,
                                               packagetarget->cbdata,
                                               packagetarget->endcb,
                                               packagetarget->mirrorfailurecb,
                                               packagetarget,
                                               packagetarget->byterangestart,
                                               packagetarget->byterangeend);

        downloadtargets = g_slist_append(downloadtargets, downloadtarget);
    }

    // Do Fastest Mirror resolving for all handles in one shot
    if (fmr_handles) {
        fmr_handles = g_slist_reverse(fmr_handles);
        ret = lr_fastestmirror_sort_internalmirrorlists(fmr_handles, err);
        g_slist_free(fmr_handles);

        if (!ret) {
            return FALSE;
        }
    }

    // Start downloading
    ret = lr_download(downloadtargets, failfast, err);

cleanup:

    // Copy download statuses from downloadtargets to targets
    for (GSList *elem = downloadtargets; elem; elem = g_slist_next(elem)) {
        LrDownloadTarget *downloadtarget = elem->data;
        LrPackageTarget *packagetarget = downloadtarget->userdata;
        if (downloadtarget->err)
            packagetarget->err = g_string_chunk_insert(packagetarget->chunk,
                                                       downloadtarget->err);
    }

    // Free downloadtargets list
    g_slist_free_full(downloadtargets, (GDestroyNotify)lr_downloadtarget_free);

    // Restore original signal handler
    if (interruptible) {
        g_debug("%s: Restoring an old SIGINT handler", __func__);
        sigaction(SIGINT, &old_sigact, NULL);
        if (lr_interrupt) {
            if (err && *err != NULL)
                g_clear_error(err);
            g_set_error(err, LR_PACKAGE_DOWNLOADER_ERROR, LRE_INTERRUPTED,
                        "Insterupted by a SIGINT signal");
            return FALSE;
        }
    }

    return ret;
}