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