librazor/razor.c
author Kristian H?gsberg <krh@redhat.com>
Fri Jun 20 23:13:09 2008 -0400 (2008-06-20)
changeset 257 0c3db660514d
parent 253 338a577cdfd2
child 259 5b0601d184ed
permissions -rw-r--r--
When uniquifying properties, also sort them on the owning package.

This ensures that whenever two packages provide or (or require, obsolete
or conflict) the same property, they appear in the same order in the
propertys list of packages.
     1 /*
     2  * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
     3  * Copyright (C) 2008  Red Hat, Inc
     4  *
     5  * This program is free software; you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation; either version 2 of the License, or
     8  * (at your option) any later version.
     9  *
    10  * This program is distributed in the hope that it will be useful,
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  * GNU General Public License for more details.
    14  *
    15  * You should have received a copy of the GNU General Public License along
    16  * with this program; if not, write to the Free Software Foundation, Inc.,
    17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    18  */
    19 
    20 #define _GNU_SOURCE
    21 
    22 #include <stdlib.h>
    23 #include <stddef.h>
    24 #include <stdint.h>
    25 #include <stdio.h>
    26 #include <string.h>
    27 #include <sys/types.h>
    28 #include <sys/stat.h>
    29 #include <sys/mman.h>
    30 #include <unistd.h>
    31 #include <fcntl.h>
    32 #include <errno.h>
    33 #include <ctype.h>
    34 #include <fnmatch.h>
    35 
    36 #include "razor-internal.h"
    37 #include "razor.h"
    38 
    39 void *
    40 zalloc(size_t size)
    41 {
    42 	void *p;
    43 
    44 	p = malloc(size);
    45 	memset(p, 0, size);
    46 
    47 	return p;
    48 }
    49 
    50 struct razor_set_section razor_sections[] = {
    51 	{ RAZOR_STRING_POOL,	offsetof(struct razor_set, string_pool) },
    52 	{ RAZOR_PACKAGES,	offsetof(struct razor_set, packages) },
    53 	{ RAZOR_PROPERTIES,	offsetof(struct razor_set, properties) },
    54 	{ RAZOR_FILES,		offsetof(struct razor_set, files) },
    55 	{ RAZOR_PACKAGE_POOL,	offsetof(struct razor_set, package_pool) },
    56 	{ RAZOR_PROPERTY_POOL,	offsetof(struct razor_set, property_pool) },
    57 	{ RAZOR_FILE_POOL,	offsetof(struct razor_set, file_pool) },
    58 };
    59 
    60 struct razor_set *
    61 razor_set_create(void)
    62 {
    63 	struct razor_set *set;
    64 	struct razor_entry *e;
    65 	char *empty;
    66 
    67 	set = zalloc(sizeof *set);
    68 
    69 	e = array_add(&set->files, sizeof *e);
    70 	empty = array_add(&set->string_pool, 1);
    71 	*empty = '\0';
    72 	e->name = 0;
    73 	e->flags = RAZOR_ENTRY_LAST;
    74 	e->start = 0;
    75 	list_set_empty(&e->packages);
    76 
    77 	return set;
    78 }
    79 
    80 struct razor_set *
    81 razor_set_open(const char *filename)
    82 {
    83 	struct razor_set *set;
    84 	struct razor_set_section *s;
    85 	struct stat stat;
    86 	struct array *array;
    87 	int fd;
    88 
    89 	set = zalloc(sizeof *set);
    90 	fd = open(filename, O_RDONLY);
    91 	if (fstat(fd, &stat) < 0)
    92 		return NULL;
    93 	set->header = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    94 	if (set->header == MAP_FAILED) {
    95 		free(set);
    96 		return NULL;
    97 	}
    98 
    99 	for (s = set->header->sections; ~s->type; s++) {
   100 		if (s->type >= ARRAY_SIZE(razor_sections))
   101 			continue;
   102 		if (s->type != razor_sections[s->type].type)
   103 			continue;
   104 		array = (void *) set + razor_sections[s->type].offset;
   105 		array->data = (void *) set->header + s->offset;
   106 		array->size = s->size;
   107 		array->alloc = s->size;
   108 	}
   109 	close(fd);
   110 
   111 	return set;
   112 }
   113 
   114 void
   115 razor_set_destroy(struct razor_set *set)
   116 {
   117 	unsigned int size;
   118 	struct array *a;
   119 	int i;
   120 
   121 	if (set->header) {
   122 		for (i = 0; set->header->sections[i].type; i++)
   123 			;
   124 		size = set->header->sections[i].type;
   125 		munmap(set->header, size);
   126 	} else {
   127 		for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
   128 			a = (void *) set + razor_sections[i].offset;
   129 			free(a->data);
   130 		}
   131 	}
   132 
   133 	free(set);
   134 }
   135 
   136 int
   137 razor_set_write_to_fd(struct razor_set *set, int fd)
   138 {
   139 	char data[4096];
   140 	struct razor_set_header *header = (struct razor_set_header *) data;
   141 	struct array *a;
   142 	uint32_t offset;
   143 	int i;
   144 
   145 	memset(data, 0, sizeof data);
   146 	header->magic = RAZOR_MAGIC;
   147 	header->version = RAZOR_VERSION;
   148 	offset = sizeof data;
   149 
   150 	for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
   151 		if (razor_sections[i].type != i)
   152 			continue;
   153 		a = (void *) set + razor_sections[i].offset;
   154 		header->sections[i].type = i;
   155 		header->sections[i].offset = offset;
   156 		header->sections[i].size = a->size;
   157 		offset += ALIGN(a->size, 4096);
   158 	}
   159 
   160 	header->sections[i].type = ~0;
   161 	header->sections[i].offset = 0;
   162 	header->sections[i].size = 0;
   163 
   164 	razor_write(fd, data, sizeof data);
   165 	memset(data, 0, sizeof data);
   166 	for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
   167 		if (razor_sections[i].type != i)
   168 			continue;
   169 		a = (void *) set + razor_sections[i].offset;
   170 		razor_write(fd, a->data, a->size);
   171 		razor_write(fd, data, ALIGN(a->size, 4096) - a->size);
   172 	}
   173 
   174 	return 0;
   175 }
   176 
   177 int
   178 razor_set_write(struct razor_set *set, const char *filename)
   179 {
   180 	int fd, status;
   181 
   182 	fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);
   183 	if (fd < 0)
   184 		return -1;
   185 
   186 	status = razor_set_write_to_fd(set, fd);
   187 	if (status) {
   188 	    close(fd);
   189 	    return status;
   190 	}
   191 
   192 	return close(fd);
   193 }
   194 
   195 void
   196 razor_build_evr(char *evr_buf, int size, const char *epoch,
   197 		const char *version, const char *release)
   198 {
   199 	int len;
   200 
   201 	if (!version || !*version) {
   202 		*evr_buf = '\0';
   203 		return;
   204 	}
   205 
   206 	if (epoch && *epoch && strcmp(epoch, "0") != 0) {
   207 		len = snprintf(evr_buf, size, "%s:", epoch);
   208 		evr_buf += len;
   209 		size -= len;
   210 	}
   211 	len = snprintf(evr_buf, size, "%s", version);
   212 	evr_buf += len;
   213 	size -= len;
   214 	if (release && *release)
   215 		snprintf(evr_buf, size, "-%s", release);
   216 }
   217 
   218 int
   219 razor_versioncmp(const char *s1, const char *s2)
   220 {
   221 	const char *p1, *p2;
   222 	long n1, n2;
   223 	int res;
   224 
   225 	n1 = strtol(s1, (char **) &p1, 10);
   226 	n2 = strtol(s2, (char **) &p2, 10);
   227 
   228 	/* Epoch; if one but not the other has an epoch set, default
   229 	 * the epoch-less version to 0. */
   230 	res = (*p1 == ':') - (*p2 == ':');
   231 	if (res < 0) {
   232 		n1 = 0;
   233 		p1 = s1;
   234 		p2++;
   235 	} else if (res > 0) {
   236 		p1++;
   237 		n2 = 0;
   238 		p2 = s2;
   239 	}
   240 
   241 	if (n1 != n2)
   242 		return n1 - n2;
   243 	while (*p1 && *p2) {
   244 		if (*p1 != *p2)
   245 			return *p1 - *p2;
   246 		p1++;
   247 		p2++;
   248 		if (isdigit(*p1) && isdigit(*p2))
   249 			return razor_versioncmp(p1, p2);
   250 	}
   251 
   252 	return *p1 - *p2;
   253 }
   254 
   255 struct razor_package *
   256 razor_set_get_package(struct razor_set *set, const char *package)
   257 {
   258 	struct razor_package_iterator *pi;
   259 	struct razor_package *p;
   260 	const char *name, *version, *arch;
   261 
   262 	pi = razor_package_iterator_create(set);
   263 	while (razor_package_iterator_next(pi, &p, &name, &version, &arch)) {
   264 		if (strcmp(package, name) == 0)
   265 			break;
   266 	}
   267 	razor_package_iterator_destroy(pi);
   268 
   269 	return p;
   270 }
   271 
   272 struct razor_entry *
   273 razor_set_find_entry(struct razor_set *set,
   274 		     struct razor_entry *dir, const char *pattern)
   275 {
   276 	struct razor_entry *e;
   277 	const char *n, *pool = set->string_pool.data;
   278 	int len;
   279 
   280 	e = (struct razor_entry *) set->files.data + dir->start;
   281 	do {
   282 		n = pool + e->name;
   283 		if (strcmp(pattern + 1, n) == 0)
   284 			return e;
   285 		len = strlen(n);
   286 		if (e->start != 0 && strncmp(pattern + 1, n, len) == 0 &&
   287 		    pattern[len + 1] == '/') {
   288 			return razor_set_find_entry(set, e, pattern + len + 1);
   289 		}
   290 	} while (!((e++)->flags & RAZOR_ENTRY_LAST));
   291 
   292 	return NULL;
   293 }
   294 
   295 static void
   296 list_dir(struct razor_set *set, struct razor_entry *dir,
   297 	 char *prefix, const char *pattern)
   298 {
   299 	struct razor_entry *e;
   300 	const char *n, *pool = set->string_pool.data;
   301 
   302 	e = (struct razor_entry *) set->files.data + dir->start;
   303 	do {
   304 		n = pool + e->name;
   305 		if (pattern && pattern[0] && fnmatch(pattern, n, 0) != 0)
   306 			continue;
   307 		printf("%s/%s\n", prefix, n);
   308 		if (e->start) {
   309 			char *sub = prefix + strlen (prefix);
   310 			*sub = '/';
   311 			strcpy (sub + 1, n);
   312 			list_dir(set, e, prefix, pattern);
   313 			*sub = '\0';
   314 		}
   315 	} while (!((e++)->flags & RAZOR_ENTRY_LAST));
   316 }
   317 
   318 void
   319 razor_set_list_files(struct razor_set *set, const char *pattern)
   320 {
   321 	struct razor_entry *e;
   322 	char buffer[512], *p, *base;
   323 
   324 	if (pattern == NULL || !strcmp (pattern, "/")) {
   325 		buffer[0] = '\0';
   326 		list_dir(set, set->files.data, buffer, NULL);
   327 		return;
   328 	}
   329 
   330 	strcpy(buffer, pattern);
   331 	e = razor_set_find_entry(set, set->files.data, buffer);
   332 	if (e && e->start > 0) {
   333 		base = NULL;
   334 	} else {
   335 		p = strrchr(buffer, '/');
   336 		if (p) {
   337 			*p = '\0';
   338 			base = p + 1;
   339 		} else {
   340 			base = NULL;
   341 		}
   342 	}
   343 	e = razor_set_find_entry(set, set->files.data, buffer);
   344 	if (e->start != 0)
   345 		list_dir(set, e, buffer, base);
   346 }
   347 
   348 static struct list *
   349 list_package_files(struct razor_set *set, struct list *r,
   350 		   struct razor_entry *dir, uint32_t end,
   351 		   char *prefix)
   352 {
   353 	struct razor_entry *e, *f, *entries;
   354 	uint32_t next, file;
   355 	char *pool;
   356 	int len;
   357 
   358 	entries = (struct razor_entry *) set->files.data;
   359 	pool = set->string_pool.data;
   360 
   361 	e = entries + dir->start;
   362 	do {
   363 		if (entries + r->data == e) {
   364 			printf("%s/%s\n", prefix, pool + e->name);
   365 			r = list_next(r);
   366 			if (!r)
   367 				return NULL;
   368 			if (r->data >= end)
   369 				return r;
   370 		}
   371 	} while (!((e++)->flags & RAZOR_ENTRY_LAST));
   372 
   373 	e = entries + dir->start;
   374 	do {
   375 		if (e->start == 0)
   376 			continue;
   377 
   378 		if (e->flags & RAZOR_ENTRY_LAST)
   379 			next = end;
   380 		else {
   381 			f = e + 1;
   382 			while (f->start == 0 && !(f->flags & RAZOR_ENTRY_LAST))
   383 				f++;
   384 			if (f->start == 0)
   385 				next = end;
   386 			else
   387 				next = f->start;
   388 		}
   389 
   390 		file = r->data;
   391 		if (e->start <= file && file < next) {
   392 			len = strlen(prefix);
   393 			prefix[len] = '/';
   394 			strcpy(prefix + len + 1, pool + e->name);
   395 			r = list_package_files(set, r, e, next, prefix);
   396 			prefix[len] = '\0';
   397 		}
   398 	} while (!((e++)->flags & RAZOR_ENTRY_LAST) && r != NULL);
   399 
   400 	return r;
   401 }
   402 
   403 void
   404 razor_set_list_package_files(struct razor_set *set, const char *name)
   405 {
   406 	struct razor_package *package;
   407 	struct list *r;
   408 	uint32_t end;
   409 	char buffer[512];
   410 
   411 	package = razor_set_get_package(set, name);
   412 
   413 	r = list_first(&package->files, &set->file_pool);
   414 	end = set->files.size / sizeof (struct razor_entry);
   415 	buffer[0] = '\0';
   416 	list_package_files(set, r, set->files.data, end, buffer);
   417 }
   418 
   419 /* The diff order matters.  We should sort the packages so that a
   420  * REMOVE of a package comes before the INSTALL, and so that all
   421  * requires for a package have been installed before the package.
   422  **/
   423 
   424 void
   425 razor_set_diff(struct razor_set *set, struct razor_set *upstream,
   426 	       razor_diff_callback_t callback, void *data)
   427 {
   428  	struct razor_package_iterator *pi1, *pi2;
   429  	struct razor_package *p1, *p2;
   430 	const char *name1, *name2, *version1, *version2, *arch1, *arch2;
   431 	int res;
   432 
   433 	pi1 = razor_package_iterator_create(set);
   434 	pi2 = razor_package_iterator_create(upstream);
   435 
   436 	razor_package_iterator_next(pi1, &p1, &name1, &version1, &arch1);
   437 	razor_package_iterator_next(pi2, &p2, &name2, &version2, &arch2);
   438 
   439 	while (p1 || p2) {
   440 		if (p1 && p2) {
   441 			res = strcmp(name1, name2);
   442 			if (res == 0)
   443 				res = razor_versioncmp(version1, version2);
   444 		} else {
   445 			res = 0;
   446 		}
   447 
   448 		if (p2 == NULL || res < 0)
   449 			callback(RAZOR_DIFF_ACTION_REMOVE,
   450 				 p1, name1, version1, arch1, data);
   451 		else if (p1 == NULL || res > 0)
   452 			callback(RAZOR_DIFF_ACTION_ADD,
   453 				 p2, name2, version2, arch2, data);
   454 
   455 		if (p1 != NULL && res <= 0)
   456 			razor_package_iterator_next(pi1, &p1,
   457 						    &name1, &version1, &arch1);
   458 		if (p2 != NULL && res >= 0)
   459 			razor_package_iterator_next(pi2, &p2,
   460 						    &name2, &version2, &arch2);
   461 	}
   462 
   463 	razor_package_iterator_destroy(pi1);
   464 	razor_package_iterator_destroy(pi2);
   465 }
   466 
   467 static void
   468 add_new_package(enum razor_diff_action action,
   469 		struct razor_package *package,
   470 		const char *name,
   471 		const char *version,
   472 		const char *arch,
   473 		void *data)
   474 {
   475 	if (action == RAZOR_DIFF_ACTION_ADD)
   476 		razor_package_query_add_package(data, package);
   477 }
   478 
   479 struct razor_package_iterator *
   480 razor_set_create_remove_iterator(struct razor_set *set,
   481 				 struct razor_set *next)
   482 {
   483 	struct razor_package_query *query;
   484 	struct razor_package_iterator *pi;
   485 
   486 	query = razor_package_query_create(set);
   487 	razor_set_diff(next, set, add_new_package, query);
   488 
   489 	pi = razor_package_query_finish(query);
   490 
   491 	/* FIXME: We need to figure out the right install order here,
   492 	 * so the post and pre scripts can run. */
   493 
   494 	/* sort */
   495 
   496 	return pi;
   497 }
   498 
   499 struct razor_package_iterator *
   500 razor_set_create_install_iterator(struct razor_set *set,
   501 				  struct razor_set *next)
   502 {
   503 	struct razor_package_query *query;
   504 	struct razor_package_iterator *pi;
   505 
   506 	query = razor_package_query_create(next);
   507 	razor_set_diff(set, next, add_new_package, query);
   508 
   509 	pi = razor_package_query_finish(query);
   510 
   511 	/* FIXME: We need to figure out the right install order here,
   512 	 * so the post and pre scripts can run. */
   513 
   514 	/* sort */
   515 
   516 	return pi;
   517 }