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