1 /*
2  * Copyright (C) 2012-2013 Red Hat, Inc.
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 #include <assert.h>
22 #include <errno.h>
23 #include <dirent.h>
24 #include <fcntl.h>
25 #if defined(__APPLE__) || defined(__FreeBSD__)
26 #include <limits.h>
27 #else
28 #include <linux/limits.h>
29 #endif
30 #include <pwd.h>
31 #include <unistd.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <sys/utsname.h>
38 #include <wordexp.h>
39 
40 // libsolv
41 extern "C" {
42 #include <solv/chksum.h>
43 #include <solv/evr.h>
44 #include <solv/solver.h>
45 #include <solv/solverdebug.h>
46 #include <solv/util.h>
47 #include <solv/pool_parserpmrichdep.h>
48 }
49 
50 // hawkey
51 #include "catch-error.hpp"
52 #include "dnf-advisory-private.hpp"
53 #include "dnf-types.h"
54 #include "hy-iutil-private.hpp"
55 #include "hy-package-private.hpp"
56 #include "hy-packageset-private.hpp"
57 #include "hy-query.h"
58 #include "hy-util-private.hpp"
59 #include "dnf-sack-private.hpp"
60 #include "sack/packageset.hpp"
61 
62 #include "utils/bgettext/bgettext-lib.h"
63 #include "sack/packageset.hpp"
64 
65 // glib
66 #include <glib.h>
67 #include <gio/gio.h>
68 
69 #include <string>
70 
71 #define BUF_BLOCK 4096
72 #define CHKSUM_TYPE REPOKEY_TYPE_SHA256
73 #define CHKSUM_IDENT "H000"
74 #define CACHEDIR_PERMISSIONS 0700
75 
76 static mode_t
get_umask(void)77 get_umask(void)
78 {
79     mode_t mask = umask(0);
80     umask(mask);
81     return mask;
82 }
83 
84 static int
glob_for_cachedir(char * path)85 glob_for_cachedir(char *path)
86 {
87     int ret = 1;
88     if (!g_str_has_suffix(path, "XXXXXX"))
89         return ret;
90 
91     wordexp_t word_vector;
92     char *p = g_strdup(path);
93     const int len = strlen(p);
94     struct stat s;
95 
96     ret = 2;
97     p[len-6] = '*';
98     p[len-5] = '\0';
99     if (wordexp(p, &word_vector, 0)) {
100         g_free(p);
101         return ret;
102     }
103     for (guint i = 0; i < word_vector.we_wordc; ++i) {
104         char *entry = word_vector.we_wordv[i];
105         if (stat(entry, &s))
106             continue;
107         if (S_ISDIR(s.st_mode) &&
108             s.st_uid == getuid()) {
109             assert(strlen(path) == strlen(entry));
110             strcpy(path, entry);
111             ret = 0;
112             break;
113         }
114     }
115     wordfree(&word_vector);
116     g_free(p);
117     return ret;
118 }
119 
120 int
checksum_cmp(const unsigned char * cs1,const unsigned char * cs2)121 checksum_cmp(const unsigned char *cs1, const unsigned char *cs2)
122 {
123     return memcmp(cs1, cs2, CHKSUM_BYTES);
124 }
125 
126 /* calls rewind(fp) before returning */
127 int
checksum_fp(unsigned char * out,FILE * fp)128 checksum_fp(unsigned char *out, FILE *fp)
129 {
130     /* based on calc_checksum_fp in libsolv's solv.c */
131     char buf[4096];
132     auto h = solv_chksum_create(CHKSUM_TYPE);
133     int l;
134 
135     rewind(fp);
136     solv_chksum_add(h, CHKSUM_IDENT, strlen(CHKSUM_IDENT));
137     while ((l = fread(buf, 1, sizeof(buf), fp)) > 0)
138         solv_chksum_add(h, buf, l);
139     rewind(fp);
140     solv_chksum_free(h, out);
141     return 0;
142 }
143 
144 /* calls rewind(fp) before returning */
145 int
checksum_read(unsigned char * csout,FILE * fp)146 checksum_read(unsigned char *csout, FILE *fp)
147 {
148     if (fseek(fp, -32, SEEK_END) ||
149         fread(csout, CHKSUM_BYTES, 1, fp) != 1)
150         return 1;
151     rewind(fp);
152     return 0;
153 }
154 
155 /* does not move the fp position */
156 int
checksum_stat(unsigned char * out,FILE * fp)157 checksum_stat(unsigned char *out, FILE *fp)
158 {
159     assert(fp);
160 
161     struct stat stat;
162     if (fstat(fileno(fp), &stat))
163         return 1;
164 
165     /* based on calc_checksum_stat in libsolv's solv.c */
166     auto h = solv_chksum_create(CHKSUM_TYPE);
167     solv_chksum_add(h, CHKSUM_IDENT, strlen(CHKSUM_IDENT));
168     solv_chksum_add(h, &stat.st_dev, sizeof(stat.st_dev));
169     solv_chksum_add(h, &stat.st_ino, sizeof(stat.st_ino));
170     solv_chksum_add(h, &stat.st_size, sizeof(stat.st_size));
171     solv_chksum_add(h, &stat.st_mtime, sizeof(stat.st_mtime));
172     solv_chksum_free(h, out);
173     return 0;
174 }
175 
176 /* moves fp to the end of file */
checksum_write(const unsigned char * cs,FILE * fp)177 int checksum_write(const unsigned char *cs, FILE *fp)
178 {
179     if (fseek(fp, 0, SEEK_END) ||
180         fwrite(cs, CHKSUM_BYTES, 1, fp) != 1)
181         return 1;
182     return 0;
183 }
184 
185 int
checksum_type2length(int type)186 checksum_type2length(int type)
187 {
188     switch(type) {
189     case G_CHECKSUM_MD5:
190         return 16;
191     case G_CHECKSUM_SHA1:
192         return 20;
193     case G_CHECKSUM_SHA256:
194         return 32;
195     case G_CHECKSUM_SHA384:
196         return 48;
197     case G_CHECKSUM_SHA512:
198         return 64;
199     default:
200         return -1;
201     }
202 }
203 
204 int
checksumt_l2h(int type)205 checksumt_l2h(int type)
206 {
207     switch (type) {
208     case REPOKEY_TYPE_MD5:
209         return G_CHECKSUM_MD5;
210     case REPOKEY_TYPE_SHA1:
211         return G_CHECKSUM_SHA1;
212     case REPOKEY_TYPE_SHA256:
213         return G_CHECKSUM_SHA256;
214     case REPOKEY_TYPE_SHA384:
215         return G_CHECKSUM_SHA384;
216     case REPOKEY_TYPE_SHA512:
217         return G_CHECKSUM_SHA512;
218     default:
219         assert(0);
220         return 0;
221     }
222 }
223 
224 const char *
pool_checksum_str(Pool * pool,const unsigned char * chksum)225 pool_checksum_str(Pool *pool, const unsigned char *chksum)
226 {
227     int length = checksum_type2length(checksumt_l2h(CHKSUM_TYPE));
228     return pool_bin2hex(pool, chksum, length);
229 }
230 
231 char *
abspath(const char * path)232 abspath(const char *path)
233 {
234     const int len = strlen(path);
235     if (len <= 1)
236         return NULL;
237 
238     if (path[0] == '/')
239         return g_strdup(path);
240 
241     char cwd[PATH_MAX];
242     if (!getcwd(cwd, PATH_MAX)) {
243         return NULL;
244     }
245 
246     return solv_dupjoin(cwd, "/", path);
247 }
248 
249 Map *
free_map_fully(Map * m)250 free_map_fully(Map *m)
251 {
252     if (m == NULL)
253         return NULL;
254     map_free(m);
255     g_free(m);
256     return NULL;
257 }
258 
259 int
is_package(const Pool * pool,const Solvable * s)260 is_package(const Pool *pool, const Solvable *s)
261 {
262     return !g_str_has_prefix(pool_id2str(pool, s->name), SOLVABLE_NAME_ADVISORY_PREFIX);
263 }
264 
265 int
is_readable_rpm(const char * fn)266 is_readable_rpm(const char *fn)
267 {
268     int len = strlen(fn);
269 
270     if (access(fn, R_OK))
271         return 0;
272     if (len <= 4 || strcmp(fn + len - 4, ".rpm"))
273         return 0;
274 
275     return 1;
276 }
277 
278 /**
279  * Recursively create directory.
280  *
281  * If it is in the format accepted by mkdtemp() the function globs for a
282  * matching name and if not found it uses mkdtemp() to create the path. 'path'
283  * is modified in those two cases.
284  */
285 int
mkcachedir(char * path)286 mkcachedir(char *path)
287 {
288     int ret = 1;
289 
290     if (!glob_for_cachedir(path))
291         return 0;
292 
293     const int len = strlen(path);
294     if (len < 1 || path[0] != '/')
295         return 1; // only absolute pathnames are accepted
296 
297     char *p = g_strdup(path);
298 
299     if (p[len-1] == '/')
300         p[len-1] = '\0';
301 
302     if (access(p, X_OK)) {
303         *(strrchr(p, '/')) = '\0';
304         ret = mkcachedir(p);
305         if (g_str_has_suffix(path, "XXXXXX")) {
306             char *retptr = mkdtemp(path);
307             if (retptr == NULL)
308                 ret |= 1;
309         } else
310             ret |= mkdir(path, CACHEDIR_PERMISSIONS);
311     } else {
312         ret = 0;
313     }
314 
315     g_free(p);
316     return ret;
317 }
318 
319 gboolean
mv(const char * old_path,const char * new_path,GError ** error)320 mv(const char* old_path, const char* new_path, GError** error) try
321 {
322     if (rename(old_path, new_path)) {
323         g_set_error(error,
324                     DNF_ERROR,
325                     DNF_ERROR_FILE_INVALID,
326                     _("Failed renaming %1$s to %2$s: %3$s"),
327                     old_path, new_path, strerror(errno));
328         return FALSE;
329     }
330     if (chmod(new_path, 0666 & ~get_umask())) {
331         g_set_error(error,
332                     DNF_ERROR,
333                     DNF_ERROR_FILE_INVALID,
334                     _("Failed setting perms on %1$s: %2$s"),
335                     new_path, strerror(errno));
336         return FALSE;
337     }
338     return TRUE;
339 } CATCH_TO_GERROR(FALSE)
340 
341 /**
342  * dnf_remove_recursive_v2:
343  * @directory: A directory path
344  * @error: A #GError, or %NULL
345  *
346  * Removes a file or a directory and its contents.
347  *
348  * Returns: %FALSE if an error was set
349  **/
350 gboolean
351 dnf_remove_recursive_v2(const gchar *path, GError **error) try
352 {
353     if (g_file_test(path, G_FILE_TEST_IS_DIR))
354         return dnf_remove_recursive(path, error);
355     else
356         return dnf_ensure_file_unlinked(path, error);
CATCH_TO_GERROR(FALSE)357 } CATCH_TO_GERROR(FALSE)
358 
359 gboolean
360 dnf_copy_file(const std::string & srcPath, const std::string & dstPath, GError ** error) try
361 {
362     g_autoptr(GFile) src = g_file_new_for_path(srcPath.c_str());
363     g_autoptr(GFile) dest = g_file_new_for_path(dstPath.c_str());
364     return g_file_copy(src, dest,
365         static_cast<GFileCopyFlags>(G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA),
366         NULL, NULL, NULL, error);
367 } CATCH_TO_GERROR(FALSE)
368 
369 gboolean
370 dnf_copy_recursive(const std::string & srcPath, const std::string & dstPath, GError ** error) try
371 {
372     struct stat info;
373     if (!stat(srcPath.c_str(), &info)) {
374         if (S_ISDIR(info.st_mode)) {
375             if (mkdir(dstPath.c_str(), info.st_mode) == -1) {
376                 auto err = errno;
377                 g_set_error(error,
378                     DNF_ERROR,
379                     DNF_ERROR_INTERNAL_ERROR,
380                     _("cannot create directory %1$s: %2$s"),
381                     dstPath.c_str(), strerror(err));
382                 return FALSE;
383             }
384             if (auto fd = opendir(srcPath.c_str())) {
385                 gboolean ret = TRUE;
386                 while (auto dent = readdir(fd)) {
387                     auto name = dent->d_name;
388                     if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
389                         continue;
390                     std::string srcItem = srcPath + "/" + name;
391                     std::string dstItem = dstPath + "/" + name;
392                     ret = dnf_copy_recursive(srcItem, dstItem, error);
393                     if (!ret)
394                         break;
395                 }
396                 closedir(fd);
397                 return ret;
398             } else {
399                 auto err = errno;
400                 g_set_error(error,
401                     DNF_ERROR,
402                     DNF_ERROR_INTERNAL_ERROR,
403                     _("cannot open directory %1$s: %2$s"),
404                     srcPath.c_str(), strerror(err));
405                 return FALSE;
406             }
407         } else {
408             return dnf_copy_file(srcPath, dstPath, error);
409         }
410     } else {
411         auto err = errno;
412         g_set_error(error,
413             DNF_ERROR,
414             DNF_ERROR_INTERNAL_ERROR,
415             _("cannot stat path %1$s: %2$s"),
416             srcPath.c_str(), strerror(err));
417         return FALSE;
418     }
CATCH_TO_GERROR(FALSE)419 } CATCH_TO_GERROR(FALSE)
420 
421 /**
422  * dnf_move_recursive:
423  * @src_dir: A source directory path
424  * @dst_dir: A destination directory path
425  * @error: A #GError, or %NULL
426  *
427  * Moves a file or a directory and its contents. Native move is preferred,
428  * if not supported copy and delete fallback is used.
429  *
430  * Returns: %TRUE on successful move, %FALSE otherwise
431  **/
432 gboolean
433 dnf_move_recursive(const char * srcDir, const char * dstDir, GError ** error) try
434 {
435     if (rename(srcDir, dstDir) == -1) {
436         if (!dnf_copy_recursive(srcDir, dstDir, error))
437             return FALSE;
438         return dnf_remove_recursive_v2(srcDir, error);
439     }
440     return TRUE;
441 } CATCH_TO_GERROR(FALSE)
442 
443 char *
444 this_username(void)
445 {
446     const struct passwd *pw = getpwuid(getuid());
447     return g_strdup(pw->pw_name);
448 }
449 
450 char *
read_whole_file(const char * path)451 read_whole_file(const char *path)
452 {
453   char *contents = NULL;
454   if (!g_file_get_contents (path, &contents, NULL, NULL))
455     return NULL;
456   return contents;
457 }
458 
459 static char *
pool_tmpdup(Pool * pool,const char * s)460 pool_tmpdup(Pool *pool, const char *s)
461 {
462     char *dup = pool_alloctmpspace(pool, strlen(s) + 1);
463     return strcpy(dup, s);
464 }
465 
466 static Id
running_kernel_check_path(DnfSack * sack,const char * fn)467 running_kernel_check_path(DnfSack *sack, const char *fn)
468 {
469     if (access(fn, F_OK))
470         g_debug("running_kernel_check_path(): no matching file: %s.", fn);
471 
472     HyQuery q = hy_query_create_flags(sack, HY_IGNORE_EXCLUDES);
473     dnf_sack_make_provides_ready(sack);
474     q->installed();
475     hy_query_filter(q, HY_PKG_FILE, HY_EQ, fn);
476     DnfPackageSet *pset = hy_query_run_set(q);
477 
478     Id id = -1;
479     id = pset->next(id);
480 
481     delete pset;
482     hy_query_free(q);
483 
484     return id;
485 }
486 
487 Id
running_kernel(DnfSack * sack)488 running_kernel(DnfSack *sack)
489 {
490     Pool *pool = dnf_sack_get_pool(sack);
491     struct utsname un;
492 
493     if (uname(&un) < 0) {
494         g_debug("uname(): %s", g_strerror(errno));
495         return -1;
496     }
497 
498     char *fn = pool_tmpjoin(pool, "/boot/vmlinuz-", un.release, NULL);
499     Id kernel_id = running_kernel_check_path(sack, fn);
500 
501     if (kernel_id < 0) {
502         fn = pool_tmpjoin(pool, "/lib/modules/", un.release, NULL);
503         kernel_id = running_kernel_check_path(sack, fn);
504     }
505 
506     if (kernel_id >= 0)
507         g_debug("running_kernel(): %s.", id2nevra(pool, kernel_id));
508     else
509         g_debug("running_kernel(): running kernel not matched to a package.");
510     return kernel_id;
511 }
512 
513 Repo *
repo_by_name(DnfSack * sack,const char * name)514 repo_by_name(DnfSack *sack, const char *name)
515 {
516     Pool *pool = dnf_sack_get_pool(sack);
517     Repo *repo;
518     int repoid;
519 
520     FOR_REPOS(repoid, repo) {
521         if (!strcmp(repo->name, name))
522             return repo;
523     }
524     return NULL;
525 }
526 
527 HyRepo
hrepo_by_name(DnfSack * sack,const char * name)528 hrepo_by_name(DnfSack *sack, const char *name)
529 {
530     Repo *repo = repo_by_name(sack, name);
531 
532     if (repo)
533         return static_cast<HyRepo>(repo->appdata);
534     return NULL;
535 }
536 
537 Id
str2archid(Pool * pool,const char * arch)538 str2archid(Pool *pool, const char *arch)
539 {
540     // originally from libsolv/examples/solv.c:str2archid()
541     Id id;
542     if (!*arch)
543         return 0;
544     id = pool_str2id(pool, arch, 0);
545     if (id == ARCH_SRC || id == ARCH_NOSRC || id == ARCH_NOARCH)
546         return id;
547     if (pool->id2arch && (id > pool->lastarch || !pool->id2arch[id]))
548         return 0;
549     return id;
550 }
551 
552 /**
553  * Return id of a package that can be upgraded with pkg.
554  *
555  * The returned package Id fulfills the following criteria:
556  * :: it is installed
557  * :: has the same name as pkg
558  * :: arch of the installed pkg is upgradable to the new pkg. In RPM world that
559  *    roughly means: if both pacakges are colored (contains ELF binaries and was
560  *    built with internal dependency generator), they are not upgradable to each
561  *    other (i.e. i386 package can not be upgraded to x86_64, neither the other
562  *    way round). If one of them is noarch and the other one colored then the
563  *    pkg is upgradable (i.e. one can upgrade .noarch to .x86_64 and then again
564  *    to a new version that is .noarch)
565  * :: is of lower version than pkg.
566  * :: if there are multiple packages of that name return the highest version
567  *    (implying we won't claim we can upgrade an old package with an already
568  *    installed version, e.g kernel).
569  *
570  * Or 0 if none such package is installed.
571  */
572 Id
what_upgrades(Pool * pool,Id pkg)573 what_upgrades(Pool *pool, Id pkg)
574 {
575     Id l = 0, l_evr = 0;
576     Id p, pp;
577     Solvable *updated, *s = pool_id2solvable(pool, pkg);
578 
579     assert(pool->installed);
580     assert(pool->whatprovides);
581     FOR_PROVIDES(p, pp, s->name) {
582         updated = pool_id2solvable(pool, p);
583         if (updated->repo != pool->installed ||
584             updated->name != s->name)
585             continue;
586         if (updated->arch != s->arch &&
587             updated->arch != ARCH_NOARCH &&
588             s->arch != ARCH_NOARCH)
589             continue;
590         if (pool_evrcmp(pool, updated->evr, s->evr, EVRCMP_COMPARE) >= 0)
591             // >= version installed, this pkg can not be used for upgrade
592             return 0;
593         if (l == 0 ||
594             pool_evrcmp(pool, updated->evr, l_evr, EVRCMP_COMPARE) > 0) {
595             l = p;
596             l_evr = updated->evr;
597         }
598     }
599     return l;
600 }
601 
602 /**
603  * Return id of a package that can be upgraded with pkg.
604  *
605  * The returned package Id fulfills the following criteria:
606  * :: it is installed
607  * :: has the same name and arch as pkg
608  * :: is of higher version than pkg.
609  * :: if there are multiple such packages return the lowest version (so we won't
610  *    claim we can downgrade a package when a lower version is already
611  *    installed)
612  *
613  * Or 0 if none such package is installed.
614  */
615 Id
what_downgrades(Pool * pool,Id pkg)616 what_downgrades(Pool *pool, Id pkg)
617 {
618     Id l = 0, l_evr = 0;
619     Id p, pp;
620     Solvable *updated, *s = pool_id2solvable(pool, pkg);
621 
622     assert(pool->installed);
623     assert(pool->whatprovides);
624     FOR_PROVIDES(p, pp, s->name) {
625         updated = pool_id2solvable(pool, p);
626         if (updated->repo != pool->installed ||
627             updated->name != s->name ||
628             updated->arch != s->arch)
629             continue;
630         if (pool_evrcmp(pool, updated->evr, s->evr, EVRCMP_COMPARE) <= 0)
631             // <= version installed, this pkg can not be used for downgrade
632             return 0;
633         if (l == 0 ||
634             pool_evrcmp(pool, updated->evr, l_evr, EVRCMP_COMPARE) < 0) {
635             l = p;
636             l_evr = updated->evr;
637         }
638     }
639     return l;
640 }
641 
642 unsigned long
pool_get_epoch(Pool * pool,const char * evr)643 pool_get_epoch(Pool *pool, const char *evr)
644 {
645     char *e, *v, *r, *endptr;
646     unsigned long epoch = 0;
647 
648     pool_split_evr(pool, evr, &e, &v, &r);
649     if (e) {
650         long int converted = strtol(e, &endptr, 10);
651         assert(converted > 0);
652         assert(*endptr == '\0');
653         epoch = converted;
654     }
655 
656     return epoch;
657 }
658 
659 /**
660  * Split evr into its components.
661  *
662  * Believes blindly in 'evr' being well formed. This could be implemented
663  * without 'pool' of course but either the caller would have to provide buffers
664  * to store the split pieces, or this would call strdup (which is more expensive
665  * than the pool temp space).
666  */
667 void
pool_split_evr(Pool * pool,const char * evr_c,char ** epoch,char ** version,char ** release)668 pool_split_evr(Pool *pool, const char *evr_c, char **epoch, char **version,
669                    char **release)
670 {
671     char *evr = pool_tmpdup(pool, evr_c);
672     char *e, *v, *r;
673 
674     for (e = evr + 1; *e != ':' && *e != '-' && *e != '\0'; ++e)
675         ;
676 
677     if (*e == '-') {
678         *e = '\0';
679         v = evr;
680         r = e + 1;
681         e = NULL;
682     } else if (*e == '\0') {
683         v = evr;
684         e = NULL;
685         r = NULL;
686     } else { /* *e == ':' */
687         *e = '\0';
688         v = e + 1;
689         e = evr;
690         for (r = v + 1; *r != '-'; ++r)
691             assert(*r);
692         *r = '\0';
693         r++;
694     }
695     *epoch = e;
696     *version = v;
697     *release = r;
698 }
699 
700 const char *
id2nevra(Pool * pool,Id id)701 id2nevra(Pool *pool, Id id)
702 {
703     Solvable *s = pool_id2solvable(pool, id);
704     return pool_solvable2str(pool, s);
705 }
706 
707 
708 GPtrArray *
packageSet2GPtrArray(libdnf::PackageSet * pset)709 packageSet2GPtrArray(libdnf::PackageSet * pset)
710 {
711     if (!pset) {
712         return NULL;
713     }
714     GPtrArray *plist = hy_packagelist_create();
715 
716     DnfSack * sack = pset->getSack();
717 
718     Id id = -1;
719     while ((id = pset->next(id)) != -1) {
720         g_ptr_array_add(plist, dnf_package_new(sack, id));
721     }
722     return plist;
723 }
724