librazor/razor.c
author J. Ali Harlow <ali@juiblex.co.uk>
Fri Jun 08 18:02:33 2018 +0100 (2018-06-08)
changeset 500 f98d77376544
parent 479 4204db81cdbc
permissions -rw-r--r--
Release 0.7
     1 /*
     2  * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
     3  * Copyright (C) 2008  Red Hat, Inc
     4  * Copyright (C) 2009-2012, 2016, 2018  J. Ali Harlow <ali@juiblex.co.uk>
     5  *
     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.
    10  *
    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.
    15  *
    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.
    19  */
    20 
    21 #define _GNU_SOURCE
    22 
    23 #include "config.h"
    24 
    25 #include <stdlib.h>
    26 #include <stddef.h>
    27 #include <stdint.h>
    28 #include <stdio.h>
    29 #include <stdarg.h>
    30 #include <string.h>
    31 #include <fcntl.h>
    32 #include <unistd.h>
    33 #include <errno.h>
    34 #include <ctype.h>
    35 #include <fnmatch.h>
    36 #include <limits.h>
    37 #include <assert.h>
    38 #ifdef MSWIN_API
    39 #include <windows.h>
    40 #endif
    41 
    42 #include "razor.h"
    43 #include "types/types.h"
    44 #include "razor-internal.h"
    45 
    46 #ifndef O_BINARY
    47 #define O_BINARY	0
    48 #endif
    49 
    50 struct razor_set_section_index {
    51 	const char *name;
    52 	uint32_t offset;
    53 	uint32_t flags;
    54 };
    55 
    56 #define MAIN(type, field) \
    57 	{ type, offsetof(struct razor_set, field), RAZOR_SECTION_MAIN }
    58 #define FILES(type, field) \
    59 	{ type, offsetof(struct razor_set, field), RAZOR_SECTION_FILES }
    60 #define DETAILS(type, field) \
    61 	{ type, offsetof(struct razor_set, field), RAZOR_SECTION_DETAILS }
    62 
    63 struct razor_set_section_index razor_sections[] = {
    64 	MAIN(RAZOR_STRING_POOL, string_pool),
    65 	MAIN(RAZOR_PACKAGES, packages),
    66 	MAIN(RAZOR_PROPERTIES, properties),
    67 	MAIN(RAZOR_PACKAGE_POOL, package_pool),
    68 	MAIN(RAZOR_PROPERTY_POOL, property_pool),
    69 	MAIN(RAZOR_PREFIX_POOL, prefix_pool),
    70 	FILES(RAZOR_FILES, files),
    71 	FILES(RAZOR_FILE_POOL, file_pool),
    72 	FILES(RAZOR_FILE_STRING_POOL, file_string_pool),
    73 	DETAILS(RAZOR_DETAILS_STRING_POOL, details_string_pool)
    74 };
    75 
    76 RAZOR_EXPORT struct razor_set *
    77 razor_set_create_without_root(void)
    78 {
    79 	struct razor_set *set;
    80 	char *empty;
    81 
    82 	set = zalloc(sizeof *set);
    83 
    84 	if (set) {
    85 		empty = array_add(&set->string_pool, 1);
    86 		*empty = '\0';
    87 
    88 		set->lock = -1;
    89 
    90 		set->ref_count = 1;
    91 
    92 		set->header_version = RAZOR_HEADER_VERSION;
    93 
    94 		set->flags = RAZOR_SET_PRIVATE;
    95 	}
    96 
    97 	return set;
    98 }
    99 
   100 RAZOR_EXPORT struct razor_set *
   101 razor_set_create(void)
   102 {
   103 	struct razor_set *set;
   104 	struct razor_entry *e;
   105 
   106 	set = razor_set_create_without_root();
   107 
   108 	e = array_add(&set->files, sizeof *e);
   109 	e->name = 0;
   110 	e->flags = RAZOR_ENTRY_LAST;
   111 	e->start = 0;
   112 	list_set_empty(&e->packages);
   113 
   114 	return set;
   115 }
   116 
   117 RAZOR_EXPORT uint32_t
   118 razor_set_get_header_version(struct razor_set *set)
   119 {
   120 	return set->header_version;
   121 }
   122 
   123 RAZOR_EXPORT int
   124 razor_set_set_header_version(struct razor_set *set, uint32_t header_version)
   125 {
   126 	if (header_version<RAZOR_HEADER_VERSION_MIN ||
   127 	    header_version>RAZOR_HEADER_VERSION)
   128 		return -1;
   129 	else {
   130 		set->header_version = header_version;
   131 		return 0;
   132 	}
   133 }
   134 
   135 struct razor_mapped_file {
   136 	struct razor_set_header *header;
   137 	size_t size;
   138 	struct razor_mapped_file *next;
   139 };
   140 
   141 RAZOR_EXPORT int
   142 razor_set_bind_sections(struct razor_set *set, const char *uri,
   143 			enum razor_set_flags flags, struct razor_error **error)
   144 {
   145 	struct razor_set_section *s, *sections;
   146 	struct razor_mapped_file *file;
   147 	const char *pool, *reason;
   148 	struct array *array;
   149 	int i, j, code;
   150 
   151 	file = zalloc(sizeof *file);
   152 	if (file == NULL) {
   153 		razor_set_error(error, RAZOR_POSIX_ERROR, ENOMEM, NULL,
   154 				"Not enough memory");
   155 		return -1;
   156 	}
   157 
   158 	file->header = razor_uri_get_contents(uri, &file->size,
   159 					      flags & RAZOR_SET_PRIVATE,
   160 					      error);
   161 	if (!file->header) {
   162 		free(file);
   163 		return -1;
   164 	}
   165 
   166 	if (file->size < sizeof *file->header) {
   167 		code = RAZOR_GENERAL_ERROR_DATABASE_CORRUPTED;
   168 		reason = "Premature EOF";
   169 	} else if (file->header->magic != RAZOR_MAGIC) {
   170 		code = RAZOR_GENERAL_ERROR_DATABASE_CORRUPTED;
   171 		reason = "Bad magic number";
   172 	} else if (file->header->version < RAZOR_HEADER_VERSION_MIN ||
   173 		   file->header->version > RAZOR_HEADER_VERSION) {
   174 		code = RAZOR_GENERAL_ERROR_DATABASE_INCOMPATIBLE;
   175 		reason = "Incompatible file version";
   176 	} else
   177 		reason = NULL;
   178 
   179 	if (reason) {
   180 		razor_set_error(error, RAZOR_GENERAL_ERROR, code, uri, reason);
   181 		razor_uri_free_contents(file->header, file->size);
   182 		free(file);
   183 		return -1;
   184 	}
   185 
   186 	set->flags = flags & RAZOR_SET_PRIVATE;
   187 
   188 	set->header_version = file->header->version;
   189 
   190 	if (set->mapped_files == NULL) {
   191 		for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
   192 			array = (void *) set + razor_sections[i].offset;
   193 			array_release(array);
   194 		}
   195 	}
   196 
   197 	file->next = set->mapped_files;
   198 	set->mapped_files = file;
   199 
   200 	sections = (void *) file->header + sizeof *file->header;
   201 	pool = (void *) sections +
   202 		file->header->num_sections * sizeof *sections;
   203 
   204 	for (i = 0; i < file->header->num_sections; i++) {
   205 		s = sections + i;
   206 		for (j = 0; j < ARRAY_SIZE(razor_sections); j++)
   207 			if (!strcmp(razor_sections[j].name, &pool[s->name]))
   208 				break;
   209 		if (j == ARRAY_SIZE(razor_sections))
   210 			continue;
   211 		array = (void *) set + razor_sections[j].offset;
   212 		array->data = (void *) file->header + s->offset;
   213 		array->size = s->size;
   214 		array->alloc = s->size;
   215 	}
   216 
   217 	return 0;
   218 }
   219 
   220 RAZOR_EXPORT struct razor_set *
   221 razor_set_open(const char *uri, enum razor_set_flags flags,
   222 	       struct razor_error **error)
   223 {
   224 	struct razor_set *set;
   225 
   226 	set = zalloc(sizeof *set);
   227 	if (!set) {
   228 		razor_set_error(error, RAZOR_POSIX_ERROR, ENOMEM, NULL,
   229 				"Not enough memory");
   230 		return NULL;
   231 	}
   232 
   233 	set->lock = -1;
   234 	set->ref_count = 1;
   235 	if (razor_set_bind_sections(set, uri, flags, error)) {
   236 		free(set);
   237 		return NULL;
   238 	}
   239 	return set;
   240 }
   241 
   242 struct razor_uri_lock {
   243 	char *uri;
   244 	int fd;
   245 	int exclusive;
   246 };
   247 
   248 static struct array razor_uri_locks = { 0, };
   249 
   250 /*
   251  * The underlying locks on MS-Windows and POSIX are different. Posix locks
   252  * created with fcntl(F_SETLK) only affect other processes whereas MS-Windows
   253  * locks affect the locking process as well. Partly because the latter is
   254  * preferable and partly because it makes testing easier if platforms behave
   255  * in the same way, we implement platform independent locks which follow the
   256  * MS-Windows pattern.
   257  *
   258  * Note that razor_set_aquire_lock() currently assumes that this is a private
   259  * helper function. Should we ever want to make this a library-wide API then
   260  * we should probably add a 'void *owner' parameter and add a new error code
   261  * (RAZOR_GENERAL_ERROR_LOCALLY_LOCKED?) if a lock is present with a different
   262  * owner so that razor_set_aquire_lock() can generate a different error message.
   263  */
   264 
   265 static int
   266 razor_uri_lock(const char *uri, int flags, mode_t mode, int exclusive,
   267 	       struct razor_error **error)
   268 {
   269 	int fd, i;
   270 	struct razor_uri_lock *ulock;
   271 #ifdef MSWIN_API
   272 	DWORD lflags = LOCKFILE_FAIL_IMMEDIATELY, err;
   273 	OVERLAPPED lock = {0};
   274 #else
   275 	struct flock lock = {0};
   276 #endif
   277 
   278 	fd = razor_uri_open(uri, flags, mode, error);
   279 	if (fd < 0)
   280 		return -1;
   281 
   282 	for (i = ptr_array_len(&razor_uri_locks) - 1; i >= 0; i--) {
   283 		ulock = ptr_array_index(&razor_uri_locks, i);
   284 		if (ulock && !strcmp(ulock->uri, uri)) {
   285 			if (!ulock->exclusive && !exclusive)
   286 				break;
   287 			razor_set_error(error, RAZOR_GENERAL_ERROR,
   288 					RAZOR_GENERAL_ERROR_ALREADY_LOCKED,
   289 					uri,
   290 					"An existing lock is already held on this file");
   291 			close(fd);
   292 			return -1;
   293 		}
   294 	}
   295 
   296 #ifdef MSWIN_API
   297 	if (exclusive)
   298 		lflags |= LOCKFILE_EXCLUSIVE_LOCK;
   299 	if (!LockFileEx((HANDLE)_get_osfhandle(fd), lflags, 0, 1, 0, &lock)) {
   300 		err = GetLastError();
   301 		if (err == ERROR_IO_PENDING)
   302 			razor_set_error(error, RAZOR_GENERAL_ERROR,
   303 					RAZOR_GENERAL_ERROR_EXTERNALLY_LOCKED,
   304 					uri,
   305 					"An existing lock is already held on this file");
   306 		else
   307 			razor_set_error_mswin(error, uri, err);
   308 		close(fd);
   309 		return -1;
   310 	}
   311 #else
   312 	lock.l_type = exclusive ? F_WRLCK : F_RDLCK;
   313 	lock.l_whence = SEEK_SET;
   314 	lock.l_start = 0;
   315 	lock.l_len = 0;
   316 	if (fcntl(fd, F_SETLK, &lock) < 0) {
   317 		if (errno == EAGAIN || errno == EACCES)
   318 			razor_set_error(error, RAZOR_GENERAL_ERROR,
   319 					RAZOR_GENERAL_ERROR_EXTERNALLY_LOCKED,
   320 					uri,
   321 					"An existing lock is already held on this file");
   322 		else
   323 			razor_set_error_posix(error, uri);
   324 		close(fd);
   325 		return -1;
   326 	}
   327 #endif
   328 
   329 	ulock = zalloc(sizeof *ulock);
   330 	ulock->uri = strdup(uri);
   331 	ulock->fd = fd;
   332 	ulock->exclusive = exclusive;
   333 	return ptr_array_add(&razor_uri_locks, ulock);
   334 }
   335 
   336 static void
   337 razor_uri_unlock(int id)
   338 {
   339 	struct razor_uri_lock *ulock;
   340 #ifndef MSWIN_API
   341 	struct flock lock = {0};
   342 #endif
   343 
   344 	if (id < 0 || id >= ptr_array_len(&razor_uri_locks))
   345 		return;
   346 
   347 	ulock = ptr_array_index(&razor_uri_locks, id);
   348 
   349 	if (!ulock)
   350 		return;
   351 
   352 #ifdef MSWIN_API
   353 	(void)UnlockFile((HANDLE)_get_osfhandle(ulock->fd), 0, 0, 1, 0);
   354 #else
   355 	lock.l_type = F_UNLCK;
   356 	lock.l_whence = SEEK_SET;
   357 	lock.l_start = 0;
   358 	lock.l_len = 0;
   359 	(void)fcntl(ulock->fd, F_SETLK, &lock);
   360 #endif
   361 
   362 	ptr_array_remove_index(&razor_uri_locks, id);
   363 	free(ulock->uri);
   364 	close(ulock->fd);
   365 	free(ulock);
   366 }
   367 
   368 int
   369 razor_set_acquire_lock(struct razor_set *set, const char *uri, int exclusive,
   370 		       struct razor_error **error)
   371 {
   372 	int lock;
   373 	const char *errstr = NULL;
   374 	assert(set != NULL);
   375 
   376 	if (!uri) {
   377 		razor_uri_unlock(set->lock);
   378 		set->lock = -1;
   379 		return 0;
   380 	}
   381 
   382 	lock = razor_uri_lock(uri,
   383 			      O_CREAT | O_RDWR | O_TRUNC | O_BINARY,
   384 			      0666, exclusive, error);
   385 
   386 	if (lock >= 0) {
   387 		set->lock = lock;
   388 		return 0;
   389 	}
   390 
   391 	if (!error)
   392 		return -1;
   393 		
   394 	if (razor_error_matches(*error, RAZOR_GENERAL_ERROR,
   395 				RAZOR_GENERAL_ERROR_ALREADY_LOCKED)) {
   396 		/*
   397 		 * This represents a design flaw in the caller.
   398 		 */
   399 		if (exclusive)
   400 			errstr = "Database is in use and cannot be locked";
   401 		else
   402 			errstr = "Database is being updated and cannot be locked";
   403 	} else if (razor_error_matches(*error, RAZOR_GENERAL_ERROR,
   404 				       RAZOR_GENERAL_ERROR_EXTERNALLY_LOCKED)) {
   405 		/*
   406 		 * This is probably caused by another application holding
   407 		 * the lock, but under MS-Windows the file could in theory
   408 		 * be locked by this application outside of razor_set.
   409 		 */
   410 		if (exclusive)
   411 			errstr = "Database is in use. Please try again later";
   412 		else
   413 			errstr = "Database is being updated. Please try again later";
   414 	}
   415 
   416 	if (errstr) {
   417 		razor_error_free(*error);
   418 		razor_set_error(error,
   419 				RAZOR_GENERAL_ERROR,
   420 				RAZOR_GENERAL_ERROR_DATABASE_LOCKED,
   421 				uri, errstr);
   422 	}
   423 
   424 	return -1;
   425 }
   426 
   427 static void
   428 razor_set_destroy(struct razor_set *set)
   429 {
   430 	struct razor_mapped_file *file, *next;
   431 	struct array *array;
   432 	int i;
   433 
   434 	assert (set != NULL);
   435 
   436 	if (set->mapped_files == NULL) {
   437 		for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
   438 			array = (void *) set + razor_sections[i].offset;
   439 			array_release(array);
   440 		}
   441 	} else {
   442 		for (file = set->mapped_files; file != NULL; file = next) {
   443 			next = file->next;
   444 			razor_uri_free_contents(file->header, file->size);
   445 			free(file);
   446 		}
   447 	}
   448 
   449 	razor_set_acquire_lock(set, NULL, 0, NULL);
   450 	free(set);
   451 }
   452 
   453 RAZOR_EXPORT void
   454 razor_set_unref(struct razor_set *set)
   455 {
   456 	if (set && !--set->ref_count)
   457 		razor_set_destroy(set);
   458 }
   459 
   460 RAZOR_EXPORT struct razor_set *
   461 razor_set_ref(struct razor_set *set)
   462 {
   463 	if (set)
   464 		set->ref_count++;
   465 	return set;
   466 }
   467 
   468 RAZOR_EXPORT void
   469 razor_set_write_to_handle(struct razor_set *set, struct razor_atomic *atomic,
   470 			  int handle, uint32_t section_mask)
   471 {
   472 	struct razor_set_header header;
   473 	struct razor_set_section sections[ARRAY_SIZE(razor_sections)];
   474 	struct hashtable table;
   475 	struct array pool, *arrays[ARRAY_SIZE(razor_sections)];
   476 	uint32_t offset;
   477 	int count, i, j;
   478 	static const char padding[4];
   479 
   480 	array_init(&pool);
   481 	hashtable_init(&table, &pool);
   482 
   483 	j = 0;
   484 	for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
   485 		if ((razor_sections[i].flags & section_mask) == 0)
   486 			continue;
   487 
   488 		arrays[j] = (void *) set + razor_sections[i].offset;
   489 		sections[j].name =
   490 			hashtable_tokenize(&table, razor_sections[i].name);
   491 		j++;
   492 	}
   493 
   494 	count = j;
   495 	header.magic = RAZOR_MAGIC;
   496 	header.version = set->header_version;
   497 	header.num_sections = count;
   498 	offset = sizeof header + count * sizeof *sections + ALIGN(pool.size, 4);
   499 
   500 	for (i = 0; i < count; i++) {
   501 		sections[i].offset = offset;
   502 		sections[i].size = arrays[i]->size;
   503 		offset += ALIGN(arrays[i]->size, 4);
   504 	}
   505 
   506 	razor_atomic_write(atomic, handle, &header, sizeof header);
   507 	razor_atomic_write(atomic, handle, sections, count * sizeof *sections);
   508 	razor_atomic_write(atomic, handle, pool.data, pool.size);
   509 	razor_atomic_write(atomic, handle, padding, PADDING(pool.size, 4));
   510 
   511 	for (i = 0; i < count; i++) {
   512 		razor_atomic_write(atomic, handle, arrays[i]->data,
   513 				   arrays[i]->size);
   514 		razor_atomic_write(atomic, handle, padding,
   515 				   PADDING(arrays[i]->size, 4));
   516 	}
   517 
   518 	array_release(&pool);
   519 	hashtable_release(&table);
   520 }
   521 
   522 RAZOR_EXPORT int
   523 razor_set_write(struct razor_set *set, struct razor_atomic *atomic,
   524 		const char *uri, uint32_t sections)
   525 {
   526 	int h;
   527 
   528 	h = razor_atomic_create_file(atomic, uri,
   529 				     S_IRWXU | S_IRWXG | S_IRWXO);
   530 	if (h < 0)
   531 		return -1;
   532 
   533 	razor_set_write_to_handle(set, atomic, h, sections);
   534 
   535 	return razor_atomic_close(atomic, h);
   536 }
   537 
   538 RAZOR_EXPORT void
   539 razor_build_evr(char *evr_buf, int size, const char *epoch,
   540 		const char *version, const char *release)
   541 {
   542 	int len;
   543 
   544 	if (!version || !*version) {
   545 		*evr_buf = '\0';
   546 		return;
   547 	}
   548 
   549 	if (epoch && *epoch && strcmp(epoch, "0") != 0) {
   550 		len = snprintf(evr_buf, size, "%s:", epoch);
   551 		evr_buf += len;
   552 		size -= len;
   553 	}
   554 	len = snprintf(evr_buf, size, "%s", version);
   555 	evr_buf += len;
   556 	size -= len;
   557 	if (release && *release)
   558 		snprintf(evr_buf, size, "-%s", release);
   559 }
   560 
   561 RAZOR_EXPORT int
   562 razor_versioncmp(const char *s1, const char *s2)
   563 {
   564 	const char *p1, *p2;
   565 	long n1, n2;
   566 	int res;
   567 
   568 	assert (s1 != NULL);
   569 	assert (s2 != NULL);
   570 
   571 	n1 = strtol(s1, (char **) &p1, 10);
   572 	n2 = strtol(s2, (char **) &p2, 10);
   573 
   574 	/* Epoch; if one but not the other has an epoch set, default
   575 	 * the epoch-less version to 0. */
   576 	res = (*p1 == ':') - (*p2 == ':');
   577 	if (res < 0) {
   578 		n1 = 0;
   579 		p1 = s1;
   580 		p2++;
   581 	} else if (res > 0) {
   582 		p1++;
   583 		n2 = 0;
   584 		p2 = s2;
   585 	}
   586 
   587 	if (n1 != n2)
   588 		return n1 - n2;
   589 	while (*p1 && *p2) {
   590 		if (*p1 != *p2)
   591 			return *p1 - *p2;
   592 		p1++;
   593 		p2++;
   594 		if (isdigit(*p1) && isdigit(*p2))
   595 			return razor_versioncmp(p1, p2);
   596 	}
   597 
   598 	return *p1 - *p2;
   599 }
   600 
   601 static const char *
   602 razor_package_get_details_string(struct razor_set *set,
   603 				 struct razor_package *package,
   604 				 enum razor_detail_type type)
   605 {
   606 	const char *pool;
   607 
   608 	switch (type) {
   609 	case RAZOR_DETAIL_NAME:
   610 		pool = set->string_pool.data;
   611 		return &pool[package->name];
   612 
   613 	case RAZOR_DETAIL_VERSION:
   614 		pool = set->string_pool.data;
   615 		return &pool[package->version];
   616 
   617 	case RAZOR_DETAIL_ARCH:
   618 		pool = set->string_pool.data;
   619 		return &pool[package->arch];
   620 
   621 	case RAZOR_DETAIL_SUMMARY:
   622 		if (!set->details_string_pool.size)
   623 			return "";
   624 		pool = set->details_string_pool.data;
   625 		return &pool[package->summary];
   626 
   627 	case RAZOR_DETAIL_DESCRIPTION:
   628 		if (!set->details_string_pool.size)
   629 			return "";
   630 		pool = set->details_string_pool.data;
   631 		return &pool[package->description];
   632 
   633 	case RAZOR_DETAIL_URL:
   634 		if (!set->details_string_pool.size)
   635 			return "";
   636 		pool = set->details_string_pool.data;
   637 		return &pool[package->url];
   638 
   639 	case RAZOR_DETAIL_LICENSE:
   640 		if (!set->details_string_pool.size)
   641 			return "";
   642 		pool = set->details_string_pool.data;
   643 		return &pool[package->license];
   644 
   645 	case RAZOR_DETAIL_PREUNPROG:
   646 		pool = set->string_pool.data;
   647 		return &pool[package->preun.program];
   648 
   649 	case RAZOR_DETAIL_PREUN:
   650 		pool = set->string_pool.data;
   651 		return &pool[package->preun.body];
   652 
   653 	case RAZOR_DETAIL_POSTUNPROG:
   654 		pool = set->string_pool.data;
   655 		return &pool[package->postun.program];
   656 
   657 	case RAZOR_DETAIL_POSTUN:
   658 		pool = set->string_pool.data;
   659 		return &pool[package->postun.body];
   660 
   661 	default:
   662 		fprintf(stderr, "type %u not found\n", type);
   663 		return NULL;
   664 	}
   665 }
   666 
   667 static const char *const *
   668 razor_package_get_details_array(struct razor_set *set,
   669 				struct razor_package *package,
   670 				enum razor_detail_type type)
   671 {
   672 	switch (type) {
   673 	case RAZOR_DETAIL_PREFIXES:
   674 		/* We don't track prefixes in packages. Install
   675 		 * prefixes are tracked, and made available via
   676 		 * razor_install_prefix_iterator_create().
   677 		 */
   678 		return NULL;
   679 
   680 	default:
   681 		fprintf(stderr, "type %u not found\n", type);
   682 		return NULL;
   683 	}
   684 }
   685 
   686 /**
   687  * razor_package_get_details_varg:
   688  * @set: a %razor_set
   689  * @package: a %razor_package
   690  * @args: a va_list of arguments to set
   691  **/
   692 void
   693 razor_package_get_details_varg(struct razor_set *set,
   694 			       struct razor_package *package,
   695 			       va_list args)
   696 {
   697 	int i;
   698 	enum razor_detail_type type;
   699 	const char **string;
   700 	const char *const **array;
   701 
   702 	for (i = 0;; i += 2) {
   703 		type = va_arg(args, enum razor_detail_type);
   704 		if (type == RAZOR_DETAIL_LAST)
   705 			break;
   706 		if (type == RAZOR_DETAIL_PREFIXES) {
   707 			array = va_arg(args, const char *const **);
   708 			*array = razor_package_get_details_array(set, package,
   709 								 type);
   710 		} else {
   711 			string = va_arg(args, const char **);
   712 			*string = razor_package_get_details_string(set, package,
   713 								   type);
   714 		}
   715 	}
   716 
   717 }
   718 
   719 /**
   720  * razor_package_get_details:
   721  * @set: a %razor_set
   722  * @package: a %razor_package
   723  *
   724  * Gets details about a package using a varg interface
   725  * The vararg must be terminated with %RAZOR_DETAIL_LAST.
   726  *
   727  * Example: razor_package_get_details (set, package,
   728  *				       RAZOR_DETAIL_URL, &url,
   729  *				       RAZOR_DETAIL_LAST);
   730  **/
   731 RAZOR_EXPORT void
   732 razor_package_get_details(struct razor_set *set, struct razor_package *package, ...)
   733 {
   734 	va_list args;
   735 
   736 	assert (set != NULL);
   737 	assert (package != NULL);
   738 
   739 	va_start(args, NULL);
   740 	razor_package_get_details_varg (set, package, args);
   741 	va_end (args);
   742 }
   743 
   744 /**
   745  * razor_package_remove:
   746  * @prev: The %razor_set before the current transaction
   747  * @next: The %razor_set after the current transaction is applied
   748  * @package: a %razor_package
   749  * @root: the root into which the package is currently installed
   750  * @install_count: the value to pass to uninstall scripts
   751  * @stage: Limit the removal to just the scripts or the files
   752  *
   753  * Removes an installed package.
   754  **/
   755 RAZOR_EXPORT int
   756 razor_package_remove(struct razor_set *prev, struct razor_set *next,
   757 		     struct razor_atomic *atomic, struct razor_package *package,
   758 		     const char *root_uri, int install_count,
   759 		     enum razor_stage_type stage)
   760 {
   761 	struct razor_file_iterator *fi;
   762 	struct razor_package_iterator *pi;
   763 	struct razor_package *p;
   764 	char *uri, *relative, *buffer, buf[32];
   765 	const char *name, *program, *script;
   766 	int i, count, retval = 0;
   767 	struct environment env;
   768 	struct list *link;
   769 	const char *prefix;
   770 	struct razor_error *tmp_error = NULL;
   771 
   772 	if (stage & RAZOR_STAGE_SCRIPTS) {
   773 		environment_init(&env);
   774 		link = list_first(&package->install_prefixes,
   775 				  &prev->prefix_pool);
   776 		for (i = 0; link; i++) {
   777 			prefix = (const char *)prev->string_pool.data +
   778 				 link->data;
   779 			sprintf(buf, "RPM_INSTALL_PREFIX%d", i);
   780 			environment_add_variable(&env, buf, prefix);
   781 			link = list_next(link);
   782 		}
   783 		environment_set(&env);
   784 	}
   785 
   786 	if (stage & RAZOR_STAGE_SCRIPTS_PRE) {
   787 		razor_package_get_details(prev, package,
   788 					  RAZOR_DETAIL_PREUNPROG, &program,
   789 					  RAZOR_DETAIL_PREUN, &script,
   790 					  RAZOR_DETAIL_LAST);
   791 
   792 		retval = razor_run_script(root_uri, RAZOR_PROPERTY_PREUN,
   793 					  program, script, install_count,
   794 					  &tmp_error);
   795 
   796 		if (retval < 0) {
   797 			razor_atomic_propagate_error(atomic, tmp_error, NULL);
   798 			tmp_error = NULL;
   799 		}
   800 	}
   801 
   802 	if (!retval && (stage & RAZOR_STAGE_FILES)) {
   803 		fi = razor_file_iterator_create(prev, package, 1);
   804 
   805 		while (razor_file_iterator_next(fi, &name)) {
   806 			pi = razor_package_iterator_create_for_file(next, name);
   807 			count = 0;
   808 			while (razor_package_iterator_next(pi, &p,
   809 							   RAZOR_DETAIL_LAST))
   810 				count++;
   811 			razor_package_iterator_destroy(pi);
   812 			if (count <= 0) {
   813 				uri = razor_path_to_uri(name);
   814 
   815 				if (str_has_prefix(uri, "file:///"))
   816 					relative = uri + 8;
   817 				else if (str_has_prefix(uri, "file:/"))
   818 					relative = uri + 6;
   819 				else if (str_has_prefix(uri, "file:"))
   820 					relative = uri + 5;
   821 				else if (str_has_prefix(uri, "/"))
   822 					relative = uri + 1;
   823 				else
   824 					relative = uri;
   825 
   826 				buffer = razor_resolve_uri_root(root_uri,
   827 								relative, 1,
   828 								&tmp_error);
   829 				free(uri);
   830 
   831 				if (buffer) {
   832 					razor_atomic_remove(atomic, buffer);
   833 					free(buffer);
   834 				} else {
   835 					razor_atomic_propagate_error(atomic,
   836 								     tmp_error,
   837 								     NULL);
   838 					tmp_error = NULL;
   839 					break;
   840 				}
   841 			}
   842 		}
   843 
   844 		razor_file_iterator_destroy(fi);
   845 
   846 		retval = razor_atomic_in_error_state(atomic);
   847 	}
   848 
   849 	if (!retval && (stage & RAZOR_STAGE_SCRIPTS_POST)) {
   850 		razor_package_get_details(prev, package,
   851 					  RAZOR_DETAIL_POSTUNPROG, &program,
   852 					  RAZOR_DETAIL_POSTUN, &script,
   853 					  RAZOR_DETAIL_LAST);
   854 
   855 		retval |= razor_run_script(root_uri, RAZOR_PROPERTY_POSTUN,
   856 					   program, script, install_count,
   857 					   &tmp_error);
   858 
   859 		if (retval < 0) {
   860 			razor_atomic_propagate_error(atomic, tmp_error, NULL);
   861 			tmp_error = NULL;
   862 		}
   863 	}
   864 
   865 	if (stage & RAZOR_STAGE_SCRIPTS) {
   866 		environment_unset(&env);
   867 		environment_release(&env);
   868 	}
   869 
   870 	return retval;
   871 }
   872 
   873 RAZOR_EXPORT const char *
   874 razor_property_relation_to_string(struct razor_property *p)
   875 {
   876 	assert (p != NULL);
   877 
   878 	switch (p->flags & RAZOR_PROPERTY_RELATION_MASK) {
   879 	case RAZOR_PROPERTY_LESS:
   880 		return "<";
   881 
   882 	case RAZOR_PROPERTY_LESS | RAZOR_PROPERTY_EQUAL:
   883 		return "<=";
   884 
   885 	case RAZOR_PROPERTY_EQUAL:
   886 		return "=";
   887 
   888 	case RAZOR_PROPERTY_GREATER | RAZOR_PROPERTY_EQUAL:
   889 		return ">=";
   890 
   891 	case RAZOR_PROPERTY_GREATER:
   892 		return ">";
   893 
   894 	default:
   895 		return "?";
   896 	}
   897 }
   898 
   899 RAZOR_EXPORT const char *
   900 razor_property_type_to_string(struct razor_property *p)
   901 {
   902 	assert (p != NULL);
   903 
   904 	switch (p->flags & RAZOR_PROPERTY_TYPE_MASK) {
   905 	case RAZOR_PROPERTY_REQUIRES:
   906 		return "requires";
   907 	case RAZOR_PROPERTY_PROVIDES:
   908 		return "provides";
   909 	case RAZOR_PROPERTY_CONFLICTS:
   910 		return "conflicts";
   911 	case RAZOR_PROPERTY_OBSOLETES:
   912 		return "obsoletes";
   913 	default:
   914 		return NULL;
   915 	}
   916 }
   917 
   918 RAZOR_EXPORT struct razor_entry *
   919 razor_set_find_entry(struct razor_set *set,
   920 		     struct razor_entry *dir, const char *pattern)
   921 {
   922 	struct razor_entry *e, *subdir;
   923 	const char *n, *pool = set->file_string_pool.data;
   924 	int len;
   925 
   926 	assert (set != NULL);
   927 	assert (pattern != NULL);
   928 
   929 	if (dir == NULL)
   930 		return NULL;
   931 
   932 	e = dir;
   933 	do {
   934 		n = pool + e->name;
   935 		if (strcmp(pattern, n) == 0)
   936 			return e;
   937 		len = strlen(n);
   938 		if (e->start != 0 && strncmp(pattern, n, len) == 0 &&
   939 		    pattern[len] == '/') {
   940 			subdir = (struct razor_entry *) set->files.data +
   941 				 e->start;
   942 			return razor_set_find_entry(set, subdir,
   943 						    pattern + len + 1);
   944 		}
   945 	} while (!((e++)->flags & RAZOR_ENTRY_LAST));
   946 
   947 	return NULL;
   948 }
   949 
   950 static void
   951 list_dir(struct razor_set *set, struct razor_entry *dir,
   952 	 char *prefix, const char *pattern)
   953 {
   954 	struct razor_entry *e, *subdir;
   955 	const char *n, *pool = set->file_string_pool.data;
   956 
   957 	e = dir;
   958 	do {
   959 		n = pool + e->name;
   960 		if (pattern && pattern[0] && fnmatch(pattern, n, 0) != 0)
   961 			continue;
   962 		printf("%s/%s\n", prefix, n);
   963 		if (e->start) {
   964 			char *sub = prefix + strlen (prefix);
   965 			*sub = '/';
   966 			strcpy (sub + 1, n);
   967 			subdir = (struct razor_entry *) set->files.data +
   968 				 e->start;
   969 			list_dir(set, subdir, prefix, pattern);
   970 			*sub = '\0';
   971 		}
   972 	} while (!((e++)->flags & RAZOR_ENTRY_LAST));
   973 }
   974 
   975 RAZOR_EXPORT void
   976 razor_set_list_files(struct razor_set *set, const char *pattern)
   977 {
   978 	struct razor_entry *root, *e;
   979 	char buffer[512], *p, *base;
   980 
   981 	assert (set != NULL);
   982 
   983 	root = (struct razor_entry *) set->files.data;
   984 
   985 	if (pattern == NULL) {
   986 		p = set->file_string_pool.data;
   987 		e = root;
   988 		do {
   989 			if (e->start) {
   990 				strcpy(buffer, p + e->name);
   991 				list_dir(set, root + e->start, buffer, NULL);
   992 			}
   993 		} while (!((e++)->flags & RAZOR_ENTRY_LAST));
   994 		return;
   995 	}
   996 
   997 	strcpy(buffer, pattern);
   998 	e = razor_set_find_entry(set, root, buffer);
   999 	if (e && e->start) {
  1000 		base = NULL;
  1001 	} else {
  1002 		p = strrchr(buffer, '/');
  1003 		if (p) {
  1004 			*p = '\0';
  1005 			base = p + 1;
  1006 		} else {
  1007 			base = NULL;
  1008 		}
  1009 	}
  1010 	e = razor_set_find_entry(set, root, buffer);
  1011 	if (e && e->start)
  1012 		list_dir(set, root + e->start, buffer, base);
  1013 }
  1014 
  1015 RAZOR_EXPORT void
  1016 razor_set_list_package_files(struct razor_set *set,
  1017 			     struct razor_package *package)
  1018 {
  1019 	struct razor_file_iterator *fi;
  1020 	const char *name;
  1021 
  1022 	assert (set != NULL);
  1023 	assert (package != NULL);
  1024 
  1025 	fi = razor_file_iterator_create(set, package, 0);
  1026 
  1027 	while (razor_file_iterator_next(fi, &name))
  1028 		printf("%s\n", name);
  1029 
  1030 	razor_file_iterator_destroy(fi);
  1031 }
  1032 
  1033 /*
  1034  * Package data can potentially come from two places. The so-called
  1035  * metadata (eg., from comps.xml) and from an RPM file. We consider
  1036  * a package which has additional data from an RPM file as "fixed".
  1037  * If a package needs fixing, then razor_transaction_fixup_package()
  1038  * will do so. When considering what packages to add and to remove
  1039  * we need to take this into account since we always want to add
  1040  * unfixed packages (otherwise we have a potential conflict between
  1041  * the existing package data and that present in the RPM).
  1042  */
  1043 static int
  1044 razor_package_is_fixed(struct razor_set *set, struct razor_package *p)
  1045 {
  1046 	const char *preunprog, *preun, *postunprog, *postun;
  1047 
  1048 	if (!p)
  1049 		return 0;
  1050 	razor_package_get_details(set, p,
  1051 				  RAZOR_DETAIL_PREUNPROG, &preunprog,
  1052 				  RAZOR_DETAIL_PREUN, &preun,
  1053 				  RAZOR_DETAIL_POSTUNPROG, &postunprog,
  1054 				  RAZOR_DETAIL_POSTUN, &postun,
  1055 				  RAZOR_DETAIL_LAST);
  1056 	return *preunprog || *preun || *postunprog || *postun;
  1057 }
  1058 
  1059 RAZOR_EXPORT void
  1060 razor_set_diff(struct razor_set *set, struct razor_set *upstream,
  1061 	       razor_diff_callback_t callback, void *data)
  1062 {
  1063  	struct razor_package_iterator *pi1, *pi2;
  1064  	struct razor_package *p1, *p2;
  1065 	const char *name1, *name2, *version1, *version2, *arch1, *arch2;
  1066 	int res, is_fixed1, is_fixed2;
  1067 
  1068 	assert (set != NULL);
  1069 	assert (upstream != NULL);
  1070 
  1071 	pi1 = razor_package_iterator_create(set);
  1072 	pi2 = razor_package_iterator_create(upstream);
  1073 
  1074 	razor_package_iterator_next(pi1, &p1,
  1075 				    RAZOR_DETAIL_NAME, &name1,
  1076 				    RAZOR_DETAIL_VERSION, &version1,
  1077 				    RAZOR_DETAIL_ARCH, &arch1,
  1078 				    RAZOR_DETAIL_LAST);
  1079 	is_fixed1 = razor_package_is_fixed(set, p1);
  1080 	razor_package_iterator_next(pi2, &p2,
  1081 				    RAZOR_DETAIL_NAME, &name2,
  1082 				    RAZOR_DETAIL_VERSION, &version2,
  1083 				    RAZOR_DETAIL_ARCH, &arch2,
  1084 				    RAZOR_DETAIL_LAST);
  1085 	is_fixed2 = razor_package_is_fixed(upstream, p2);
  1086 
  1087 	while (p1 || p2) {
  1088 		if (p1 && p2) {
  1089 			res = strcmp(name1, name2);
  1090 			if (res == 0)
  1091 				res = razor_versioncmp(version1, version2);
  1092 			if (res == 0)
  1093 				res = is_fixed1 - is_fixed2;
  1094 		} else {
  1095 			res = 0;
  1096 		}
  1097 
  1098 		if (p2 == NULL || res < 0)
  1099 			callback(RAZOR_DIFF_ACTION_REMOVE,
  1100 				 p1, name1, version1, arch1, data);
  1101 		else if (p1 == NULL || res > 0)
  1102 			callback(RAZOR_DIFF_ACTION_ADD,
  1103 				 p2, name2, version2, arch2, data);
  1104 
  1105 		if (p1 != NULL && res <= 0) {
  1106 			razor_package_iterator_next(pi1, &p1,
  1107 						    RAZOR_DETAIL_NAME, &name1,
  1108 						    RAZOR_DETAIL_VERSION, &version1,
  1109 						    RAZOR_DETAIL_ARCH, &arch1,
  1110 						    RAZOR_DETAIL_LAST);
  1111 			is_fixed1 = razor_package_is_fixed(set, p1);
  1112 		}
  1113 		if (p2 != NULL && res >= 0) {
  1114 			razor_package_iterator_next(pi2, &p2,
  1115 						    RAZOR_DETAIL_NAME, &name2,
  1116 						    RAZOR_DETAIL_VERSION, &version2,
  1117 						    RAZOR_DETAIL_ARCH, &arch2,
  1118 						    RAZOR_DETAIL_LAST);
  1119 			is_fixed2 = razor_package_is_fixed(upstream, p2);
  1120 		}
  1121 	}
  1122 
  1123 	razor_package_iterator_destroy(pi1);
  1124 	razor_package_iterator_destroy(pi2);
  1125 }
  1126 
  1127 struct install_action {
  1128 	enum razor_install_action action;
  1129 	struct razor_package *package;
  1130 };
  1131 
  1132 struct razor_install_iterator {
  1133 	struct razor_set *set;
  1134 	struct razor_set *next;
  1135 	struct array actions;
  1136 	struct deque *order, *left;
  1137 };
  1138 
  1139 static void
  1140 add_action(enum razor_diff_action action,
  1141 	   struct razor_package *package,
  1142 	   const char *name,
  1143 	   const char *version,
  1144 	   const char *arch,
  1145 	   void *data)
  1146 {
  1147 	struct razor_install_iterator *ii = data;
  1148 	struct install_action *a;
  1149 
  1150 	a = array_add(&ii->actions, sizeof *a);
  1151 	a->package = package;
  1152 
  1153 	switch (action) {
  1154 	case RAZOR_DIFF_ACTION_ADD:
  1155 		a->action = RAZOR_INSTALL_ACTION_ADD;
  1156 		break;
  1157 	case RAZOR_DIFF_ACTION_REMOVE:
  1158 		a->action = RAZOR_INSTALL_ACTION_REMOVE;
  1159 		break;
  1160 	}
  1161 }
  1162 
  1163 /*
  1164  * Does <package> have a requirement for <script> which is
  1165  * satisfied by <provider> ?
  1166  * Note: We already know that <provider> is to be added as part of this install
  1167  * so there is no need to check the relation.
  1168  */
  1169 static int
  1170 package_script_requires(struct razor_set *set, struct razor_package *package,
  1171 			enum razor_property_flags script,
  1172 			struct razor_package *provider)
  1173 {
  1174 	struct list *link;
  1175 	struct razor_property *prop;
  1176 
  1177 	link = list_first(&package->properties, &set->property_pool);
  1178 	for(; link; link = list_next(link)) {
  1179 		prop = set->properties.data;
  1180 		prop += link->data;
  1181 		if ((prop->flags & RAZOR_PROPERTY_SCRIPT_MASK) & script &&
  1182 		    (prop->flags & RAZOR_PROPERTY_TYPE_MASK) == RAZOR_PROPERTY_REQUIRES &&
  1183 		    provider->name == prop->name)
  1184 			return 1;
  1185 	}
  1186 	return 0;
  1187 }
  1188 
  1189 RAZOR_EXPORT struct razor_install_iterator *
  1190 razor_set_create_install_iterator(struct razor_set *set,
  1191 				  struct razor_set *next)
  1192 {
  1193 	struct razor_install_iterator *ii;
  1194 	struct razor_property *prop;
  1195 	/* A graph of the actions to be perfomed where
  1196 	 * A->B means action A should follow action B.
  1197 	 */
  1198 	struct graph follows;
  1199 	struct install_action *actions, *ai, *aj, *an;
  1200 	int i, j, count, vertex_added;
  1201 	struct list *link;
  1202 	struct razor_set *rs;
  1203 	struct deque *order;
  1204 	int barrier_needed;
  1205 
  1206 	assert (set != NULL);
  1207 	assert (next != NULL);
  1208 
  1209 	ii = zalloc(sizeof *ii);
  1210 	ii->set = set;
  1211 	ii->next = next;
  1212 	
  1213 	razor_set_diff(set, next, add_action, ii);
  1214 
  1215 	actions = ii->actions.data;
  1216 	count = ii->actions.size / sizeof (struct install_action);
  1217 
  1218 	graph_init(&follows);
  1219 
  1220 	for(i = 0; i < count; i++) {
  1221 		ai = actions + i;
  1222 		rs = ai->action == RAZOR_INSTALL_ACTION_ADD ? next : set;
  1223 		vertex_added = 0;
  1224 		link = list_first(&ai->package->properties, &rs->property_pool);
  1225 		for(; link; link = list_next(link)) {
  1226 			prop = rs->properties.data;
  1227 			prop += link->data;
  1228 			switch(prop->flags & RAZOR_PROPERTY_TYPE_MASK) {
  1229 			case RAZOR_PROPERTY_REQUIRES:
  1230 			case RAZOR_PROPERTY_CONFLICTS:
  1231 				for(j = 0; j < count; j++) {
  1232 					if (j == i)
  1233 						continue;
  1234 					aj = actions + j;
  1235 					if (aj->package->name == prop->name) {
  1236 						if (ai->action ==
  1237 						    RAZOR_INSTALL_ACTION_ADD)
  1238 							graph_add_edge(&follows,
  1239 								       i, j);
  1240 						else
  1241 							graph_add_edge(&follows,
  1242 								       j, i);
  1243 						vertex_added++;
  1244 					}
  1245 				}
  1246 				break;
  1247 			}
  1248 		}
  1249 		if (ai->action == RAZOR_INSTALL_ACTION_ADD) {
  1250 			for(j = 0; j < count; j++) {
  1251 				if (j == i)
  1252 					continue;
  1253 				aj = actions + j;
  1254 				if (aj->package == ai->package &&
  1255 				    aj->action == RAZOR_INSTALL_ACTION_REMOVE) {
  1256 					graph_add_edge(&follows, i, j);
  1257 					vertex_added++;
  1258 				}
  1259 			}
  1260 		}
  1261 		if (!vertex_added)
  1262 			graph_add_edge(&follows, i, i);
  1263 	}
  1264 
  1265 	/*
  1266 	 * Because files are installed and removed using razor_atomic,
  1267 	 * but scripts are run with no regard for these, we need some
  1268 	 * means for synchronisation between the two. We support this
  1269 	 * via transaction barriers which are points where
  1270 	 * razor_atomic_commit() should be called so that scripts can
  1271 	 * rely on the files they need being present.
  1272 	 *
  1273 	 * Rules:
  1274 	 *   1)	Transaction barriers never occur within a dependency
  1275 	 *	loop. Since we can't guarantee any ordering of actions
  1276 	 *	within such a loop, they make no sense.
  1277 	 *   2) Otherwise the following table applies:
  1278 	 *	Action I    Action J	Barrier between I and J iff
  1279 	 *	ADD P	    ADD Q	%pre(Q) requires P
  1280 	 *	ADD P	    REMOVE Q	%preun(Q) requires P
  1281 	 *	REMOVE P    ADD Q	never
  1282 	 *	REMOVE P    REMOVE Q	%postun(P) requires Q
  1283 	 *
  1284 	 * FIXME:
  1285 	 *	This should take account of conflicts somehow.
  1286 	 */
  1287 	order = graph_sort(&follows);
  1288 	ii->order = deque_new(0);
  1289 	if (!deque_empty(order)) {
  1290 		j = deque_pop(order);
  1291 		deque_unshift(ii->order, j);
  1292 	}
  1293 	while (!deque_empty(order)) {
  1294 		i = j;
  1295 		j = deque_pop(order);
  1296 		ai = actions + i;
  1297 		aj = actions + j;
  1298 		if (graph_is_connected(&follows, i, j) &&
  1299 		    graph_is_connected(&follows, j, i))
  1300 			barrier_needed = 0;
  1301 		else if (ai->action == RAZOR_INSTALL_ACTION_ADD &&
  1302 			 aj->action == RAZOR_INSTALL_ACTION_ADD &&
  1303 			 package_script_requires(next, aj->package,
  1304 						 RAZOR_PROPERTY_PRE,
  1305 						 ai->package))
  1306 			barrier_needed = 1;
  1307 		else if (ai->action == RAZOR_INSTALL_ACTION_ADD &&
  1308 			 aj->action == RAZOR_INSTALL_ACTION_REMOVE &&
  1309 			 package_script_requires(set, aj->package,
  1310 						 RAZOR_PROPERTY_PREUN,
  1311 						 ai->package))
  1312 			barrier_needed = 1;
  1313 		else if (ai->action == RAZOR_INSTALL_ACTION_REMOVE &&
  1314 			 aj->action == RAZOR_INSTALL_ACTION_REMOVE &&
  1315 			 package_script_requires(set, ai->package,
  1316 						 RAZOR_PROPERTY_POSTUN,
  1317 						 aj->package))
  1318 			barrier_needed = 1;
  1319 		else
  1320 			barrier_needed = 0;
  1321 		if (barrier_needed) {
  1322 			an = array_add(&ii->actions, sizeof *an);
  1323 			actions = ii->actions.data;
  1324 			an->package = NULL;
  1325 			an->action = RAZOR_INSTALL_ACTION_COMMIT;
  1326 			deque_unshift(ii->order, an - actions);
  1327 		}
  1328 		deque_unshift(ii->order, j);
  1329 	}
  1330 	deque_free(order);
  1331 
  1332 	ii->left = deque_dup(ii->order);
  1333 	graph_release(&follows);
  1334 
  1335 	return ii;
  1336 }
  1337 
  1338 RAZOR_EXPORT int
  1339 razor_install_iterator_next(struct razor_install_iterator *ii,
  1340 			    struct razor_package **package,
  1341 			    enum razor_install_action *action,
  1342 			    int *count)
  1343 {
  1344 	struct install_action *a;
  1345 	struct razor_package_iterator *pi;
  1346 	struct razor_package *pkg;
  1347 	const char *removing, *name;
  1348 
  1349 	if (deque_empty(ii->left))
  1350 		return 0;
  1351 
  1352 	a = (struct install_action *)ii->actions.data + deque_pop(ii->left);
  1353 	*package = a->package;
  1354 	*action = a->action;
  1355 	*count = 0;
  1356 
  1357 	if (a->action == RAZOR_INSTALL_ACTION_REMOVE) {
  1358 		razor_package_get_details(ii->set, a->package,
  1359 					  RAZOR_DETAIL_NAME, &removing,
  1360 					  RAZOR_DETAIL_LAST);
  1361 
  1362 		pi = razor_package_iterator_create(ii->next);
  1363 		while (razor_package_iterator_next(pi, &pkg,
  1364 						   RAZOR_DETAIL_NAME, &name,
  1365 						   RAZOR_DETAIL_LAST)) {
  1366 			if (!strcmp(name, removing))
  1367 				(*count)++;
  1368 		}
  1369 		razor_package_iterator_destroy(pi);
  1370 	} else if (a->action == RAZOR_INSTALL_ACTION_ADD)
  1371 		*count = 1;
  1372 
  1373 	return 1;
  1374 }
  1375 
  1376 static int
  1377 action_is_included(struct razor_install_iterator *ii, struct deque *done,
  1378 		   struct razor_package *package,
  1379 		   enum razor_install_action action)
  1380 {
  1381 	struct deque *t;
  1382 	struct install_action *a;
  1383 	int retval;
  1384 
  1385 	t = deque_dup(done);
  1386 
  1387 	while(!deque_empty(t)) {
  1388 		a = (struct install_action *)ii->actions.data + deque_pop(t);
  1389 		if (a->package == package && a->action == action)
  1390 			break;
  1391 	}
  1392 	retval = !deque_empty(t);
  1393 
  1394 	deque_free(t);
  1395 
  1396 	return retval;
  1397 }
  1398 
  1399 RAZOR_EXPORT struct razor_set *
  1400 razor_install_iterator_commit_set(struct razor_install_iterator *ii)
  1401 {
  1402 	struct razor_merger *merger;
  1403 	struct razor_set *set;
  1404 	struct razor_package *n, *nend, *s, *send;
  1405 	struct deque *done;
  1406 	size_t pos;
  1407 	char *npool, *spool;
  1408 	int cmp;
  1409 
  1410 	done = deque_dup(ii->order);
  1411 	pos = razor_install_iterator_tell(ii);
  1412 	while(deque_length(done) > pos)
  1413 		deque_shift(done);
  1414 
  1415 	s = ii->set->packages.data;
  1416 	send = ii->set->packages.data + ii->set->packages.size;
  1417 	spool = ii->set->string_pool.data;
  1418 
  1419 	n = ii->next->packages.data;
  1420 	nend = ii->next->packages.data + ii->next->packages.size;
  1421 	npool = ii->next->string_pool.data;
  1422 
  1423 	merger = razor_merger_create(ii->set, ii->next);
  1424 	while (s < send || n < nend) {
  1425 		if (s < send && n < nend)
  1426 			cmp = strcmp(&spool[s->name], &npool[n->name]);
  1427 		else if (s < send)
  1428 			cmp = -1;
  1429 		else
  1430 			cmp = 1;
  1431 
  1432 		if (cmp < 0) {
  1433 			if (!action_is_included(ii, done, s,
  1434 						RAZOR_INSTALL_ACTION_REMOVE))
  1435 				razor_merger_add_package(merger, s);
  1436 			s++;
  1437 		} else if (cmp == 0) {
  1438 			if (!action_is_included(ii, done, s,
  1439 						RAZOR_INSTALL_ACTION_REMOVE))
  1440 				razor_merger_add_package(merger, s);
  1441 			if (action_is_included(ii, done, n,
  1442 					       RAZOR_INSTALL_ACTION_ADD))
  1443 				razor_merger_add_package(merger, n);
  1444 
  1445 			s++;
  1446 			n++;
  1447 		} else {
  1448 			if (action_is_included(ii, done, n,
  1449 					       RAZOR_INSTALL_ACTION_ADD))
  1450 				razor_merger_add_package(merger, n);
  1451 			n++;
  1452 		}
  1453 	}
  1454 
  1455 	set = razor_merger_commit(merger);
  1456 	razor_merger_destroy(merger);
  1457 	deque_free(done);
  1458 
  1459 	return set;
  1460 }
  1461 
  1462 RAZOR_EXPORT void
  1463 razor_install_iterator_rewind(struct razor_install_iterator *ii)
  1464 {
  1465 	deque_free(ii->left);
  1466 	ii->left=deque_dup(ii->order);
  1467 }
  1468 
  1469 RAZOR_EXPORT size_t
  1470 razor_install_iterator_tell(struct razor_install_iterator *ii)
  1471 {
  1472 	return deque_length(ii->order) - deque_length(ii->left);
  1473 }
  1474 
  1475 RAZOR_EXPORT size_t
  1476 razor_install_iterator_seek(struct razor_install_iterator *ii, size_t pos)
  1477 {
  1478 	size_t current_pos;
  1479 
  1480 	if (pos > deque_length(ii->order))
  1481 		pos = deque_length(ii->order);
  1482 
  1483 	current_pos = razor_install_iterator_tell(ii);
  1484 
  1485 	if (pos < current_pos) {
  1486 		razor_install_iterator_rewind(ii);
  1487 		current_pos = 0;
  1488 	}
  1489 
  1490 	while(current_pos < pos) {
  1491 		(void) deque_pop(ii->left);
  1492 		current_pos++;
  1493 	}
  1494 
  1495 	return current_pos;
  1496 }
  1497 
  1498 RAZOR_EXPORT void
  1499 razor_install_iterator_destroy(struct razor_install_iterator *ii)
  1500 {
  1501 	array_release(&ii->actions);
  1502 	deque_free(ii->order);
  1503 	deque_free(ii->left);
  1504 	free(ii);
  1505 }