2 * Copyright (C) 2008 Kristian Høgsberg <krh@redhat.com>
3 * Copyright (C) 2008 Red Hat, Inc
4 * Copyright (C) 2009, 2010 J. Ali Harlow <ali@juiblex.co.uk>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program 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
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
31 #include <sys/types.h>
44 #include "razor-internal.h"
62 struct razor_set_section_index {
68 #define MAIN(type, field) \
69 { type, offsetof(struct razor_set, field), RAZOR_SECTION_MAIN }
70 #define FILES(type, field) \
71 { type, offsetof(struct razor_set, field), RAZOR_SECTION_FILES }
72 #define DETAILS(type, field) \
73 { type, offsetof(struct razor_set, field), RAZOR_SECTION_DETAILS }
75 struct razor_set_section_index razor_sections[] = {
76 MAIN(RAZOR_STRING_POOL, string_pool),
77 MAIN(RAZOR_PACKAGES, packages),
78 MAIN(RAZOR_PROPERTIES, properties),
79 MAIN(RAZOR_PACKAGE_POOL, package_pool),
80 MAIN(RAZOR_PROPERTY_POOL, property_pool),
81 MAIN(RAZOR_PREFIX_POOL, prefix_pool),
82 FILES(RAZOR_FILES, files),
83 FILES(RAZOR_FILE_POOL, file_pool),
84 FILES(RAZOR_FILE_STRING_POOL, file_string_pool),
85 DETAILS(RAZOR_DETAILS_STRING_POOL, details_string_pool)
88 RAZOR_EXPORT struct razor_set *
89 razor_set_create_without_root(void)
91 struct razor_set *set;
94 set = zalloc(sizeof *set);
96 empty = array_add(&set->string_pool, 1);
104 RAZOR_EXPORT struct razor_set *
105 razor_set_create(void)
107 struct razor_set *set;
108 struct razor_entry *e;
110 set = razor_set_create_without_root();
112 e = array_add(&set->files, sizeof *e);
114 e->flags = RAZOR_ENTRY_LAST;
116 list_set_empty(&e->packages);
121 struct razor_mapped_file {
122 struct razor_set_header *header;
124 struct razor_mapped_file *next;
128 razor_set_bind_sections(struct razor_set *set, const char *filename)
130 struct razor_set_section *s, *sections;
131 struct razor_mapped_file *file;
136 file = zalloc(sizeof *file);
140 file->header = razor_file_get_contents(filename, &file->size);
146 if (file->size < sizeof *file->header ||
147 file->header->magic != RAZOR_MAGIC ||
148 file->header->version != RAZOR_VERSION) {
149 razor_file_free_contents(file->header, file->size);
154 if (set->mapped_files == NULL) {
155 for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
156 array = (void *) set + razor_sections[i].offset;
157 array_release(array);
161 file->next = set->mapped_files;
162 set->mapped_files = file;
164 sections = (void *) file->header + sizeof *file->header;
165 pool = (void *) sections +
166 file->header->num_sections * sizeof *sections;
168 for (i = 0; i < file->header->num_sections; i++) {
170 for (j = 0; j < ARRAY_SIZE(razor_sections); j++)
171 if (!strcmp(razor_sections[j].name, &pool[s->name]))
173 if (j == ARRAY_SIZE(razor_sections))
175 array = (void *) set + razor_sections[j].offset;
176 array->data = (void *) file->header + s->offset;
177 array->size = s->size;
178 array->alloc = s->size;
184 RAZOR_EXPORT struct razor_set *
185 razor_set_open(const char *filename)
187 struct razor_set *set;
189 set = zalloc(sizeof *set);
191 if (razor_set_bind_sections(set, filename)){
199 razor_set_aquire_lock(struct razor_set *set, const char *path, int exclusive)
205 fd = open(path, O_CREAT | O_RDWR | O_TRUNC | O_BINARY, 0666);
213 DWORD flags = LOCKFILE_FAIL_IMMEDIATELY;
214 OVERLAPPED lock = {0};
217 flags |= LOCKFILE_EXCLUSIVE_LOCK;
218 if (fd >= 0 && !LockFileEx(_get_osfhandle(fd), flags, 0, 1, 0, &lock)) {
222 if (set->lock_fd >= 0)
223 (void)UnlockFile(_get_osfhandle(set->lock_fd), 0, 0, 1, 0);
225 struct flock lock = {0};
227 lock.l_type = exclusive ? F_WRLCK : F_RDLCK;
228 lock.l_whence = SEEK_SET;
231 if (fd >= 0 && fcntl(fd, F_SETLK, &lock) < 0) {
235 if (set->lock_fd >= 0) {
236 lock.l_type = F_UNLCK;
237 (void)fcntl(set->lock_fd, F_SETLK, &lock);
241 if (set->lock_fd >= 0)
249 razor_set_destroy(struct razor_set *set)
251 struct razor_mapped_file *file, *next;
255 assert (set != NULL);
257 if (set->mapped_files == NULL) {
258 for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
259 array = (void *) set + razor_sections[i].offset;
260 array_release(array);
263 for (file = set->mapped_files; file != NULL; file = next) {
265 razor_file_free_contents(file->header, file->size);
270 razor_set_aquire_lock(set, NULL, 0);
275 razor_set_write_to_fd(struct razor_set *set, int fd, uint32_t section_mask)
277 struct razor_set_header header;
278 struct razor_set_section sections[ARRAY_SIZE(razor_sections)];
279 struct hashtable table;
280 struct array pool, *arrays[ARRAY_SIZE(razor_sections)];
283 static const char padding[4];
286 hashtable_init(&table, &pool);
289 for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
290 if ((razor_sections[i].flags & section_mask) == 0)
293 arrays[j] = (void *) set + razor_sections[i].offset;
295 hashtable_tokenize(&table, razor_sections[i].name);
300 header.magic = RAZOR_MAGIC;
301 header.version = RAZOR_VERSION;
302 header.num_sections = count;
303 offset = sizeof header + count * sizeof *sections + ALIGN(pool.size, 4);
305 for (i = 0; i < count; i++) {
306 sections[i].offset = offset;
307 sections[i].size = arrays[i]->size;
308 offset += ALIGN(arrays[i]->size, 4);
311 razor_write(fd, &header, sizeof header);
312 razor_write(fd, sections, count * sizeof *sections);
313 razor_write(fd, pool.data, pool.size);
314 razor_write(fd, padding, PADDING(pool.size, 4));
316 for (i = 0; i < count; i++) {
317 razor_write(fd, arrays[i]->data, arrays[i]->size);
318 razor_write(fd, padding, PADDING(arrays[i]->size, 4));
321 array_release(&pool);
322 hashtable_release(&table);
328 razor_set_write(struct razor_set *set, const char *filename, uint32_t sections)
332 fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY, 0666);
336 status = razor_set_write_to_fd(set, fd, sections);
346 razor_build_evr(char *evr_buf, int size, const char *epoch,
347 const char *version, const char *release)
351 if (!version || !*version) {
356 if (epoch && *epoch && strcmp(epoch, "0") != 0) {
357 len = snprintf(evr_buf, size, "%s:", epoch);
361 len = snprintf(evr_buf, size, "%s", version);
364 if (release && *release)
365 snprintf(evr_buf, size, "-%s", release);
369 razor_versioncmp(const char *s1, const char *s2)
378 n1 = strtol(s1, (char **) &p1, 10);
379 n2 = strtol(s2, (char **) &p2, 10);
381 /* Epoch; if one but not the other has an epoch set, default
382 * the epoch-less version to 0. */
383 res = (*p1 == ':') - (*p2 == ':');
388 } else if (res > 0) {
401 if (isdigit(*p1) && isdigit(*p2))
402 return razor_versioncmp(p1, p2);
409 razor_package_get_details_string(struct razor_set *set,
410 struct razor_package *package,
411 enum razor_detail_type type)
416 case RAZOR_DETAIL_NAME:
417 pool = set->string_pool.data;
418 return &pool[package->name];
420 case RAZOR_DETAIL_VERSION:
421 pool = set->string_pool.data;
422 return &pool[package->version];
424 case RAZOR_DETAIL_ARCH:
425 pool = set->string_pool.data;
426 return &pool[package->arch];
428 case RAZOR_DETAIL_SUMMARY:
429 if (!set->details_string_pool.size)
431 pool = set->details_string_pool.data;
432 return &pool[package->summary];
434 case RAZOR_DETAIL_DESCRIPTION:
435 if (!set->details_string_pool.size)
437 pool = set->details_string_pool.data;
438 return &pool[package->description];
440 case RAZOR_DETAIL_URL:
441 if (!set->details_string_pool.size)
443 pool = set->details_string_pool.data;
444 return &pool[package->url];
446 case RAZOR_DETAIL_LICENSE:
447 if (!set->details_string_pool.size)
449 pool = set->details_string_pool.data;
450 return &pool[package->license];
452 case RAZOR_DETAIL_PREUNPROG:
453 pool = set->string_pool.data;
454 return &pool[package->preun.program];
456 case RAZOR_DETAIL_PREUN:
457 pool = set->string_pool.data;
458 return &pool[package->preun.body];
460 case RAZOR_DETAIL_POSTUNPROG:
461 pool = set->string_pool.data;
462 return &pool[package->postun.program];
464 case RAZOR_DETAIL_POSTUN:
465 pool = set->string_pool.data;
466 return &pool[package->postun.body];
469 fprintf(stderr, "type %u not found\n", type);
474 static const char *const *
475 razor_package_get_details_array(struct razor_set *set,
476 struct razor_package *package,
477 enum razor_detail_type type)
480 case RAZOR_DETAIL_PREFIXES:
481 /* We don't track prefixes in packages. Install prefixes
482 * are tracked, but we don't provide an API to get them.
487 fprintf(stderr, "type %u not found\n", type);
493 * razor_package_get_details_varg:
495 * @package: a %razor_package
496 * @args: a va_list of arguments to set
499 razor_package_get_details_varg(struct razor_set *set,
500 struct razor_package *package,
504 enum razor_detail_type type;
506 const char *const **array;
508 for (i = 0;; i += 2) {
509 type = va_arg(args, enum razor_detail_type);
510 if (type == RAZOR_DETAIL_LAST)
512 if (type == RAZOR_DETAIL_PREFIXES) {
513 array = va_arg(args, const char *const **);
514 *array = razor_package_get_details_array(set, package,
517 string = va_arg(args, const char **);
518 *string = razor_package_get_details_string(set, package,
526 * razor_package_get_details:
528 * @package: a %razor_package
530 * Gets details about a package using a varg interface
531 * The vararg must be terminated with %RAZOR_DETAIL_LAST.
533 * Example: razor_package_get_details (set, package,
534 * RAZOR_DETAIL_URL, &url,
535 * RAZOR_DETAIL_LAST);
538 razor_package_get_details(struct razor_set *set, struct razor_package *package, ...)
542 assert (set != NULL);
543 assert (package != NULL);
545 va_start(args, NULL);
546 razor_package_get_details_varg (set, package, args);
551 * razor_package_remove:
552 * @prev: The %razor_set before the current transaction
553 * @next: The %razor_set after the current transaction is applied
554 * @package: a %razor_package
555 * @root: the root into which the package is currently installed
556 * @install_count: the value to pass to uninstall scripts
558 * Removes an installed package.
561 razor_package_remove(struct razor_set *prev, struct razor_set *next,
562 struct razor_package *package, const char *root,
565 struct razor_file_iterator *fi;
566 struct razor_package_iterator *pi;
567 struct razor_package *p;
568 char buffer[PATH_MAX];
569 const char *name, *program, *script;
570 int retval = 0, i, count;
571 struct environment env;
575 environment_init(&env);
576 link = list_first(&package->install_prefixes, &prev->prefix_pool);
577 for (i = 0; link; i++) {
578 prefix = (const char *)prev->string_pool.data + link->data;
579 sprintf(buffer, "RPM_INSTALL_PREFIX%d", i);
580 environment_add_variable(&env, buffer, prefix);
581 link = list_next(link);
583 environment_set(&env);
585 razor_package_get_details(prev, package,
586 RAZOR_DETAIL_PREUNPROG, &program,
587 RAZOR_DETAIL_PREUN, &script,
590 retval = razor_run_script(root, RAZOR_PROPERTY_PREUN, program, script,
593 fi = razor_file_iterator_create(prev, package, 1);
595 while (razor_file_iterator_next(fi, &name)) {
596 pi = razor_package_iterator_create_for_file(next, name);
598 while (razor_package_iterator_next(pi, &p, RAZOR_DETAIL_LAST))
600 razor_package_iterator_destroy(pi);
602 snprintf(buffer, sizeof buffer, "%s%s", root, name);
603 if (razor_remove(buffer) && errno != ENOENT) {
610 razor_file_iterator_destroy(fi);
612 razor_package_get_details(prev, package,
613 RAZOR_DETAIL_POSTUNPROG, &program,
614 RAZOR_DETAIL_POSTUN, &script,
617 if (razor_run_script(root, RAZOR_PROPERTY_POSTUN, program, script,
621 environment_unset(&env);
622 environment_release(&env);
627 RAZOR_EXPORT const char *
628 razor_property_relation_to_string(struct razor_property *p)
632 switch (p->flags & RAZOR_PROPERTY_RELATION_MASK) {
633 case RAZOR_PROPERTY_LESS:
636 case RAZOR_PROPERTY_LESS | RAZOR_PROPERTY_EQUAL:
639 case RAZOR_PROPERTY_EQUAL:
642 case RAZOR_PROPERTY_GREATER | RAZOR_PROPERTY_EQUAL:
645 case RAZOR_PROPERTY_GREATER:
653 RAZOR_EXPORT const char *
654 razor_property_type_to_string(struct razor_property *p)
658 switch (p->flags & RAZOR_PROPERTY_TYPE_MASK) {
659 case RAZOR_PROPERTY_REQUIRES:
661 case RAZOR_PROPERTY_PROVIDES:
663 case RAZOR_PROPERTY_CONFLICTS:
665 case RAZOR_PROPERTY_OBSOLETES:
672 RAZOR_EXPORT struct razor_entry *
673 razor_set_find_entry(struct razor_set *set,
674 struct razor_entry *dir, const char *pattern)
676 struct razor_entry *e, *subdir;
677 const char *n, *pool = set->file_string_pool.data;
680 assert (set != NULL);
681 assert (pattern != NULL);
689 if (strcmp(pattern, n) == 0)
692 if (e->start != 0 && strncmp(pattern, n, len) == 0 &&
693 pattern[len] == '/') {
694 subdir = (struct razor_entry *) set->files.data +
696 return razor_set_find_entry(set, subdir,
699 } while (!((e++)->flags & RAZOR_ENTRY_LAST));
705 list_dir(struct razor_set *set, struct razor_entry *dir,
706 char *prefix, const char *pattern)
708 struct razor_entry *e, *subdir;
709 const char *n, *pool = set->file_string_pool.data;
714 if (pattern && pattern[0] && fnmatch(pattern, n, 0) != 0)
716 printf("%s/%s\n", prefix, n);
718 char *sub = prefix + strlen (prefix);
721 subdir = (struct razor_entry *) set->files.data +
723 list_dir(set, subdir, prefix, pattern);
726 } while (!((e++)->flags & RAZOR_ENTRY_LAST));
730 razor_set_list_files(struct razor_set *set, const char *pattern)
732 struct razor_entry *root, *e;
733 char buffer[512], *p, *base;
735 assert (set != NULL);
737 root = (struct razor_entry *) set->files.data;
739 if (pattern == NULL) {
740 p = set->file_string_pool.data;
744 strcpy(buffer, p + e->name);
745 list_dir(set, root + e->start, buffer, NULL);
747 } while (!((e++)->flags & RAZOR_ENTRY_LAST));
751 strcpy(buffer, pattern);
752 e = razor_set_find_entry(set, root, buffer);
756 p = strrchr(buffer, '/');
764 e = razor_set_find_entry(set, root, buffer);
766 list_dir(set, root + e->start, buffer, base);
770 razor_set_list_package_files(struct razor_set *set,
771 struct razor_package *package)
773 struct razor_file_iterator *fi;
776 assert (set != NULL);
777 assert (package != NULL);
779 fi = razor_file_iterator_create(set, package, 0);
781 while (razor_file_iterator_next(fi, &name))
782 printf("%s\n", name);
784 razor_file_iterator_destroy(fi);
788 * Package data can potentially come from two places. The so-called
789 * metadata (eg., from comps.xml) and from an RPM file. We consider
790 * a package which has additional data from an RPM file as "fixed".
791 * If a package needs fixing, then razor_transaction_fixup_package()
792 * will do so. When considering what packages to add and to remove
793 * we need to take this into account since we always want to add
794 * unfixed packages (otherwise we have a potential conflict between
795 * the existing package data and that present in the RPM).
798 razor_package_is_fixed(struct razor_set *set, struct razor_package *p)
800 const char *preunprog, *preun, *postunprog, *postun;
804 razor_package_get_details(set, p,
805 RAZOR_DETAIL_PREUNPROG, &preunprog,
806 RAZOR_DETAIL_PREUN, &preun,
807 RAZOR_DETAIL_POSTUNPROG, &postunprog,
808 RAZOR_DETAIL_POSTUN, &postun,
810 return *preunprog || *preun || *postunprog || *postun;
814 razor_set_diff(struct razor_set *set, struct razor_set *upstream,
815 razor_diff_callback_t callback, void *data)
817 struct razor_package_iterator *pi1, *pi2;
818 struct razor_package *p1, *p2;
819 const char *name1, *name2, *version1, *version2, *arch1, *arch2;
820 int res, is_fixed1, is_fixed2;
822 assert (set != NULL);
823 assert (upstream != NULL);
825 pi1 = razor_package_iterator_create(set);
826 pi2 = razor_package_iterator_create(upstream);
828 razor_package_iterator_next(pi1, &p1,
829 RAZOR_DETAIL_NAME, &name1,
830 RAZOR_DETAIL_VERSION, &version1,
831 RAZOR_DETAIL_ARCH, &arch1,
833 is_fixed1 = razor_package_is_fixed(set, p1);
834 razor_package_iterator_next(pi2, &p2,
835 RAZOR_DETAIL_NAME, &name2,
836 RAZOR_DETAIL_VERSION, &version2,
837 RAZOR_DETAIL_ARCH, &arch2,
839 is_fixed2 = razor_package_is_fixed(upstream, p2);
843 res = strcmp(name1, name2);
845 res = razor_versioncmp(version1, version2);
847 res = is_fixed1 - is_fixed2;
852 if (p2 == NULL || res < 0)
853 callback(RAZOR_DIFF_ACTION_REMOVE,
854 p1, name1, version1, arch1, data);
855 else if (p1 == NULL || res > 0)
856 callback(RAZOR_DIFF_ACTION_ADD,
857 p2, name2, version2, arch2, data);
859 if (p1 != NULL && res <= 0) {
860 razor_package_iterator_next(pi1, &p1,
861 RAZOR_DETAIL_NAME, &name1,
862 RAZOR_DETAIL_VERSION, &version1,
863 RAZOR_DETAIL_ARCH, &arch1,
865 is_fixed1 = razor_package_is_fixed(set, p1);
867 if (p2 != NULL && res >= 0) {
868 razor_package_iterator_next(pi2, &p2,
869 RAZOR_DETAIL_NAME, &name2,
870 RAZOR_DETAIL_VERSION, &version2,
871 RAZOR_DETAIL_ARCH, &arch2,
873 is_fixed2 = razor_package_is_fixed(upstream, p2);
877 razor_package_iterator_destroy(pi1);
878 razor_package_iterator_destroy(pi2);
881 struct install_action {
882 enum razor_install_action action;
883 struct razor_package *package;
886 struct razor_install_iterator {
887 struct razor_set *set;
888 struct razor_set *next;
889 struct array actions;
894 add_action(enum razor_diff_action action,
895 struct razor_package *package,
901 struct razor_install_iterator *ii = data;
902 struct install_action *a;
904 a = array_add(&ii->actions, sizeof *a);
905 a->package = package;
908 case RAZOR_DIFF_ACTION_ADD:
909 a->action = RAZOR_INSTALL_ACTION_ADD;
911 case RAZOR_DIFF_ACTION_REMOVE:
912 a->action = RAZOR_INSTALL_ACTION_REMOVE;
917 RAZOR_EXPORT struct razor_install_iterator *
918 razor_set_create_install_iterator(struct razor_set *set,
919 struct razor_set *next)
921 struct razor_install_iterator *ii;
922 struct razor_property *prop;
923 /* A graph of the actions to be perfomed where
924 * A->B means action A should follow action B.
926 struct graph follows;
927 struct install_action *actions, *ai, *aj;
928 int i, j, count, vertex_added;
930 struct razor_set *rs;
932 assert (set != NULL);
933 assert (next != NULL);
935 ii = zalloc(sizeof *ii);
939 razor_set_diff(set, next, add_action, ii);
941 actions = ii->actions.data;
942 count = ii->actions.size / sizeof (struct install_action);
944 graph_init(&follows);
946 for(i = 0; i < count; i++) {
948 rs = ai->action == RAZOR_INSTALL_ACTION_ADD ? next : set;
950 link = list_first(&ai->package->properties, &rs->property_pool);
951 for(; link; link = list_next(link)) {
952 prop = rs->properties.data;
954 switch(prop->flags & RAZOR_PROPERTY_TYPE_MASK) {
955 case RAZOR_PROPERTY_REQUIRES:
956 case RAZOR_PROPERTY_CONFLICTS:
957 for(j = 0; j < count; j++) {
961 if (aj->package->name == prop->name) {
963 RAZOR_INSTALL_ACTION_ADD)
964 graph_add_edge(&follows,
967 graph_add_edge(&follows,
975 if (ai->action == RAZOR_INSTALL_ACTION_ADD) {
976 for(j = 0; j < count; j++) {
980 if (aj->package == ai->package &&
981 aj->action == RAZOR_INSTALL_ACTION_REMOVE) {
982 graph_add_edge(&follows, i, j);
988 graph_add_edge(&follows, i, i);
991 ii->order = graph_sort(&follows);
992 graph_release(&follows);
998 razor_install_iterator_next(struct razor_install_iterator *ii,
999 struct razor_package **package,
1000 enum razor_install_action *action,
1003 struct install_action *a;
1004 struct razor_package_iterator *pi;
1005 struct razor_package *pkg;
1006 const char *removing, *name;
1008 if (deque_empty(ii->order))
1011 a = (struct install_action *)ii->actions.data + deque_pop(ii->order);
1012 *package = a->package;
1013 *action = a->action;
1016 if (a->action == RAZOR_INSTALL_ACTION_REMOVE) {
1017 razor_package_get_details(ii->set, a->package,
1018 RAZOR_DETAIL_NAME, &removing,
1021 pi = razor_package_iterator_create(ii->next);
1022 while (razor_package_iterator_next(pi, &pkg,
1023 RAZOR_DETAIL_NAME, &name,
1024 RAZOR_DETAIL_LAST)) {
1025 if (!strcmp(name, removing))
1028 razor_package_iterator_destroy(pi);
1035 razor_install_iterator_destroy(struct razor_install_iterator *ii)
1037 array_release(&ii->actions);
1038 deque_free(ii->order);