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