librazor/importer.c
author J. Ali Harlow <ali@juiblex.co.uk>
Thu Feb 09 20:45:27 2012 +0000 (2012-02-09)
changeset 418 33b825d3128d
parent 369 f8c27fe9fe63
child 438 fab0b8a61dcb
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  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 <string.h>
    24 #include "razor-internal.h"
    25 #include "razor.h"
    26 
    27 /**
    28  * razor_importer_create:
    29  *
    30  * Create a new %razor_importer.
    31  *
    32  * Returns: the new %razor_importer.
    33  **/
    34 RAZOR_EXPORT struct razor_importer *
    35 razor_importer_create(void)
    36 {
    37 	struct razor_importer *importer;
    38 
    39 	importer = zalloc(sizeof *importer);
    40 	importer->set = razor_set_create();
    41 	hashtable_init(&importer->table, &importer->set->string_pool);
    42 	hashtable_init(&importer->details_table,
    43 		       &importer->set->details_string_pool);
    44 	hashtable_init(&importer->file_table,
    45 		       &importer->set->file_string_pool);
    46 
    47 	return importer;
    48 }
    49 
    50 /**
    51  * razor_importer_destroy:
    52  * @importer: the %razor_importer
    53  *
    54  * Destroy an importer without creating a %razor_set.  Normally,
    55  * %razor_importer_finish will create a new %razor_set and destroy the
    56  * importer.  If the import must be aborted without creating the set,
    57  * just destroy the import using this function.
    58  **/
    59 RAZOR_EXPORT void
    60 razor_importer_destroy(struct razor_importer *importer)
    61 {
    62 	/* FIXME: write this */
    63 }
    64 
    65 
    66 /**
    67  * razor_importer_begin_package:
    68  * @importer: the %razor_importer
    69  * @name: the name of the new package
    70  * @version: the version of the new package
    71  * @arch: the architechture of the new package.
    72  *
    73  * Begin describing a new package to the importer.  This creates a new
    74  * package and sets the %name, %version and %arch.  Subsequent calls
    75  * to %razor_importer_add_details, %razor_importer_add_property and
    76  * %razor_importer_add_file further describe this package and
    77  * %razor_importer_finish_package marks the end of meta data for this
    78  * package.
    79  **/
    80 RAZOR_EXPORT void
    81 razor_importer_begin_package(struct razor_importer *importer,
    82 			     const char *name,
    83 			     const char *version,
    84 			     const char *arch)
    85 {
    86 	uint32_t empty;
    87 	struct razor_package *p;
    88 
    89 	p = array_add(&importer->set->packages, sizeof *p);
    90 	p->name = hashtable_tokenize(&importer->table, name);
    91 	p->flags = 0;
    92 	p->version = hashtable_tokenize(&importer->table, version);
    93 	p->arch = hashtable_tokenize(&importer->table, arch);
    94 
    95 	importer->package = p;
    96 	array_init(&importer->properties);
    97 	array_init(&importer->install_prefixes);
    98 
    99 	empty = hashtable_tokenize(&importer->details_table, "");
   100 	importer->package->preun.program = empty;
   101 	importer->package->preun.body = empty;
   102 	importer->package->postun.program = empty;
   103 	importer->package->postun.body = empty;
   104 }
   105 
   106 /**
   107  * razor_importer_finish_package:
   108  * @importer: the %razor_importer
   109  *
   110  * Tells the importer that the current package is complete.
   111  **/
   112 RAZOR_EXPORT void
   113 razor_importer_finish_package(struct razor_importer *importer)
   114 {
   115 	list_set_array(&importer->package->properties,
   116 		       &importer->set->property_pool,
   117 		       &importer->properties,
   118 		       1);
   119 
   120 	list_set_array(&importer->package->install_prefixes,
   121 		       &importer->set->prefix_pool,
   122 		       &importer->install_prefixes,
   123 		       0);
   124 
   125 	array_release(&importer->properties);
   126 	array_release(&importer->install_prefixes);
   127 }
   128 
   129 /**
   130  * razor_importer_add_details:
   131  * @importer: the %razor_importer
   132  * @summary: the package summary
   133  * @description: the package description
   134  * @url: the package url
   135  * @license: the package license
   136  *
   137  * Provide additional information for the current package.
   138  **/
   139 RAZOR_EXPORT void
   140 razor_importer_add_details(struct razor_importer *importer,
   141 			   const char *summary,
   142 			   const char *description,
   143 			   const char *url,
   144 			   const char *license)
   145 {
   146 	importer->package->summary = hashtable_tokenize(&importer->details_table, summary);
   147 	importer->package->description = hashtable_tokenize(&importer->details_table, description);
   148 	importer->package->url = hashtable_tokenize(&importer->details_table, url);
   149 	importer->package->license = hashtable_tokenize(&importer->details_table, license);
   150 }
   151 
   152 /**
   153  * razor_importer_add_script:
   154  * @importer: the %razor_importer
   155  * @script: either %RAZOR_PROPERTY_PREUN or %RAZOR_PROPERTY_POSTUN
   156  * @program: the program to run the script
   157  * @body: the body of the script
   158  *
   159  * Provide a script to use when uninstalling the current package.
   160  **/
   161 RAZOR_EXPORT void
   162 razor_importer_add_script(struct razor_importer *importer,
   163 			  enum razor_property_flags script,
   164 			  const char *program,
   165 			  const char *body)
   166 {
   167 	switch (script) {
   168 	case RAZOR_PROPERTY_PREUN:
   169 		importer->package->preun.program =
   170 			hashtable_tokenize(&importer->table, program);
   171 		importer->package->preun.body =
   172 			hashtable_tokenize(&importer->table, body);
   173 		break;
   174 	case RAZOR_PROPERTY_POSTUN:
   175 		importer->package->postun.program =
   176 			hashtable_tokenize(&importer->table, program);
   177 		importer->package->postun.body =
   178 			hashtable_tokenize(&importer->table, body);
   179 		break;
   180 	default:
   181 		break;
   182 	}
   183 }
   184 
   185 /**
   186  * razor_importer_add_install_prefixes:
   187  * @importer: the %razor_importer
   188  * @install_prefix: the relocated prefix
   189  *
   190  * Adds a relocated prefix for the current package.
   191  **/
   192 RAZOR_EXPORT void
   193 razor_importer_add_install_prefix(struct razor_importer *importer,
   194 				  const char *install_prefix)
   195 {
   196 	uint32_t *r;
   197 
   198 	r = array_add(&importer->install_prefixes, sizeof *r);
   199 	*r = hashtable_tokenize(&importer->table, install_prefix);
   200 }
   201 
   202 /**
   203  * razor_importer_add_property:
   204  * @importer: the %razor_importer
   205  * @name: name of the property
   206  * @flags: property flags
   207  * @version: version of the property or %NULL
   208  *
   209  * Add a property for the current package.  The %flags parameter
   210  * determines the type of the property and optionally the relation to
   211  * the specified version and the availability constraint.  See
   212  * %razor_property_flags for further information about the flag
   213  * parameter.
   214  **/
   215 RAZOR_EXPORT void
   216 razor_importer_add_property(struct razor_importer *importer,
   217 			    const char *name,
   218 			    uint32_t flags,
   219 			    const char *version)
   220 {
   221 	struct razor_property *p;
   222 	uint32_t *r;
   223 
   224 	p = array_add(&importer->set->properties, sizeof *p);
   225 	p->name = hashtable_tokenize(&importer->table, name);
   226 	p->flags = flags;
   227 	p->version = hashtable_tokenize(&importer->table, version);
   228 	list_set_ptr(&p->packages, importer->package -
   229 		     (struct razor_package *) importer->set->packages.data);
   230 
   231 	r = array_add(&importer->properties, sizeof *r);
   232 	*r = p - (struct razor_property *) importer->set->properties.data;
   233 
   234 	if (((flags & RAZOR_PROPERTY_TYPE_MASK) == RAZOR_PROPERTY_REQUIRES) &&
   235 	    *name == '/') {
   236 		r = array_add(&importer->file_requires, sizeof *r);
   237 		*r = p->name;
   238 	}
   239 }
   240 
   241 /**
   242  * razor_importer_add_file:
   243  * @importer: the %razor_importer
   244  * @name: name of the file
   245  *
   246  * Add a file to the current package.
   247  **/
   248 RAZOR_EXPORT void
   249 razor_importer_add_file(struct razor_importer *importer, const char *name)
   250 {
   251 	struct import_entry *e;
   252 
   253 	e = array_add(&importer->files, sizeof *e);
   254 
   255 	e->package = importer->package -
   256 		(struct razor_package *) importer->set->packages.data;
   257 	e->name = strdup(name);
   258 }
   259 
   260 static int
   261 compare_packages(const void *p1, const void *p2, void *data)
   262 {
   263 	const struct razor_package *pkg1 = p1, *pkg2 = p2;
   264 	struct razor_set *set = data;
   265 	char *pool = set->string_pool.data;
   266 
   267 	/* FIXME: what if the flags are different? */
   268 	if (pkg1->name == pkg2->name)
   269 		return razor_versioncmp(&pool[pkg1->version], &pool[pkg2->version]);
   270 	else
   271 		return strcmp(&pool[pkg1->name], &pool[pkg2->name]);
   272 }
   273 
   274 static int
   275 compare_properties(const void *p1, const void *p2, void *data)
   276 {
   277 	const struct razor_property *prop1 = p1, *prop2 = p2;
   278 	struct razor_set *set = data;
   279 	char *pool = set->string_pool.data;
   280 
   281 	if (prop1->name != prop2->name)
   282 		return strcmp(&pool[prop1->name], &pool[prop2->name]);
   283 	else if (prop1->flags != prop2->flags)
   284 		return prop1->flags - prop2->flags;
   285 	else if (prop1->version != prop2->version)
   286 		return razor_versioncmp(&pool[prop1->version], &pool[prop2->version]);
   287 	else
   288 		return prop1->packages.list_ptr - prop2->packages.list_ptr;
   289 }
   290 
   291 static uint32_t *
   292 uniqueify_properties(struct razor_set *set)
   293 {
   294 	struct razor_property *rp, *up, *rp_end;
   295 	struct array *pkgs, *p;
   296 	struct list_head *r;
   297 	uint32_t *map, *rmap;
   298 	int i, count, unique;
   299 
   300 	count = set->properties.size / sizeof(struct razor_property);
   301 	map = razor_qsort_with_data(set->properties.data,
   302 				    count,
   303 				    sizeof(struct razor_property),
   304 				    compare_properties,
   305 				    set);
   306 
   307 	rp_end = set->properties.data + set->properties.size;
   308 	rmap = malloc(count * sizeof *map);
   309 	pkgs = zalloc(count * sizeof *pkgs);
   310 	for (rp = set->properties.data, up = rp, i = 0; rp < rp_end; rp++, i++) {
   311 		if (rp->name != up->name ||
   312 		    rp->flags != up->flags ||
   313 		    rp->version != up->version) {
   314 			up++;
   315 			up->name = rp->name;
   316 			up->flags = rp->flags;
   317 			up->version = rp->version;
   318 		}
   319 
   320 		unique = up - (struct razor_property *) set->properties.data;
   321 		rmap[map[i]] = unique;
   322 		r = array_add(&pkgs[unique], sizeof *r);
   323 		*r = rp->packages;
   324 	}
   325 	free(map);
   326 
   327 	if (up != rp)
   328 		up++;
   329 	set->properties.size = (void *) up - set->properties.data;
   330 	rp_end = up;
   331 	for (rp = set->properties.data, p = pkgs; rp < rp_end; rp++, p++) {
   332 		list_set_array(&rp->packages, &set->package_pool, p, 0);
   333 		array_release(p);
   334 	}
   335 
   336 	free(pkgs);
   337 
   338 	return rmap;
   339 }
   340 
   341 static int
   342 compare_filenames(const void *p1, const void *p2, void *data)
   343 {
   344 	const struct import_entry *e1 = p1;
   345 	const struct import_entry *e2 = p2;
   346 	const char *n1 = e1->name;
   347 	const char *n2 = e2->name;
   348 
   349 	/* Need to make sure that the contents of a directory
   350 	 * are sorted immediately after it. So "foo/bar" has to
   351 	 * sort before "foo.conf"
   352 	 *
   353 	 * FIXME: this is about 60% slower than strcmp
   354 	 */
   355 	while (*n1 && *n2) {
   356 		if (*n1 < *n2)
   357 			return *n2 == '/' ? 1 : -1;
   358 		else if (*n1 > *n2)
   359 			return *n1 == '/' ? -1 : 1;
   360 		n1++;
   361 		n2++;
   362 	}
   363 	if (*n1)
   364 		return 1;
   365 	else if (*n2)
   366 		return -1;
   367 	else
   368 		return 0;
   369 }
   370 
   371 static void
   372 count_entries(struct import_directory *d)
   373 {
   374 	struct import_directory *p, *end;
   375 
   376 	p = d->files.data;
   377 	end = d->files.data + d->files.size;
   378 	d->count = 0;
   379 	while (p < end) {
   380 		count_entries(p);
   381 		d->count += p->count + 1;
   382 		p++;
   383 	}
   384 }
   385 
   386 static void
   387 serialize_files(struct razor_set *set,
   388 		struct import_directory *d, struct array *array)
   389 {
   390 	struct import_directory *p, *end;
   391 	struct razor_entry *e = NULL;
   392 	uint32_t s;
   393 
   394 	p = d->files.data;
   395 	end = d->files.data + d->files.size;
   396 	s = array->size / sizeof *e + d->files.size / sizeof *p;
   397 	while (p < end) {
   398 		e = array_add(array, sizeof *e);
   399 		e->name = p->name;
   400 		e->flags = 0;
   401 		e->start = p->count > 0 ? s : 0;
   402 		s += p->count;
   403 
   404 		list_set_array(&e->packages, &set->package_pool, &p->packages, 0);
   405 		array_release(&p->packages);
   406 		p++;
   407 	}
   408 	if (e != NULL)
   409 		e->flags |= RAZOR_ENTRY_LAST;
   410 
   411 	p = d->files.data;
   412 	end = d->files.data + d->files.size;
   413 	while (p < end) {
   414 		serialize_files(set, p, array);
   415 		p++;
   416 	}
   417 }
   418 
   419 static void
   420 remap_property_package_links(struct array *properties, uint32_t *rmap)
   421 {
   422 	struct razor_property *p, *end;
   423 
   424 	end = properties->data + properties->size;
   425 	for (p = properties->data; p < end; p++)
   426 		list_remap_head(&p->packages, rmap);
   427 }
   428 
   429 static void
   430 build_file_tree(struct razor_importer *importer)
   431 {
   432 	int count, i, length;
   433 	struct import_entry *filenames;
   434 	char *f, *end;
   435 	uint32_t name, *r, s;
   436 	char rootname[256], dirname[256];
   437 	struct import_directory *d, *last_root;
   438 	struct array roots;
   439 	struct razor_entry *e;
   440 
   441 	count = importer->files.size / sizeof (struct import_entry);
   442 	filenames = importer->files.data;
   443 	razor_qsort_with_data(filenames,
   444 			      count,
   445 			      sizeof (struct import_entry),
   446 			      compare_filenames,
   447 			      NULL);
   448 
   449 	array_init(&roots);
   450 	last_root = NULL;
   451 
   452 	for (i = 0; i < count; i++) {
   453 		f = filenames[i].name;
   454 		d = NULL;
   455 		while (*f) {
   456 			end = strchr(f, '/');
   457 			if (end == NULL)
   458 				end = f + strlen(f);
   459 			length = end - f;
   460 			memcpy(dirname, f, length);
   461 			dirname[length] = '\0';
   462 			name = hashtable_tokenize(&importer->file_table,
   463 						  dirname);
   464 			if (!d) {
   465 				if (!last_root || last_root->name != name) {
   466 					d = array_add(&roots, sizeof *d);
   467 					d->name = name;
   468 					d->last = NULL;
   469 					array_init(&d->files);
   470 					array_init(&d->packages);
   471 					last_root = d;
   472 				}
   473 				d = last_root;
   474 			} else {
   475 				if (!d->last || d->last->name != name) {
   476 					d->last = array_add(&d->files,
   477 							    sizeof *d);
   478 					d->last->name = name;
   479 					d->last->last = NULL;
   480 					array_init(&d->last->files);
   481 					array_init(&d->last->packages);
   482 				}
   483 				d = d->last;
   484 			}
   485 			f = end + 1;
   486 			if (*end == '\0')
   487 				break;
   488 		}
   489 
   490 		r = array_add(&d->packages, sizeof *r);
   491 		*r = filenames[i].package;
   492 		free(filenames[i].name);
   493 	}
   494 
   495 	count = roots.size / sizeof (struct import_directory);
   496 	d = roots.data;
   497 	s = count;
   498 	for (i = 0; i < count; i++) {
   499 		count_entries(d);
   500 		if (i)
   501 			e = array_add(&importer->set->files, sizeof *e);
   502 		else
   503 			e = importer->set->files.data;
   504 		e->name = d->name;
   505 		e->flags = 0;
   506 		e->start = d->count > 0 ? s : 0;
   507 		s += d->count;
   508 		list_set_empty(&e->packages);
   509 		d++;
   510 	}
   511 	if (count)
   512 		e->flags |= RAZOR_ENTRY_LAST;
   513 
   514 	d = roots.data;
   515 	for (i = 0; i < count; i++) {
   516 		serialize_files(importer->set, d, &importer->set->files);
   517 		d++;
   518 	}
   519 
   520 	array_release(&importer->files);
   521 	array_release(&roots);
   522 }
   523 
   524 static void
   525 list_to_array(struct list *list, struct array *array)
   526 {
   527 	uint32_t *item;
   528 
   529 	while (list) {
   530 		 item = array_add(array, sizeof *item);
   531 		 *item = list->data;
   532 		 list = list_next(list);
   533 	}
   534 }
   535 
   536 static int
   537 compare_file_requires(const void *p1, const void *p2, void *data)
   538 {
   539 	uint32_t *f1 = (void *)p1, *f2 = (void *)p2;
   540 	const char *pool = data;
   541 
   542 	return strcmp(&pool[*f1], &pool[*f2]);
   543 }
   544 
   545 static void
   546 find_file_provides(struct razor_importer *importer)
   547 {
   548 	struct razor_property *prop;
   549 	struct razor_entry *top, *entry;
   550 	struct razor_package *packages;
   551 	struct array pkgprops;
   552 	struct list *pkg;
   553 	uint32_t *req, *req_start, *req_end;
   554 	uint32_t *map, *newprop;
   555 	char *pool;
   556 
   557 	pool = importer->set->string_pool.data;
   558 	packages = importer->set->packages.data;
   559 	top = importer->set->files.data;
   560 
   561 	req = req_start = importer->file_requires.data;
   562 	req_end = importer->file_requires.data + importer->file_requires.size;
   563 	map = razor_qsort_with_data(req, req_end - req, sizeof *req,
   564 				    compare_file_requires, pool);
   565 	free(map);
   566 
   567 	for (req = req_start; req < req_end; req++) {
   568 		if (req > req_start && req[0] == req[-1])
   569 			continue;
   570 		entry = razor_set_find_entry(importer->set, top, &pool[*req]);
   571 		if (!entry)
   572 			continue;
   573 
   574 		for (pkg = list_first(&entry->packages, &importer->set->package_pool); pkg; pkg = list_next(pkg)) {
   575 			prop = array_add(&importer->set->properties, sizeof *prop);
   576 			prop->name = *req;
   577 			prop->flags =
   578 				RAZOR_PROPERTY_PROVIDES | RAZOR_PROPERTY_EQUAL;
   579 			prop->version = hashtable_tokenize(&importer->table, "");
   580 			list_set_ptr(&prop->packages, pkg->data);
   581 
   582 			/* Update property list of pkg */
   583 			array_init(&pkgprops);
   584 			list_to_array(list_first(&packages[pkg->data].properties, &importer->set->property_pool), &pkgprops);
   585 			newprop = array_add(&pkgprops, sizeof *newprop);
   586 			*newprop = prop - (struct razor_property *)importer->set->properties.data;
   587 			list_set_array(&packages[pkg->data].properties, &importer->set->property_pool, &pkgprops, 1);
   588 			array_release(&pkgprops);
   589 		}
   590 	}
   591 
   592 	array_release(&importer->file_requires);
   593 }
   594 
   595 static void
   596 build_package_file_lists(struct razor_set *set, uint32_t *rmap)
   597 {
   598 	struct razor_package *p, *packages;
   599 	struct array *pkgs;
   600 	struct razor_entry *e, *end;
   601 	struct list *r;
   602 	uint32_t *q;
   603 	int i, count;
   604 
   605 	count = set->packages.size / sizeof *p;
   606 	pkgs = zalloc(count * sizeof *pkgs);
   607 
   608 	end = set->files.data + set->files.size;
   609 	for (e = set->files.data; e < end; e++) {
   610 		list_remap_head(&e->packages, rmap);
   611 		r = list_first(&e->packages, &set->package_pool);
   612 		while (r) {
   613 			q = array_add(&pkgs[r->data], sizeof *q);
   614 			*q = e - (struct razor_entry *) set->files.data;
   615 			r = list_next(r);
   616 		}
   617 	}
   618 
   619 	packages = set->packages.data;
   620 	for (i = 0; i < count; i++) {
   621 		list_set_array(&packages[i].files, &set->file_pool, &pkgs[i], 0);
   622 		array_release(&pkgs[i]);
   623 	}
   624 	free(pkgs);
   625 }
   626 
   627 /**
   628  * razor_importer_finish:
   629  * @importer: the %razor_importer
   630  *
   631  * Finish importing packages and create the package set.  This sorts
   632  * and indexes all the packages, properties and files in the importer
   633  * and creates a new %razor_set.  After creating the new package set,
   634  * the importer is destroyed.
   635  *
   636  * Returns: the new %razor_set
   637  **/
   638 RAZOR_EXPORT struct razor_set *
   639 razor_importer_finish(struct razor_importer *importer)
   640 {
   641 	struct razor_set *set;
   642 	uint32_t *map, *rmap;
   643 	int i, count;
   644 
   645 	build_file_tree(importer);
   646 	find_file_provides(importer);
   647 
   648 	map = uniqueify_properties(importer->set);
   649 	list_remap_pool(&importer->set->property_pool, map);
   650 	free(map);
   651 
   652 	count = importer->set->packages.size / sizeof(struct razor_package);
   653 	map = razor_qsort_with_data(importer->set->packages.data,
   654 				    count,
   655 				    sizeof(struct razor_package),
   656 				    compare_packages,
   657 				    importer->set);
   658 
   659 	rmap = malloc(count * sizeof *rmap);
   660 	for (i = 0; i < count; i++)
   661 		rmap[map[i]] = i;
   662 	free(map);
   663 
   664 	list_remap_pool(&importer->set->package_pool, rmap);
   665 	build_package_file_lists(importer->set, rmap);
   666 	remap_property_package_links(&importer->set->properties, rmap);
   667 	free(rmap);
   668 
   669 	set = importer->set;
   670 	hashtable_release(&importer->table);
   671 	hashtable_release(&importer->details_table);
   672 	hashtable_release(&importer->file_table);
   673 	free(importer);
   674 
   675 	return set;
   676 }