1 /* librepo - A library providing (libcURL like) API to downloading repository
2  * Copyright (C) 2012  Tomas Mlcoch
3  *
4  * Licensed under the GNU Lesser General Public License Version 2.1
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 //#define _POSIX_SOURCE
22 //#define _DEFAULT_SOURCE
23 #define  BITS_IN_BYTE 8
24 
25 #include <stdio.h>
26 #include <libgen.h>
27 #include <assert.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 
35 #ifdef WITH_ZCHUNK
36 #include <zck.h>
37 #endif /* WITH_ZCHUNK */
38 
39 #include "util.h"
40 #include "metalink.h"
41 #include "repomd.h"
42 #include "downloader.h"
43 #include "handle_internal.h"
44 #include "result_internal.h"
45 #include "yum_internal.h"
46 #include "gpg.h"
47 #include "cleanup.h"
48 #include "librepo.h"
49 
50 /* helper functions for YumRepo manipulation */
51 
52 LrYumRepo *
lr_yum_repo_init(void)53 lr_yum_repo_init(void)
54 {
55     return lr_malloc0(sizeof(LrYumRepo));
56 }
57 
58 void
lr_yum_repo_free(LrYumRepo * repo)59 lr_yum_repo_free(LrYumRepo *repo)
60 {
61     if (!repo)
62         return;
63 
64     for (GSList *elem = repo->paths; elem; elem = g_slist_next(elem)) {
65         LrYumRepoPath *yumrepopath = elem->data;
66         assert(yumrepopath);
67         lr_free(yumrepopath->type);
68         lr_free(yumrepopath->path);
69         lr_free(yumrepopath);
70     }
71 
72     g_slist_free(repo->paths);
73     lr_free(repo->repomd);
74     lr_free(repo->url);
75     lr_free(repo->destdir);
76     lr_free(repo->signature);
77     lr_free(repo->mirrorlist);
78     lr_free(repo->metalink);
79     lr_free(repo);
80 }
81 
82 static char *
get_type(LrYumRepo * repo,const char * type)83 get_type(LrYumRepo *repo, const char *type)
84 {
85     if (!repo->use_zchunk)
86         return g_strdup(type);
87 
88     gchar *chk_type = g_strconcat(type, "_zck", NULL);
89 
90     for (GSList *elem = repo->paths; elem; elem = g_slist_next(elem)) {
91         LrYumRepoPath *yumrepopath = elem->data;
92         assert(yumrepopath);
93         if (!strcmp(yumrepopath->type, chk_type))
94             return chk_type;
95     }
96     g_free(chk_type);
97     return g_strdup(type);
98 }
99 
100 static const char *
yum_repo_path(LrYumRepo * repo,const char * type)101 yum_repo_path(LrYumRepo *repo, const char *type)
102 {
103     assert(repo);
104 
105     for (GSList *elem = repo->paths; elem; elem = g_slist_next(elem)) {
106         LrYumRepoPath *yumrepopath = elem->data;
107         assert(yumrepopath);
108         if (!strcmp(yumrepopath->type, type))
109             return yumrepopath->path;
110     }
111     return NULL;
112 }
113 
114 const char *
lr_yum_repo_path(LrYumRepo * repo,const char * type)115 lr_yum_repo_path(LrYumRepo *repo, const char *type)
116 {
117     assert(repo);
118 
119     gchar *chk_type = get_type(repo, type);
120     const char *path = yum_repo_path(repo, chk_type);
121     g_free(chk_type);
122     return path;
123 }
124 
125 /** Append path to the repository object.
126  * @param repo          Yum repo object.
127  * @param type          Type of file. E.g. "primary", "filelists", ...
128  * @param path          Path to the file.
129  */
130 static void
lr_yum_repo_append(LrYumRepo * repo,const char * type,const char * path)131 lr_yum_repo_append(LrYumRepo *repo, const char *type, const char *path)
132 {
133     assert(repo);
134     assert(type);
135     assert(path);
136 
137     LrYumRepoPath *yumrepopath = lr_malloc(sizeof(LrYumRepoPath));
138     yumrepopath->type = g_strdup(type);
139     yumrepopath->path = g_strdup(path);
140     repo->paths = g_slist_append(repo->paths, yumrepopath);
141 }
142 
143 static void
lr_yum_repo_update(LrYumRepo * repo,const char * type,const char * path)144 lr_yum_repo_update(LrYumRepo *repo, const char *type, const char *path)
145 {
146     assert(repo);
147     assert(type);
148     assert(path);
149 
150     for (GSList *elem = repo->paths; elem; elem = g_slist_next(elem)) {
151         LrYumRepoPath *yumrepopath = elem->data;
152         assert(yumrepopath);
153 
154         if (!strcmp(yumrepopath->type, type)) {
155             lr_free(yumrepopath->path);
156             yumrepopath->path = g_strdup(path);
157             return;
158         }
159     }
160 
161     lr_yum_repo_append(repo, type, path);
162 }
163 
164 /* main business logic */
165 
166 gint
compare_records(gconstpointer a,gconstpointer b)167 compare_records(gconstpointer a, gconstpointer b)
168 {
169     LrYumRepoMdRecord* yum_record = (LrYumRepoMdRecord*) a;
170     char *type1 = (char *) yum_record->type;
171     char *type2 = (char *) b;
172     return g_strcmp0(type1, type2);
173 }
174 
175 static void
lr_yum_switch_to_zchunk(LrHandle * handle,LrYumRepoMd * repomd)176 lr_yum_switch_to_zchunk(LrHandle *handle, LrYumRepoMd *repomd)
177 {
178     if (handle->yumdlist) {
179         int x = 0;
180         while (handle->yumdlist[x]) {
181             char *check_type = g_strconcat(handle->yumdlist[x], "_zck", NULL);
182             assert(check_type);
183 
184             /* Check whether we already want the zchunk version of this record */
185             int found = FALSE;
186             int y = 0;
187             while (handle->yumdlist[y]) {
188                 if (y == x) {
189                     y++;
190                     continue;
191                 }
192                 if (strcmp(handle->yumdlist[y], check_type) == 0) {
193                     found = TRUE;
194                     break;
195                 }
196                 y++;
197             }
198             if (found) {
199                 g_free(check_type);
200                 x++;
201                 continue;
202             }
203 
204             found = FALSE;
205             /* Check whether the zchunk version of this record exists */
206             for (GSList *elem = repomd->records; elem; elem = g_slist_next(elem)) {
207                 LrYumRepoMdRecord *record = elem->data;
208 
209                 if (strcmp(record->type, check_type) == 0) {
210                     g_debug("Found %s so using instead of %s", check_type,
211                             handle->yumdlist[x]);
212                     g_free(handle->yumdlist[x]);
213                     handle->yumdlist[x] = check_type;
214                     found = TRUE;
215                     break;
216                 }
217             }
218             if (!found)
219                 g_free(check_type);
220             x++;
221         }
222     }
223     return;
224 }
225 
226 static gboolean
lr_yum_repomd_record_enabled(LrHandle * handle,const char * type,GSList * records)227 lr_yum_repomd_record_enabled(LrHandle *handle, const char *type, GSList* records)
228 {
229     // Check for records that shouldn't be downloaded
230     if (handle->yumblist) {
231         int x = 0;
232         while (handle->yumblist[x]) {
233             if (!strcmp(handle->yumblist[x], type))
234                 return FALSE;
235             x++;
236         }
237     }
238 
239     // Check for records that should be downloaded
240     if (handle->yumdlist) {
241         int x = 0;
242         while (handle->yumdlist[x]) {
243             if (!strcmp(handle->yumdlist[x], type))
244                 return TRUE;
245             x++;
246         }
247         // Substitution check
248         if (handle->yumslist) {
249             for (GSList *elem = handle->yumslist; elem; elem = g_slist_next(elem)) {
250                 LrVar* subs = elem->data;
251                 if (!g_strcmp0(subs->val, type)) {
252                     char *orig = subs->var;
253                     for (guint i = 0; handle->yumdlist[i]; i++) {
254                         if (!g_strcmp0(orig, handle->yumdlist[i]) &&
255                             !g_slist_find_custom(records, orig, (GCompareFunc) compare_records))
256                             return TRUE;
257                     }
258                     return FALSE;
259                 }
260             }
261         }
262         return FALSE;
263     }
264     return TRUE;
265 }
266 
cbdata_new(void * userdata,void * cbdata,LrProgressCb progresscb,LrHandleMirrorFailureCb hmfcb,const char * metadata)267 static CbData *cbdata_new(void *userdata,
268                           void *cbdata,
269                           LrProgressCb progresscb,
270                           LrHandleMirrorFailureCb hmfcb,
271                           const char *metadata)
272 {
273     CbData *data = calloc(1, sizeof(*data));
274     data->userdata = userdata;
275     data->cbdata = cbdata;
276     data->progresscb = progresscb;
277     data->hmfcb = hmfcb;
278     data->metadata = g_strdup(metadata);
279     return data;
280 }
281 
282 static void
cbdata_free(CbData * data)283 cbdata_free(CbData *data)
284 {
285     if (!data) return;
286     free(data->metadata);
287     free(data);
288 }
289 
290 static int
progresscb(void * clientp,double total_to_download,double downloaded)291 progresscb(void *clientp, double total_to_download, double downloaded)
292 {
293     CbData *data = clientp;
294     if (data->progresscb)
295         return data->progresscb(data->userdata, total_to_download, downloaded);
296     return LR_CB_OK;
297 }
298 
299 int
hmfcb(void * clientp,const char * msg,const char * url)300 hmfcb(void *clientp, const char *msg, const char *url)
301 {
302     CbData *data = clientp;
303     if (data->hmfcb)
304         return data->hmfcb(data->userdata, msg, url, data->metadata);
305     return LR_CB_OK;
306 }
307 
308 gboolean
lr_prepare_repodata_dir(LrHandle * handle,GError ** err)309 lr_prepare_repodata_dir(LrHandle *handle,
310                         GError **err)
311 {
312     int rc;
313     int create_repodata_dir = 1;
314     char *path_to_repodata;
315 
316     path_to_repodata = lr_pathconcat(handle->destdir, "repodata", NULL);
317 
318     if (handle->update) {  /* Check if should create repodata/ subdir */
319         struct stat buf;
320         if (stat(path_to_repodata, &buf) != -1)
321             if (S_ISDIR(buf.st_mode))
322                 create_repodata_dir = 0;
323     }
324 
325     if (create_repodata_dir) {
326         /* Prepare repodata/ subdir */
327         rc = mkdir(path_to_repodata, S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH);
328         if (rc == -1) {
329             g_debug("%s: Cannot create dir: %s (%s)",
330                     __func__, path_to_repodata, g_strerror(errno));
331             g_set_error(err, LR_YUM_ERROR, LRE_CANNOTCREATEDIR,
332                         "Cannot create directory: %s: %s",
333                         path_to_repodata, g_strerror(errno));
334             lr_free(path_to_repodata);
335             return FALSE;
336         }
337     }
338     lr_free(path_to_repodata);
339 
340     return TRUE;
341 }
342 
343 gboolean
lr_store_mirrorlist_files(LrHandle * handle,LrYumRepo * repo,GError ** err)344 lr_store_mirrorlist_files(LrHandle *handle,
345                           LrYumRepo *repo,
346                           GError **err)
347 {
348     int fd;
349     int rc;
350 
351     if (handle->mirrorlist_fd != -1) {
352         char *ml_file_path = lr_pathconcat(handle->destdir,
353                                            "mirrorlist", NULL);
354         fd = open(ml_file_path, O_CREAT|O_TRUNC|O_RDWR, 0666);
355         if (fd < 0) {
356             g_debug("%s: Cannot create: %s", __func__, ml_file_path);
357             g_set_error(err, LR_YUM_ERROR, LRE_IO,
358                         "Cannot create %s: %s", ml_file_path, g_strerror(errno));
359             lr_free(ml_file_path);
360             return FALSE;
361         }
362         rc = lr_copy_content(handle->mirrorlist_fd, fd);
363         close(fd);
364         if (rc != 0) {
365             g_debug("%s: Cannot copy content of mirrorlist file", __func__);
366             g_set_error(err, LR_YUM_ERROR, LRE_IO,
367                         "Cannot copy content of mirrorlist file %s: %s",
368                         ml_file_path, g_strerror(errno));
369             lr_free(ml_file_path);
370             return FALSE;
371         }
372         repo->mirrorlist = ml_file_path;
373     }
374 
375     return TRUE;
376 }
377 
378 gboolean
lr_copy_metalink_content(LrHandle * handle,LrYumRepo * repo,GError ** err)379 lr_copy_metalink_content(LrHandle *handle,
380                          LrYumRepo *repo,
381                          GError **err)
382 {
383     int fd;
384     int rc;
385 
386     if (handle->metalink_fd != -1) {
387         char *ml_file_path = lr_pathconcat(handle->destdir,
388                                            "metalink.xml", NULL);
389         fd = open(ml_file_path, O_CREAT|O_TRUNC|O_RDWR, 0666);
390         if (fd < 0) {
391             g_debug("%s: Cannot create: %s", __func__, ml_file_path);
392             g_set_error(err, LR_YUM_ERROR, LRE_IO,
393                         "Cannot create %s: %s", ml_file_path, g_strerror(errno));
394             lr_free(ml_file_path);
395             return FALSE;
396         }
397         rc = lr_copy_content(handle->metalink_fd, fd);
398         close(fd);
399         if (rc != 0) {
400             g_debug("%s: Cannot copy content of metalink file", __func__);
401             g_set_error(err, LR_YUM_ERROR, LRE_IO,
402                         "Cannot copy content of metalink file %s: %s",
403                         ml_file_path, g_strerror(errno));
404             lr_free(ml_file_path);
405             return FALSE;
406         }
407         repo->metalink = ml_file_path;
408     }
409 
410     return TRUE;
411 }
412 
413 int
lr_prepare_repomd_xml_file(LrHandle * handle,char ** path,GError ** err)414 lr_prepare_repomd_xml_file(LrHandle *handle,
415                            char **path,
416                            GError **err)
417 {
418     int fd;
419 
420     *path = lr_pathconcat(handle->destdir, "/repodata/repomd.xml", NULL);
421     fd = open(*path, O_CREAT|O_TRUNC|O_RDWR, 0666);
422     if (fd == -1) {
423         g_set_error(err, LR_YUM_ERROR, LRE_IO,
424                     "Cannot open %s: %s", *path, g_strerror(errno));
425         lr_free(*path);
426         return -1;
427     }
428 
429     return fd;
430 }
431 
432 /** Check repomd.xml.asc if available.
433  * Try to download and verify GPG signature (repomd.xml.asc).
434  * Try to download only from the mirror where repomd.xml itself was
435  * downloaded. It is because most of yum repositories are not signed
436  * and try every mirror for signature is non effective.
437  * Every mirror would be tried because mirrored_download function have
438  * no clue if 404 for repomd.xml.asc means that no signature exists or
439  * it is just error on the mirror and should try the next one.
440  **/
441 gboolean
lr_check_repomd_xml_asc_availability(LrHandle * handle,LrYumRepo * repo,int fd,char * path,GError ** err)442 lr_check_repomd_xml_asc_availability(LrHandle *handle,
443                                      LrYumRepo *repo,
444                                      int fd,
445                                      char *path,
446                                      GError **err)
447 {
448     GError *tmp_err = NULL;
449     gboolean ret;
450 
451     if (handle->checks & LR_CHECK_GPG) {
452         int fd_sig;
453         char *url, *signature;
454 
455         signature = lr_pathconcat(handle->destdir, "repodata/repomd.xml.asc", NULL);
456         fd_sig = open(signature, O_CREAT | O_TRUNC | O_RDWR, 0666);
457         if (fd_sig == -1) {
458             g_debug("%s: Cannot open: %s", __func__, signature);
459             g_set_error(err, LR_YUM_ERROR, LRE_IO,
460                         "Cannot open %s: %s", signature, g_strerror(errno));
461             lr_free(signature);
462             return FALSE;
463         }
464 
465         url = lr_pathconcat(handle->used_mirror, "repodata/repomd.xml.asc", NULL);
466         ret = lr_download_url(handle, url, fd_sig, &tmp_err);
467         lr_free(url);
468         close(fd_sig);
469         if (!ret) {
470             // Error downloading signature
471             g_set_error(err, LR_YUM_ERROR, LRE_BADGPG,
472                         "GPG verification is enabled, but GPG signature "
473                         "is not available. This may be an error or the "
474                         "repository does not support GPG verification: %s", tmp_err->message);
475             g_clear_error(&tmp_err);
476             unlink(signature);
477             lr_free(signature);
478             return FALSE;
479         } else {
480             // Signature downloaded
481             repo->signature = g_strdup(signature);
482             ret = lr_gpg_check_signature(signature,
483                                          path,
484                                          handle->gnupghomedir,
485                                          &tmp_err);
486             lr_free(signature);
487             if (!ret) {
488                 g_debug("%s: GPG signature verification failed: %s",
489                         __func__, tmp_err->message);
490                 g_propagate_prefixed_error(err, tmp_err,
491                                            "repomd.xml GPG signature verification error: ");
492                 return FALSE;
493             }
494             g_debug("%s: GPG signature successfully verified", __func__);
495         }
496     }
497 
498     return TRUE;
499 }
500 
501 void
lr_get_best_checksum(const LrMetalink * metalink,GSList ** checksums)502 lr_get_best_checksum(const LrMetalink *metalink,
503                      GSList **checksums)
504 {
505     gboolean ret;
506     LrChecksumType ch_type;
507     gchar *ch_value;
508 
509     // From the metalink itself
510     ret = lr_best_checksum(metalink->hashes, &ch_type, &ch_value);
511     if (ret)
512     {
513         LrDownloadTargetChecksum *dtch;
514         dtch = lr_downloadtargetchecksum_new(ch_type, ch_value);
515         *checksums = g_slist_prepend(*checksums, dtch);
516         g_debug("%s: Expected checksum for repomd.xml: (%s) %s",
517                 __func__, lr_checksum_type_to_str(ch_type), ch_value);
518     }
519 
520     // From the alternates entries
521     for (GSList *elem = metalink->alternates; elem; elem = g_slist_next(elem))
522     {
523         LrMetalinkAlternate *alt = elem->data;
524         ret = lr_best_checksum(alt->hashes, &ch_type, &ch_value);
525         if (ret) {
526             LrDownloadTargetChecksum *dtch;
527             dtch = lr_downloadtargetchecksum_new(ch_type, ch_value);
528             *checksums = g_slist_prepend(*checksums, dtch);
529             g_debug("%s: Expected alternate checksum for repomd.xml: (%s) %s",
530                     __func__, lr_checksum_type_to_str(ch_type), ch_value);
531         }
532     }
533 }
534 
535 CbData *
lr_get_metadata_failure_callback(const LrHandle * handle)536 lr_get_metadata_failure_callback(const LrHandle *handle)
537 {
538     CbData *cbdata = NULL;
539     if (handle->hmfcb) {
540         cbdata = cbdata_new(handle->user_data,
541                             NULL,
542                             NULL,
543                             handle->hmfcb,
544                             "repomd.xml");
545     }
546     return cbdata;
547 }
548 
549 gboolean
lr_yum_download_url(LrHandle * lr_handle,const char * url,int fd,gboolean no_cache,gboolean is_zchunk,GError ** err)550 lr_yum_download_url(LrHandle *lr_handle, const char *url, int fd,
551                     gboolean no_cache, gboolean is_zchunk, GError **err)
552 {
553     gboolean ret;
554     LrDownloadTarget *target;
555     GError *tmp_err = NULL;
556     CbData *cbdata = NULL;
557 
558     assert(url);
559     assert(!err || *err == NULL);
560 
561     if (lr_handle != NULL)
562         cbdata = cbdata_new(lr_handle->user_data,
563                             NULL,
564                             lr_handle->user_cb,
565                             lr_handle->hmfcb,
566                             url);
567 
568     // Prepare target
569     target = lr_downloadtarget_new(lr_handle,
570                                    url, NULL, fd, NULL,
571                                    NULL, 0, 0,(lr_handle && lr_handle->user_cb) ? progresscb : NULL, cbdata,
572                                    NULL, (lr_handle && lr_handle->hmfcb) ? hmfcb : NULL, NULL, 0, 0,
573                                    NULL, no_cache, is_zchunk);
574 
575     // Download the target
576     ret = lr_download_target(target, &tmp_err);
577 
578     assert(ret || tmp_err);
579     assert(!(target->err) || !ret);
580     if (cbdata)
581         cbdata_free(cbdata);
582 
583     if (!ret)
584         g_propagate_error(err, tmp_err);
585 
586     lr_downloadtarget_free(target);
587 
588     lseek(fd, 0, SEEK_SET);
589 
590     return ret;
591 }
592 
593 static gboolean
lr_yum_download_repomd(LrHandle * handle,LrMetalink * metalink,int fd,GError ** err)594 lr_yum_download_repomd(LrHandle *handle,
595                        LrMetalink *metalink,
596                        int fd,
597                        GError **err)
598 {
599     int ret = TRUE;
600     GError *tmp_err = NULL;
601 
602     assert(!err || *err == NULL);
603 
604     g_debug("%s: Downloading repomd.xml via mirrorlist", __func__);
605 
606     GSList *checksums = NULL;
607     if (metalink && (handle->checks & LR_CHECK_CHECKSUM)) {
608         lr_get_best_checksum(metalink, &checksums);
609     }
610 
611     CbData *cbdata = cbdata_new(handle->user_data,
612                             NULL,
613                             handle->user_cb,
614                             handle->hmfcb,
615                             "repomd.xml");
616 
617     LrDownloadTarget *target = lr_downloadtarget_new(handle,
618                                                      "repodata/repomd.xml",
619                                                      NULL,
620                                                      fd,
621                                                      NULL,
622                                                      checksums,
623                                                      0,
624                                                      0,
625                                                      (handle->user_cb) ? progresscb : NULL,
626                                                      cbdata,
627                                                      NULL,
628                                                      (handle->hmfcb) ? hmfcb : NULL,
629                                                      NULL,
630                                                      0,
631                                                      0,
632                                                      NULL,
633                                                      TRUE,
634                                                      FALSE);
635 
636     ret = lr_download_target(target, &tmp_err);
637     assert((ret && !tmp_err) || (!ret && tmp_err));
638 
639     if (cbdata)
640         cbdata_free(cbdata);
641 
642     if (tmp_err) {
643         g_propagate_prefixed_error(err, tmp_err,
644                                    "Cannot download repomd.xml: ");
645     } else if (target->err) {
646         assert(0); // This should not happen since failfast should be TRUE
647         ret = FALSE;
648         g_set_error(err, LR_DOWNLOADER_ERROR, target->rcode,
649                     "Cannot download repomd.xml: %s",target->err);
650     } else {
651         // Set mirror used for download a repomd.xml to the handle
652         // TODO: Get rid of use_mirror attr
653         lr_free(handle->used_mirror);
654         handle->used_mirror = g_strdup(target->usedmirror);
655     }
656 
657     lr_downloadtarget_free(target);
658 
659     if (!ret) {
660         /* Download of repomd.xml was not successful */
661         g_debug("%s: repomd.xml download was unsuccessful", __func__);
662     }
663 
664     return ret;
665 }
666 
667 gboolean
prepare_repo_download_std_target(LrHandle * handle,LrYumRepoMdRecord * record,char ** path,int * fd,GSList ** checksums,GSList ** targets,GError ** err)668 prepare_repo_download_std_target(LrHandle *handle,
669                                  LrYumRepoMdRecord *record,
670                                  char **path,
671                                  int *fd,
672                                  GSList **checksums,
673                                  GSList **targets,
674                                  GError **err)
675 {
676     *path = lr_pathconcat(handle->destdir, record->location_href, NULL);
677     *fd = open(*path, O_CREAT|O_TRUNC|O_RDWR, 0666);
678     if (*fd < 0) {
679         g_debug("%s: Cannot create/open %s (%s)",
680                 __func__, *path, g_strerror(errno));
681         g_set_error(err, LR_YUM_ERROR, LRE_IO,
682                     "Cannot create/open %s: %s", *path, g_strerror(errno));
683         lr_free(*path);
684         g_slist_free_full(*targets, (GDestroyNotify) lr_downloadtarget_free);
685         return FALSE;
686     }
687 
688     if (handle->checks & LR_CHECK_CHECKSUM) {
689         // Select proper checksum type only if checksum check is enabled
690         LrDownloadTargetChecksum *checksum;
691         checksum = lr_downloadtargetchecksum_new(
692                        lr_checksum_type(record->checksum_type),
693                        record->checksum);
694         *checksums = g_slist_prepend(*checksums, checksum);
695     }
696     return TRUE;
697 }
698 
699 #ifdef WITH_ZCHUNK
700 gboolean
prepare_repo_download_zck_target(LrHandle * handle,LrYumRepoMdRecord * record,char ** path,int * fd,GSList ** checksums,GSList ** targets,GError ** err)701 prepare_repo_download_zck_target(LrHandle *handle,
702                                  LrYumRepoMdRecord *record,
703                                  char **path,
704                                  int *fd,
705                                  GSList **checksums,
706                                  GSList **targets,
707                                  GError **err)
708 {
709     *path = lr_pathconcat(handle->destdir, record->location_href, NULL);
710     *fd = open(*path, O_CREAT|O_RDWR, 0666);
711     if (*fd < 0) {
712         g_debug("%s: Cannot create/open %s (%s)",
713                 __func__, *path, g_strerror(errno));
714         g_set_error(err, LR_YUM_ERROR, LRE_IO,
715                     "Cannot create/open %s: %s", *path, g_strerror(errno));
716         lr_free(*path);
717         g_slist_free_full(*targets, (GDestroyNotify) lr_downloadtarget_free);
718         return FALSE;
719     }
720 
721     if (handle->checks & LR_CHECK_CHECKSUM) {
722         // Select proper checksum type only if checksum check is enabled
723         LrDownloadTargetChecksum *checksum;
724         checksum = lr_downloadtargetchecksum_new(
725                        lr_checksum_type(record->header_checksum_type),
726                        record->header_checksum);
727         *checksums = g_slist_prepend(*checksums, checksum);
728     }
729     return TRUE;
730 }
731 #endif /* WITH_ZCHUNK */
732 
733 gboolean
prepare_repo_download_targets(LrHandle * handle,LrYumRepo * repo,LrYumRepoMd * repomd,LrMetadataTarget * mdtarget,GSList ** targets,GSList ** cbdata_list,GError ** err)734 prepare_repo_download_targets(LrHandle *handle,
735                               LrYumRepo *repo,
736                               LrYumRepoMd *repomd,
737                               LrMetadataTarget *mdtarget,
738                               GSList **targets,
739                               GSList **cbdata_list,
740                               GError **err)
741 {
742     char *destdir;  /* Destination dir */
743 
744     destdir = handle->destdir;
745     assert(destdir);
746     assert(strlen(destdir));
747     assert(!err || *err == NULL);
748 
749     if(handle->cachedir) {
750         lr_yum_switch_to_zchunk(handle, repomd);
751         repo->use_zchunk = TRUE;
752     } else {
753         g_debug("%s: Cache directory not set, disabling zchunk", __func__);
754         repo->use_zchunk = FALSE;
755     }
756 
757     for (GSList *elem = repomd->records; elem; elem = g_slist_next(elem)) {
758         int fd;
759         char *path;
760         LrDownloadTarget *target;
761         LrYumRepoMdRecord *record = elem->data;
762         CbData *cbdata = NULL;
763         void *user_cbdata = NULL;
764         LrEndCb endcb = NULL;
765 
766         if (mdtarget != NULL) {
767             user_cbdata = mdtarget->cbdata;
768             endcb = mdtarget->endcb;
769         }
770 
771         assert(record);
772 
773         if (!lr_yum_repomd_record_enabled(handle, record->type, repomd->records))
774             continue;
775 
776         char *location_href = record->location_href;
777 
778         char *dest_dir = realpath(handle->destdir, NULL);
779         path = lr_pathconcat(handle->destdir, record->location_href, NULL);
780         char *requested_dir = realpath(dirname(path), NULL);
781         lr_free(path);
782         if (!g_str_has_prefix(requested_dir, dest_dir)) {
783             g_debug("%s: Invalid path: %s", __func__, location_href);
784             g_set_error(err, LR_YUM_ERROR, LRE_IO, "Invalid path: %s", location_href);
785             g_slist_free_full(*targets, (GDestroyNotify) lr_downloadtarget_free);
786             free(requested_dir);
787             free(dest_dir);
788             return FALSE;
789         }
790         free(requested_dir);
791         free(dest_dir);
792 
793         gboolean is_zchunk = FALSE;
794         #ifdef WITH_ZCHUNK
795         if (handle->cachedir && record->header_checksum)
796             is_zchunk = TRUE;
797         #endif /* WITH_ZCHUNK */
798 
799         GSList *checksums = NULL;
800         if (is_zchunk) {
801             #ifdef WITH_ZCHUNK
802             if(!prepare_repo_download_zck_target(handle, record, &path, &fd,
803                                                  &checksums, targets, err))
804                 return FALSE;
805             #endif /* WITH_ZCHUNK */
806         } else {
807             if(!prepare_repo_download_std_target(handle, record, &path, &fd,
808                                                  &checksums, targets, err))
809                 return FALSE;
810         }
811 
812         if (handle->user_cb || handle->hmfcb) {
813             cbdata = cbdata_new(handle->user_data,
814                                 user_cbdata,
815                                 handle->user_cb,
816                                 handle->hmfcb,
817                                 record->type);
818             *cbdata_list = g_slist_append(*cbdata_list, cbdata);
819         }
820 
821         target = lr_downloadtarget_new(handle,
822                                        location_href,
823                                        record->location_base,
824                                        fd,
825                                        NULL,
826                                        checksums,
827                                        0,
828                                        0,
829                                        NULL,
830                                        cbdata,
831                                        endcb,
832                                        NULL,
833                                        NULL,
834                                        0,
835                                        0,
836                                        NULL,
837                                        FALSE,
838                                        is_zchunk);
839 
840         if(is_zchunk) {
841             #ifdef WITH_ZCHUNK
842             target->expectedsize = record->size_header;
843             target->zck_header_size = record->size_header;
844             #endif /* WITH_ZCHUNK */
845         }
846 
847         if (mdtarget != NULL)
848             mdtarget->repomd_records_to_download++;
849         *targets = g_slist_append(*targets, target);
850 
851         /* Because path may already exists in repo (while update) */
852         lr_yum_repo_update(repo, record->type, path);
853         lr_free(path);
854     }
855 
856     return TRUE;
857 }
858 
859 gboolean
error_handling(GSList * targets,GError ** dest_error,GError * src_error)860 error_handling(GSList *targets, GError **dest_error, GError *src_error)
861 {
862     if (src_error) {
863         g_propagate_prefixed_error(dest_error, src_error,
864                                    "Downloading error: ");
865         return FALSE;
866     } else {
867         int code = LRE_OK;
868         char *error_summary = NULL;
869 
870         for (GSList *elem = targets; elem; elem = g_slist_next(elem)) {
871             LrDownloadTarget *target = elem->data;
872             if (target->rcode != LRE_OK) {
873                 if (code == LRE_OK) {
874                     // First failed download target found
875                     code = target->rcode;
876                     error_summary = g_strconcat(target->path,
877                                                 " - ",
878                                                 target->err,
879                                                 NULL);
880                 } else {
881                     char *tmp = error_summary;
882                     error_summary = g_strconcat(error_summary,
883                                                 "; ",
884                                                 target->path,
885                                                 " - ",
886                                                 target->err,
887                                                 NULL);
888                     g_free(tmp);
889                 }
890             }
891 
892             close(target->fd);
893         }
894 
895         if (code != LRE_OK) {
896             // At least one target failed
897             g_set_error(dest_error, LR_DOWNLOADER_ERROR, code,
898                         "Downloading error(s): %s", error_summary);
899             g_free(error_summary);
900             return FALSE;
901         }
902     }
903 
904     return TRUE;
905 }
906 
907 gboolean
lr_yum_download_repos(GSList * targets,GError ** err)908 lr_yum_download_repos(GSList *targets,
909                       GError **err)
910 {
911     gboolean ret;
912     GSList *download_targets = NULL;
913     GSList *cbdata_list = NULL;
914     GError *download_error = NULL;
915 
916     for (GSList *elem = targets; elem; elem = g_slist_next(elem)) {
917         LrMetadataTarget *target = elem->data;
918 
919         if (!target->handle) {
920             continue;
921         }
922 
923         prepare_repo_download_targets(target->handle,
924                                       target->repo,
925                                       target->repomd,
926                                       target,
927                                       &download_targets,
928                                       &cbdata_list,
929                                       &download_error);
930     }
931 
932     if (!download_targets) {
933         g_propagate_error(err, download_error);
934         return TRUE;
935     }
936 
937     ret = lr_download_single_cb(download_targets,
938                                 FALSE,
939                                 (cbdata_list) ? progresscb : NULL,
940                                 (cbdata_list) ? hmfcb : NULL,
941                                 &download_error);
942 
943     error_handling(download_targets, err, download_error);
944 
945     g_slist_free_full(cbdata_list, (GDestroyNotify)cbdata_free);
946     g_slist_free_full(download_targets, (GDestroyNotify)lr_downloadtarget_free);
947 
948     return ret;
949 }
950 
951 gboolean
lr_yum_download_repo(LrHandle * handle,LrYumRepo * repo,LrYumRepoMd * repomd,GError ** err)952 lr_yum_download_repo(LrHandle *handle,
953                      LrYumRepo *repo,
954                      LrYumRepoMd *repomd,
955                      GError **err)
956 {
957     gboolean ret = TRUE;
958     GSList *targets = NULL;
959     GSList *cbdata_list = NULL;
960     GError *tmp_err = NULL;
961 
962     assert(!err || *err == NULL);
963 
964     prepare_repo_download_targets(handle, repo, repomd, NULL, &targets, &cbdata_list, err);
965 
966     if (!targets)
967         return TRUE;
968 
969     ret = lr_download_single_cb(targets,
970                                 FALSE,
971                                 (cbdata_list) ? progresscb : NULL,
972                                 (cbdata_list) ? hmfcb : NULL,
973                                 &tmp_err);
974 
975     assert((ret && !tmp_err) || (!ret && tmp_err));
976     ret = error_handling(targets, err, tmp_err);
977 
978     g_slist_free_full(cbdata_list, (GDestroyNotify)cbdata_free);
979     g_slist_free_full(targets, (GDestroyNotify)lr_downloadtarget_free);
980 
981     return ret;
982 }
983 
984 static gboolean
lr_yum_check_checksum_of_md_record(LrYumRepoMdRecord * rec,const char * path,GError ** err)985 lr_yum_check_checksum_of_md_record(LrYumRepoMdRecord *rec,
986                                    const char *path,
987                                    GError **err)
988 {
989     int fd;
990     char *expected_checksum;
991     LrChecksumType checksum_type;
992     gboolean ret, matches;
993     gboolean is_zchunk = FALSE;
994     GError *tmp_err = NULL;
995 
996     assert(!err || *err == NULL);
997 
998     if (!rec || !path)
999         return TRUE;
1000 
1001     #ifdef WITH_ZCHUNK
1002     if(rec->header_checksum) {
1003         expected_checksum = rec->header_checksum;
1004         checksum_type = lr_checksum_type(rec->header_checksum_type);
1005         is_zchunk = TRUE;
1006     } else {
1007     #endif /* WITH_ZCHUNK */
1008         expected_checksum = rec->checksum;
1009         checksum_type = lr_checksum_type(rec->checksum_type);
1010     #ifdef WITH_ZCHUNK
1011     }
1012     #endif /* WITH_ZCHUNK */
1013 
1014     g_debug("%s: Checking checksum of %s (expected: %s [%s])",
1015                        __func__, path, expected_checksum, rec->checksum_type);
1016 
1017     if (!expected_checksum) {
1018         // Empty checksum - suppose it's ok
1019         g_debug("%s: No checksum in repomd", __func__);
1020         return TRUE;
1021     }
1022 
1023     if (checksum_type == LR_CHECKSUM_UNKNOWN) {
1024         g_debug("%s: Unknown checksum", __func__);
1025         g_set_error(err, LR_YUM_ERROR, LRE_UNKNOWNCHECKSUM,
1026                     "Unknown checksum type for %s", path);
1027         return FALSE;
1028     }
1029 
1030     fd = open(path, O_RDONLY);
1031     if (fd < 0) {
1032         g_debug("%s: Cannot open %s", __func__, path);
1033         g_set_error(err, LR_YUM_ERROR, LRE_IO,
1034                     "Cannot open %s: %s", path, g_strerror(errno));
1035         return FALSE;
1036     }
1037 
1038     if (is_zchunk) {
1039         #ifdef WITH_ZCHUNK
1040         ret = FALSE;
1041         matches = FALSE;
1042         zckCtx *zck = lr_zck_init_read_base(expected_checksum, checksum_type,
1043                                             rec->size_header, fd, &tmp_err);
1044         if (!tmp_err) {
1045             if(zck_validate_data_checksum(zck) < 1) {
1046                 g_set_error(&tmp_err, LR_YUM_ERROR, LRE_ZCK,
1047                             "Unable to validate zchunk checksums");
1048             } else {
1049                 ret = TRUE;
1050                 matches = TRUE;
1051             }
1052         }
1053         if (zck)
1054             zck_free(&zck);
1055         #endif /* WITH_ZCHUNK */
1056     } else {
1057         ret = lr_checksum_fd_cmp(checksum_type,
1058                                  fd,
1059                                  expected_checksum,
1060                                  1,
1061                                  &matches,
1062                                  &tmp_err);
1063     }
1064 
1065     close(fd);
1066 
1067     assert(ret || tmp_err);
1068 
1069     if (!ret) {
1070         // Checksum calculation error
1071         g_debug("%s: Checksum check %s - Error: %s",
1072                 __func__, path, tmp_err->message);
1073         g_propagate_prefixed_error(err, tmp_err,
1074                                    "Checksum error %s: ", path);
1075         return FALSE;
1076     } else if (!matches) {
1077         g_debug("%s: Checksum check %s - Mismatch", __func__, path);
1078         g_set_error(err, LR_YUM_ERROR, LRE_BADCHECKSUM,
1079                     "Checksum mismatch %s", path);
1080         return FALSE;
1081     }
1082 
1083     g_debug("%s: Checksum check - Passed", __func__);
1084 
1085     return TRUE;
1086 }
1087 
1088 static gboolean
lr_yum_check_repo_checksums(LrYumRepo * repo,LrYumRepoMd * repomd,GError ** err)1089 lr_yum_check_repo_checksums(LrYumRepo *repo,
1090                             LrYumRepoMd *repomd,
1091                             GError **err)
1092 {
1093     assert(!err || *err == NULL);
1094 
1095     for (GSList *elem = repomd->records; elem; elem = g_slist_next(elem)) {
1096         gboolean ret;
1097         LrYumRepoMdRecord *record = elem->data;
1098 
1099         assert(record);
1100 
1101         const char *path = yum_repo_path(repo, record->type);
1102 
1103         ret = lr_yum_check_checksum_of_md_record(record, path, err);
1104         if (!ret)
1105             return FALSE;
1106     }
1107 
1108     return TRUE;
1109 }
1110 
1111 static gboolean
lr_yum_use_local_load_base(LrHandle * handle,LrResult * result,LrYumRepo * repo,LrYumRepoMd * repomd,const gchar * baseurl,GError ** err)1112 lr_yum_use_local_load_base(LrHandle *handle,
1113                            LrResult *result,
1114                            LrYumRepo *repo,
1115                            LrYumRepoMd *repomd,
1116                            const gchar *baseurl,
1117                            GError **err)
1118 {
1119     gboolean ret;
1120     GError *tmp_err = NULL;
1121     _cleanup_free_ gchar *path = NULL;
1122     _cleanup_free_ gchar *sig = NULL;
1123     _cleanup_fd_close_ int fd = -1;
1124 
1125     if (handle->mirrorlist_fd != -1) {
1126         // Locate mirrorlist if available.
1127         gchar *mrl_fn = lr_pathconcat(baseurl, "mirrorlist", NULL);
1128         if (g_file_test(mrl_fn, G_FILE_TEST_IS_REGULAR)) {
1129             g_debug("%s: Found local mirrorlist: %s", __func__, mrl_fn);
1130             repo->mirrorlist = mrl_fn;
1131         } else {
1132             repo->mirrorlist = NULL;
1133             lr_free(mrl_fn);
1134         }
1135     }
1136 
1137     if (handle->metalink_fd != -1) {
1138         // Locate metalink.xml if available.
1139         gchar *mtl_fn = lr_pathconcat(baseurl, "metalink.xml", NULL);
1140         if (g_file_test(mtl_fn, G_FILE_TEST_IS_REGULAR)) {
1141             g_debug("%s: Found local metalink: %s", __func__, mtl_fn);
1142             repo->metalink = mtl_fn;
1143         } else {
1144             repo->metalink = NULL;
1145             lr_free(mtl_fn);
1146         }
1147     }
1148 
1149     // Open repomd.xml
1150     path = lr_pathconcat(baseurl, "repodata/repomd.xml", NULL);
1151     fd = open(path, O_RDONLY);
1152     if (fd < 0) {
1153         g_debug("%s: open(%s): %s", __func__, path, g_strerror(errno));
1154         g_set_error(err, LR_YUM_ERROR, LRE_IO,
1155                     "Cannot open %s: %s", path, g_strerror(errno));
1156         return FALSE;
1157     }
1158 
1159     // Parse repomd.xml
1160     g_debug("%s: Parsing repomd.xml", __func__);
1161     ret = lr_yum_repomd_parse_file(repomd, fd, lr_xml_parser_warning_logger,
1162                                    "Repomd xml parser", &tmp_err);
1163     if (!ret) {
1164         g_debug("%s: Parsing unsuccessful: %s", __func__, tmp_err->message);
1165         g_propagate_prefixed_error(err, tmp_err,
1166                                    "repomd.xml parser error: ");
1167         return FALSE;
1168     }
1169 
1170     // Fill result object
1171     result->destdir = g_strdup(baseurl);
1172     repo->destdir = g_strdup(baseurl);
1173     repo->repomd = g_strdup(path);
1174 
1175     // Check if signature file exists
1176     sig = lr_pathconcat(baseurl, "repodata/repomd.xml.asc", NULL);
1177     if (access(sig, F_OK) == 0)
1178         repo->signature = g_strdup(sig);
1179 
1180     // Signature checking
1181     if (handle->checks & LR_CHECK_GPG) {
1182 
1183         if (!repo->signature) {
1184             // Signature doesn't exist
1185             g_set_error(err, LR_YUM_ERROR, LRE_BADGPG,
1186                         "GPG verification is enabled, but GPG signature "
1187                         "repomd.xml.asc is not available. This may be an "
1188                         "error or the repository does not support GPG verification.");
1189             return FALSE;
1190         }
1191 
1192         ret = lr_gpg_check_signature(repo->signature,
1193                                      repo->repomd,
1194                                      handle->gnupghomedir,
1195                                      &tmp_err);
1196         if (!ret) {
1197             g_debug("%s: repomd.xml GPG signature verification failed: %s",
1198                     __func__, tmp_err->message);
1199             g_propagate_prefixed_error(err, tmp_err,
1200                         "repomd.xml GPG signature verification failed: ");
1201             return FALSE;
1202         }
1203     }
1204 
1205     // Done - repomd is loaded and checked
1206     g_debug("%s: Repomd revision: %s", __func__, repomd->revision);
1207 
1208     return TRUE;
1209 }
1210 
1211 /* Do not duplicate repoata, just locate the local one */
1212 static gboolean
lr_yum_use_local(LrHandle * handle,LrResult * result,GError ** err)1213 lr_yum_use_local(LrHandle *handle, LrResult *result, GError **err)
1214 {
1215     char *baseurl;
1216     LrYumRepo *repo;
1217     LrYumRepoMd *repomd;
1218 
1219     assert(!err || *err == NULL);
1220 
1221     g_debug("%s: Locating repo..", __func__);
1222 
1223     // Shortcuts
1224     repo   = result->yum_repo;
1225     repomd = result->yum_repomd;
1226     baseurl = handle->urls[0];
1227 
1228     // Skip "file://" prefix if present
1229     if (g_str_has_prefix(baseurl, "file://"))
1230         baseurl += 7;
1231     else if (g_str_has_prefix(baseurl, "file:"))
1232         baseurl += 5;
1233 
1234     // Check sanity
1235     if (strstr(baseurl, "://")) {
1236         g_set_error(err, LR_YUM_ERROR, LRE_NOTLOCAL,
1237                     "URL: %s doesn't seem to be a local repository",
1238                     baseurl);
1239         return FALSE;
1240     }
1241 
1242     if (!handle->update) {
1243         // Load repomd.xml and mirrorlist+metalink if locally available
1244         if (!lr_yum_use_local_load_base(handle, result, repo, repomd, baseurl, err))
1245             return FALSE;
1246     }
1247 
1248     if(handle->cachedir) {
1249         lr_yum_switch_to_zchunk(handle, repomd);
1250         repo->use_zchunk = TRUE;
1251     } else {
1252         g_debug("%s: Cache directory not set, disabling zchunk", __func__);
1253         repo->use_zchunk = FALSE;
1254     }
1255 
1256     // Locate rest of metadata files
1257     for (GSList *elem = repomd->records; elem; elem = g_slist_next(elem)) {
1258         _cleanup_free_ char *path = NULL;
1259         LrYumRepoMdRecord *record = elem->data;
1260 
1261         assert(record);
1262 
1263         if (!lr_yum_repomd_record_enabled(handle, record->type, repomd->records))
1264             continue; // Caller isn't interested in this record type
1265         if (yum_repo_path(repo, record->type))
1266             continue; // This path already exists in repo
1267 
1268         path = lr_pathconcat(baseurl, record->location_href, NULL);
1269 
1270         if (access(path, F_OK) == -1) {
1271             // A repo file is missing
1272             if (!handle->ignoremissing) {
1273                 g_debug("%s: Incomplete repository - %s is missing",
1274                         __func__, path);
1275                 g_set_error(err, LR_YUM_ERROR, LRE_INCOMPLETEREPO,
1276                             "Incomplete repository - %s is missing",
1277                             path);
1278                 return FALSE;
1279             }
1280 
1281             continue;
1282         }
1283 
1284         lr_yum_repo_append(repo, record->type, path);
1285     }
1286 
1287     g_debug("%s: Repository was successfully located", __func__);
1288     return TRUE;
1289 }
1290 
1291 static gboolean
lr_yum_download_remote(LrHandle * handle,LrResult * result,GError ** err)1292 lr_yum_download_remote(LrHandle *handle, LrResult *result, GError **err)
1293 {
1294     gboolean ret = TRUE;
1295     int fd;
1296     LrYumRepo *repo;
1297     LrYumRepoMd *repomd;
1298     GError *tmp_err = NULL;
1299 
1300     assert(!err || *err == NULL);
1301 
1302     repo   = result->yum_repo;
1303     repomd = result->yum_repomd;
1304 
1305     g_debug("%s: Downloading/Copying repo..", __func__);
1306 
1307     if (!lr_prepare_repodata_dir(handle, err))
1308         return FALSE;
1309 
1310     if (!handle->update) {
1311         char *path = NULL;
1312 
1313         if (!lr_store_mirrorlist_files(handle, repo, err))
1314             return FALSE;
1315 
1316         if (!lr_copy_metalink_content(handle, repo, err))
1317             return FALSE;
1318 
1319         if ((fd = lr_prepare_repomd_xml_file(handle, &path, err)) == -1)
1320             return FALSE;
1321 
1322         /* Download repomd.xml */
1323         ret = lr_yum_download_repomd(handle, handle->metalink, fd, err);
1324         if (!ret) {
1325             close(fd);
1326             lr_free(path);
1327             return FALSE;
1328         }
1329 
1330         if (!lr_check_repomd_xml_asc_availability(handle, repo, fd, path, err)) {
1331             close(fd);
1332             lr_free(path);
1333             return FALSE;
1334         }
1335 
1336         lseek(fd, 0, SEEK_SET);
1337 
1338         /* Parse repomd */
1339         g_debug("%s: Parsing repomd.xml", __func__);
1340         ret = lr_yum_repomd_parse_file(repomd, fd, lr_xml_parser_warning_logger,
1341                                        "Repomd xml parser", &tmp_err);
1342         close(fd);
1343         if (!ret) {
1344             g_debug("%s: Parsing unsuccessful: %s", __func__, tmp_err->message);
1345             g_propagate_prefixed_error(err, tmp_err,
1346                                        "repomd.xml parser error: ");
1347             lr_free(path);
1348             return FALSE;
1349         }
1350 
1351         /* Fill result object */
1352         result->destdir = g_strdup(handle->destdir);
1353         repo->destdir = g_strdup(handle->destdir);
1354         repo->repomd = path;
1355         if (handle->used_mirror)
1356             repo->url = g_strdup(handle->used_mirror);
1357         else
1358             repo->url = g_strdup(handle->urls[0]);
1359 
1360         g_debug("%s: Repomd revision: %s", repomd->revision, __func__);
1361     }
1362 
1363     /* Download rest of metadata files */
1364     ret = lr_yum_download_repo(handle, repo, repomd, &tmp_err);
1365     assert((ret && !tmp_err) || (!ret && tmp_err));
1366 
1367     if (!ret) {
1368         g_debug("%s: Repository download error: %s", __func__, tmp_err->message);
1369         g_propagate_prefixed_error(err, tmp_err, "Yum repo downloading error: ");
1370         return FALSE;
1371     }
1372 
1373     return TRUE;
1374 }
1375 
1376 gboolean
lr_yum_perform(LrHandle * handle,LrResult * result,GError ** err)1377 lr_yum_perform(LrHandle *handle, LrResult *result, GError **err)
1378 {
1379     int ret = TRUE;
1380     LrYumRepo *repo;
1381     LrYumRepoMd *repomd;
1382 
1383     assert(handle);
1384     assert(!err || *err == NULL);
1385 
1386     if (!result) {
1387         g_set_error(err, LR_YUM_ERROR, LRE_BADFUNCARG,
1388                     "Missing result parameter");
1389         return FALSE;
1390     }
1391 
1392     if (!handle->urls && !handle->mirrorlisturl && !handle->metalinkurl) {
1393         g_set_error(err, LR_YUM_ERROR, LRE_NOURL,
1394                 "No LRO_URLS, LRO_MIRRORLISTURL nor LRO_METALINKURL specified");
1395         return FALSE;
1396     }
1397 
1398     if (handle->local && (!handle->urls || !handle->urls[0])) {
1399         g_set_error(err, LR_YUM_ERROR, LRE_NOURL,
1400                     "Localrepo specified, but no LRO_URLS set");
1401         return FALSE;
1402     }
1403 
1404     if (handle->update) {
1405         // Download/Locate only specified files
1406         if (!result->yum_repo || !result->yum_repomd) {
1407             g_set_error(err, LR_YUM_ERROR, LRE_INCOMPLETERESULT,
1408                     "Incomplete result object - "
1409                     "Cannot update on this result object");
1410             return FALSE;
1411         }
1412     } else {
1413         // Download/Locate from scratch
1414         if (result->yum_repo || result->yum_repomd) {
1415             g_set_error(err, LR_YUM_ERROR, LRE_ALREADYUSEDRESULT,
1416                         "This result object is not clear - "
1417                         "Already used result object");
1418             return FALSE;
1419         }
1420         result->yum_repo = lr_yum_repo_init();
1421         result->yum_repomd = lr_yum_repomd_init();
1422     }
1423 
1424     repo   = result->yum_repo;
1425     repomd = result->yum_repomd;
1426 
1427     if (handle->local) {
1428         // Do not duplicate repository, just use the existing local one
1429 
1430         ret = lr_yum_use_local(handle, result, err);
1431         if (!ret)
1432             return FALSE;
1433 
1434         if (handle->checks & LR_CHECK_CHECKSUM)
1435             ret = lr_yum_check_repo_checksums(repo, repomd, err);
1436     } else {
1437         // Download remote/Duplicate local repository
1438         // Note: All checksums are checked while downloading
1439 
1440         ret = lr_yum_download_remote(handle, result, err);
1441     }
1442 
1443     return ret;
1444 }
1445