2 * Copyright (c) 2018, SUSE LLC.
4 * This program is licensed under the BSD license, read LICENSE.BSD
5 * for further information
22 #include "repo_rpmdb.h"
26 #include "repo_repomdxml.h"
27 #include "repo_rpmmd.h"
28 #include "repo_updateinfoxml.h"
29 #include "repo_deltainfoxml.h"
32 #ifdef ENABLE_SUSEREPO
33 #include "repo_content.h"
34 #include "repo_susetags.h"
38 #include "repo_autopattern.h"
41 #include "repo_appdata.h"
43 #include "common_write.h"
44 #include "solv_xfopen.h"
55 int add_changelog = 0;
56 int filtered_filelist = 0;
59 #define REPO_PLAINDIR 1
61 #define REPO_RPMMD_REPODATA 3
62 #define REPO_SUSETAGS 4
65 autodetect_repotype(Pool *pool, const char *dir)
71 tmp = pool_tmpjoin(pool, dir, "/repomd.xml", 0);
72 if (stat(tmp, &stb) == 0)
74 tmp = pool_tmpjoin(pool, dir, "/repodata/repomd.xml", 0);
75 if (stat(tmp, &stb) == 0)
76 return REPO_RPMMD_REPODATA;
77 tmp = pool_tmpjoin(pool, dir, "/content", 0);
78 if ((fp = fopen(tmp, "r")) != 0)
80 char buf[512], *descrdir = 0;
81 while (fgets(buf, sizeof(buf), fp))
85 if (buf[l - 1] != '\n')
88 while ((c = getc(fp)) != EOF && c != '\n')
92 while (l && (buf[l - 1] == '\n' || buf[l - 1] == ' ' || buf[l - 1] == '\t'))
95 while (*bp == ' ' || *bp == '\t')
97 if (strncmp(bp, "DESCRDIR", 8) != 0 || (bp[8] != ' ' && bp[8] != '\t'))
100 while (*bp == ' ' || *bp == '\t')
108 tmp = pool_tmpjoin(pool, dir, "/", descrdir);
109 if (stat(tmp, &stb) == 0 && S_ISDIR(stb.st_mode))
110 return REPO_SUSETAGS;
113 tmp = pool_tmpjoin(pool, dir, "/suse/setup/descr", 0);
114 if (stat(tmp, &stb) == 0 && S_ISDIR(stb.st_mode))
115 return REPO_SUSETAGS;
116 return REPO_PLAINDIR;
123 read_plaindir_repo(Repo *repo, const char *dir)
125 Pool *pool = repo->pool;
139 /* run find command */
145 while ((pid = fork()) == (pid_t)-1)
164 if (dup2(fds[1], 1) == -1)
172 execl("/usr/bin/find", "/usr/bin/find", ".", "-name", ".", "-o", "-name", ".*", "-prune", "-o", "-name", "*.delta.rpm", "-o", "-name", "*.patch.rpm", "-o", "-name", "*.rpm", "-a", "-type", "f", "-print0", (char *)0);
174 execl("/usr/bin/find", "/usr/bin/find", ".", "-maxdepth", "1", "-name", ".", "-o", "-name", ".*", "-prune", "-o", "-name", "*.delta.rpm", "-o", "-name", "*.patch.rpm", "-o", "-name", "*.rpm", "-a", "-type", "f", "-print0", (char *)0);
175 perror("/usr/bin/find");
179 if ((fp = fdopen(fds[0], "r")) == 0)
184 data = repo_add_repodata(repo, 0);
186 while ((c = getc(fp)) != EOF)
190 size_t len = bp - buf;
191 buf = solv_realloc(buf, len + 4096);
199 rpm = solv_dupjoin(dir, "/", bp[0] == '.' && bp[1] == '/' ? bp + 2 : bp);
200 if ((p = repo_add_rpm(repo, rpm, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|REPO_NO_LOCATION|(filtered_filelist ? RPM_ADD_FILTERED_FILELIST : 0))) == 0)
202 fprintf(stderr, "%s: %s\n", rpm, pool_errstr(pool));
208 repodata_set_location(data, p, 0, 0, bp[0] == '.' && bp[1] == '/' ? bp + 2 : bp);
213 while (waitpid(pid, &wstatus, 0) == -1)
222 fprintf(stderr, "find: exit status %d\n", (wstatus >> 8) | (wstatus & 255) << 8);
227 repo_internalize(repo);
234 read_plaindir_repo(Repo *repo, const char *dir)
236 fprintf(stderr, "plaindir repo type is not supported\n");
242 #ifdef ENABLE_SUSEREPO
245 susetags_find(char **files, int nfiles, const char *what)
248 size_t l = strlen(what);
249 for (i = 0; i < nfiles; i++)
252 if (strncmp(fn, what, l) != 0)
258 if (strchr(fn + l + 1, '.') != 0)
260 if (solv_xfopen_iscompressed(fn) <= 0)
268 susetags_open(const char *dir, const char *filename, char **tmpp, int missingok)
276 *tmpp = solv_dupjoin(dir, "/", filename);
277 if ((fp = solv_xfopen(*tmpp, "r")) == 0)
284 *tmpp = solv_free(*tmpp);
291 susetags_extend(Repo *repo, const char *dir, char **files, int nfiles, char *what, Id defvendor, char *language, int missingok)
293 const char *filename;
297 filename = susetags_find(files, nfiles, what);
300 if ((fp = susetags_open(dir, filename, &tmp, missingok)) != 0)
302 if (repo_add_susetags(repo, fp, defvendor, language, REPO_EXTEND_SOLVABLES))
304 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(repo->pool));
313 susetags_extend_languages(Repo *repo, const char *dir, char **files, int nfiles, Id defvendor, int missingok)
316 for (i = 0; i < nfiles; i++)
320 if (strncmp(fn, "packages.", 9) != 0)
322 if (strlen(fn + 9) + 1 >= sizeof(lang))
324 strncpy(lang, fn + 9, sizeof(lang) - 1);
325 lang[sizeof(lang) - 1] = 0;
326 p = strrchr(lang, '.');
329 if (solv_xfopen_iscompressed(lang) <= 0)
333 if (strchr(lang, '.'))
335 if (!strcmp(lang, "en"))
336 continue; /* already did that one */
337 if (!strcmp(lang, "DU"))
338 continue; /* disk usage */
339 if (!strcmp(lang, "FL"))
340 continue; /* file list */
341 if (!strcmp(lang, "DL"))
342 continue; /* deltas */
343 susetags_extend(repo, dir, files, nfiles, fn, defvendor, lang, missingok);
348 susetags_dircmp(const void *ap, const void *bp, void *dp)
350 return strcmp(*(const char **)ap, *(const char **)bp);
354 read_susetags_repo(Repo *repo, const char *dir)
356 Pool *pool = repo->pool;
357 const char *filename;
362 const char *descrdir = 0;
368 /* read content file */
369 repo_add_repodata(repo, 0);
370 tmp = solv_dupjoin(dir, "/content", 0);
371 if ((fp = fopen(tmp, "r")) != 0)
373 if (repo_add_content(repo, fp, REPO_REUSE_REPODATA))
375 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
379 descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
380 defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
383 descrdir = "suse/setup/descr";
384 tmp = solv_free(tmp);
386 /* get content of descrdir directory */
387 ddir = solv_dupjoin(dir, "/", descrdir);
388 if ((dp = opendir(ddir)) == 0)
393 while ((de = readdir(dp)) != 0)
395 if (de->d_name[0] == 0 || de->d_name[0] == '.')
397 files = solv_extend(files, nfiles, 1, sizeof(char *), 63);
398 files[nfiles++] = solv_strdup(de->d_name);
402 solv_sort(files, nfiles, sizeof(char *), susetags_dircmp, 0);
405 filename = susetags_find(files, nfiles, "packages");
406 if (filename && (fp = susetags_open(ddir, filename, &tmp, 1)) != 0)
408 if (repo_add_susetags(repo, fp, defvendor, 0, SUSETAGS_RECORD_SHARES))
410 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
414 tmp = solv_free(tmp);
416 /* now extend the packages */
417 susetags_extend(repo, ddir, files, nfiles, "packages.DU", defvendor, 0, 1);
418 susetags_extend(repo, ddir, files, nfiles, "packages.en", defvendor, 0, 1);
419 susetags_extend_languages(repo, ddir, files, nfiles, defvendor, 1);
421 susetags_extend(repo, ddir, files, nfiles, "packages.FL", defvendor, 0, 1);
425 filename = susetags_find(files, nfiles, "packages.DL");
426 if (filename && (fp = susetags_open(ddir, filename, &tmp, 1)) != 0)
428 if (repo_add_susetags(repo, fp, defvendor, 0, 0))
430 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
434 tmp = solv_free(tmp);
437 /* add legacy patterns */
438 tmp = solv_dupjoin(ddir, "/patterns", 0);
439 if ((fp = fopen(tmp, "r")) != 0)
443 repo_add_repodata(repo, 0);
444 while (fgets(pbuf, sizeof(pbuf), fp))
448 if (strchr(pbuf, '/') != 0)
450 if ((p = strchr(pbuf, '\n')) != 0)
455 tmp = solv_dupjoin(ddir, "/", pbuf);
456 if ((pfp = solv_xfopen(tmp, "r")) != 0)
458 if (repo_add_susetags(repo, pfp, defvendor, 0, REPO_NO_INTERNALIZE|REPO_REUSE_REPODATA))
460 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
468 tmp = solv_free(tmp);
470 #ifdef ENABLE_APPDATA
472 filename = add_appdata ? susetags_find(files, nfiles, "appdata.xml") : 0;
473 if (filename && (fp = susetags_open(ddir, filename, &tmp, 1)) != 0)
475 if (repo_add_appdata(repo, fp, 0))
477 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
481 tmp = solv_free(tmp);
486 solv_free(files[--nfiles]);
489 repo_internalize(repo);
496 read_susetags_repo(Repo *repo, const char *dir)
498 fprintf(stderr, "susetags repo type is not supported\n");
507 # ifdef ENABLE_ZCHUNK_COMPRESSION
510 repomd_exists(const char *dir, const char *filename)
518 path = solv_dupjoin(dir, "/", filename);
519 r = stat(path, &stb) == 0;
527 repomd_find(Repo *repo, const char *dir, const char *what, int findzchunk)
529 Pool *pool = repo->pool;
531 const char *filename;
533 # ifdef ENABLE_ZCHUNK_COMPRESSION
536 char *what_zck = solv_dupjoin(what, "_zck", 0);
537 filename = repomd_find(repo, dir, what_zck, 0);
539 if (filename && repomd_exists(dir, filename))
544 dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
545 dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
546 if (dataiterator_step(&di))
548 dataiterator_setpos_parent(&di);
549 filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
551 dataiterator_free(&di);
552 if (filename && strncmp(filename, "repodata/", 9) == 0)
558 repomd_open(const char *dir, const char *filename, char **tmpp, int missingok)
566 *tmpp = solv_dupjoin(dir, "/", filename);
567 if ((fp = solv_xfopen(*tmpp, "r")) == 0)
574 *tmpp = solv_free(*tmpp);
581 repomd_extend(Repo *repo, const char *dir, const char *what, const char *language, int missingok)
583 const char *filename;
587 filename = repomd_find(repo, dir, what, 1);
590 fp = repomd_open(dir, filename, &tmp, missingok);
593 if (repo_add_rpmmd(repo, fp, language, REPO_EXTEND_SOLVABLES))
595 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(repo->pool));
604 repomd_extend_languages(Repo *repo, const char *dir, int missingok)
606 char **susedatas = 0;
607 int nsusedatas = 0, i;
609 dataiterator_init(&di, repo->pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, "susedata.", SEARCH_STRINGSTART);
610 dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
611 while (dataiterator_step(&di))
613 char *str = solv_strdup(di.kv.str);
614 size_t l = strlen(str);
615 if (l > 4 && !strcmp(str + l - 4, "_zck"))
617 for (i = 0; i < nsusedatas; i++)
618 if (!strcmp(susedatas[i], str))
623 continue; /* already have that entry */
625 susedatas = solv_extend(susedatas, nsusedatas, 1, sizeof(char *), 15);
626 susedatas[nsusedatas++] = str;
628 dataiterator_free(&di);
629 for (i = 0; i < nsusedatas; i++)
631 repomd_extend(repo, dir, susedatas[i], susedatas[i] + 9, missingok);
632 susedatas[i] = solv_free(susedatas[i]);
634 solv_free(susedatas);
638 add_rpmmd_file(Repo *repo, const char *dir, const char *filename, int missingok)
643 fp = repomd_open(dir, filename, &tmp, missingok);
646 if (repo_add_rpmmd(repo, fp, 0, 0))
648 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(repo->pool));
656 read_rpmmd_repo(Repo *repo, const char *dir)
658 Pool *pool = repo->pool;
661 const char *filename;
663 /* add repomd.xml and suseinfo.xml */
664 fp = repomd_open(dir, "repomd.xml", &tmp, 0);
665 if (repo_add_repomdxml(repo, fp, 0))
667 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
671 tmp = solv_free(tmp);
672 filename = repomd_find(repo, dir, "suseinfo", 0);
673 if (filename && (fp = repomd_open(dir, filename, &tmp, 0)) != 0)
675 if (repo_add_repomdxml(repo, fp, REPO_REUSE_REPODATA))
677 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
681 tmp = solv_free(tmp);
684 /* first all primary packages */
685 filename = repomd_find(repo, dir, "primary", 1);
688 add_rpmmd_file(repo, dir, filename, 0);
689 repomd_extend(repo, dir, "susedata", 0, 1);
690 repomd_extend_languages(repo, dir, 1);
692 repomd_extend(repo, dir, "filelists", 0, 1);
694 repomd_extend(repo, dir, "other", 0, 1);
697 /* some legacy stuff */
698 filename = repomd_find(repo, dir, "products", 0);
700 filename = repomd_find(repo, dir, "product", 0);
702 add_rpmmd_file(repo, dir, filename, 1);
703 filename = repomd_find(repo, dir, "patterns", 0);
704 add_rpmmd_file(repo, dir, filename, 1);
707 filename = repomd_find(repo, dir, "updateinfo", 1);
708 if (filename && (fp = repomd_open(dir, filename, &tmp, 0)) != 0)
710 if (repo_add_updateinfoxml(repo, fp, 0))
712 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
716 tmp = solv_free(tmp);
720 filename = repomd_find(repo, dir, "deltainfo", 1);
722 filename = repomd_find(repo, dir, "prestodelta", 1);
723 if (filename && (fp = repomd_open(dir, filename, &tmp, 1)) != 0)
725 if (repo_add_deltainfoxml(repo, fp, 0))
727 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
731 tmp = solv_free(tmp);
734 #ifdef ENABLE_APPDATA
736 filename = add_appdata ? repomd_find(repo, dir, "appdata", 1) : 0;
737 if (filename && (fp = repomd_open(dir, filename, &tmp, 1)) != 0)
739 if (repo_add_appdata(repo, fp, 0))
741 fprintf(stderr, "%s: %s\n", tmp, pool_errstr(pool));
745 tmp = solv_free(tmp);
749 repo_internalize(repo);
756 read_rpmmd_repo(Repo *repo, const char *dir)
758 fprintf(stderr, "rpmmd repo type is not supported\n");
767 fprintf(stderr, "\nUsage:\n"
768 "repo2solv [-R] [-X] [-A] [-o <out.solv>] <dir>\n"
769 " Convert a repository in <dir> to a solv file\n"
770 " -h : print help & exit\n"
771 " -o <out.solv>: write to this file instead of stdout\n"
772 " -F : add filelist\n"
773 " -R : also search subdirectories for rpms\n"
774 " -X : generate pattern/product pseudo packages\n"
775 " -A : add appdata packages\n"
781 main(int argc, char **argv)
789 Pool *pool = pool_create();
790 Repo *repo = repo_create(pool, "<repo>");
792 while ((c = getopt(argc, argv, "hAXRFCo:")) >= 0)
805 #ifdef ENABLE_APPDATA
810 repotype = REPO_PLAINDIR;
827 if (optind + 1 != argc)
835 if (!S_ISDIR(stb.st_mode))
837 fprintf(stderr, "%s: not a directory\n", dir);
840 dir = solv_strdup(dir);
842 repotype = autodetect_repotype(pool, dir);
847 res = read_rpmmd_repo(repo, dir);
849 case REPO_RPMMD_REPODATA:
850 dir = solv_dupappend(dir, "/repodata", 0);
851 res = read_rpmmd_repo(repo, dir);
854 res = read_susetags_repo(repo, dir);
857 res = read_plaindir_repo(repo, dir);
860 fprintf(stderr, "unknown repotype %d\n", repotype);
863 if (outfile && freopen(outfile, "w", stdout) == 0)
870 repo_add_autopattern(repo, 0);
872 tool_write(repo, stdout);