librazor/razor.c
author Kristian H?gsberg <krh@redhat.com>
Mon Jun 23 09:59:08 2008 -0400 (2008-06-23)
changeset 259 5b0601d184ed
parent 254 ccb1c11968ab
parent 258 29d5002bd17f
child 262 63644cc28e0b
permissions -rw-r--r--
Merge commit 'jbowes/master'
     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_PACKAGE_POOL,	offsetof(struct razor_set, package_pool) },
    55 	{ RAZOR_PROPERTY_POOL,	offsetof(struct razor_set, property_pool) },
    56 };
    57 
    58 struct razor_set_section razor_files_sections[] = {
    59 	{ RAZOR_FILES,			offsetof(struct razor_set, files) },
    60 	{ RAZOR_FILE_POOL,		offsetof(struct razor_set, file_pool) },
    61 	{ RAZOR_FILE_STRING_POOL,	offsetof(struct razor_set, file_string_pool) },
    62 };
    63 
    64 struct razor_set_section razor_details_sections[] = {
    65 	{ RAZOR_DETAILS_STRING_POOL,	offsetof(struct razor_set, details_string_pool) },
    66 };
    67 struct razor_set *
    68 razor_set_create(void)
    69 {
    70 	struct razor_set *set;
    71 	struct razor_entry *e;
    72 	char *empty;
    73 
    74 	set = zalloc(sizeof *set);
    75 
    76 	e = array_add(&set->files, sizeof *e);
    77 	empty = array_add(&set->string_pool, 1);
    78 	*empty = '\0';
    79 	e->name = 0;
    80 	e->flags = RAZOR_ENTRY_LAST;
    81 	e->start = 0;
    82 	list_set_empty(&e->packages);
    83 
    84 	return set;
    85 }
    86 
    87 struct razor_set *
    88 razor_set_open(const char *filename)
    89 {
    90 	struct razor_set *set;
    91 	struct razor_set_section *s;
    92 	struct stat stat;
    93 	struct array *array;
    94 	int fd;
    95 
    96 	set = zalloc(sizeof *set);
    97 	fd = open(filename, O_RDONLY);
    98 	if (fstat(fd, &stat) < 0)
    99 		return NULL;
   100 	set->header = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
   101 	if (set->header == MAP_FAILED) {
   102 		free(set);
   103 		return NULL;
   104 	}
   105 
   106 	for (s = set->header->sections; ~s->type; s++) {
   107 		if (s->type >= ARRAY_SIZE(razor_sections))
   108 			continue;
   109 		if (s->type != razor_sections[s->type].type)
   110 			continue;
   111 		array = (void *) set + razor_sections[s->type].offset;
   112 		array->data = (void *) set->header + s->offset;
   113 		array->size = s->size;
   114 		array->alloc = s->size;
   115 	}
   116 	close(fd);
   117 
   118 	return set;
   119 }
   120 
   121 void
   122 razor_set_open_details(struct razor_set *set, const char *filename)
   123 {
   124 	struct razor_set_section *s;
   125 	struct stat stat;
   126 	struct array *array;
   127 	int fd;
   128 
   129 	fd = open(filename, O_RDONLY);
   130 	if (fstat(fd, &stat) < 0)
   131 		return;
   132 	set->details_header = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
   133 	if (set->details_header == MAP_FAILED)
   134 		return;
   135 
   136 	for (s = set->details_header->sections; ~s->type; s++) {
   137 		if (s->type >= ARRAY_SIZE(razor_details_sections))
   138 			continue;
   139 		if (s->type != razor_details_sections[s->type].type)
   140 			continue;
   141 		array = (void *) set + razor_details_sections[s->type].offset;
   142 		array->data = (void *) set->details_header + s->offset;
   143 		array->size = s->size;
   144 		array->alloc = s->size;
   145 	}
   146 	close(fd);
   147 }
   148 
   149 void
   150 razor_set_open_files(struct razor_set *set, const char *filename)
   151 {
   152 	struct razor_set_section *s;
   153 	struct stat stat;
   154 	struct array *array;
   155 	int fd;
   156 
   157 	fd = open(filename, O_RDONLY);
   158 	if (fstat(fd, &stat) < 0)
   159 		return;
   160 	set->files_header = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
   161 	if (set->files_header == MAP_FAILED)
   162 		return;
   163 
   164 	for (s = set->files_header->sections; ~s->type; s++) {
   165 		if (s->type >= ARRAY_SIZE(razor_files_sections))
   166 			continue;
   167 		if (s->type != razor_files_sections[s->type].type)
   168 			continue;
   169 		array = (void *) set + razor_files_sections[s->type].offset;
   170 		array->data = (void *) set->files_header + s->offset;
   171 		array->size = s->size;
   172 		array->alloc = s->size;
   173 	}
   174 	close(fd);
   175 }
   176 
   177 void
   178 razor_set_destroy(struct razor_set *set)
   179 {
   180 	unsigned int size;
   181 	struct array *a;
   182 	int i;
   183 
   184 	if (set->header) {
   185 		for (i = 0; set->header->sections[i].type; i++)
   186 			;
   187 		size = set->header->sections[i].type;
   188 		munmap(set->header, size);
   189 	} else {
   190 		for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
   191 			a = (void *) set + razor_sections[i].offset;
   192 			free(a->data);
   193 		}
   194 	}
   195 
   196 	if (set->details_header) {
   197 		for (i = 0; set->details_header->sections[i].type; i++)
   198 			;
   199 		size = set->details_header->sections[i].type;
   200 		munmap(set->details_header, size);
   201 	} else {
   202 		for (i = 0; i < ARRAY_SIZE(razor_details_sections); i++) {
   203 			a = (void *) set + razor_details_sections[i].offset;
   204 			free(a->data);
   205 		}
   206 	}
   207 
   208 	if (set->files_header) {
   209 		for (i = 0; set->files_header->sections[i].type; i++)
   210 			;
   211 		size = set->files_header->sections[i].type;
   212 		munmap(set->files_header, size);
   213 	} else {
   214 		for (i = 0; i < ARRAY_SIZE(razor_files_sections); i++) {
   215 			a = (void *) set + razor_files_sections[i].offset;
   216 			free(a->data);
   217 		}
   218 	}
   219 
   220 	free(set);
   221 }
   222 
   223 static int
   224 razor_set_write_sections_to_fd(struct razor_set *set, int fd, int magic,
   225 			       struct razor_set_section *sections,
   226 			       size_t array_size)
   227 {
   228 	char data[4096];
   229 	struct razor_set_header *header = (struct razor_set_header *) data;
   230 	struct array *a;
   231 	uint32_t offset;
   232 	int i;
   233 
   234 	memset(data, 0, sizeof data);
   235 	header->magic = magic;
   236 	header->version = RAZOR_VERSION;
   237 	offset = sizeof data;
   238 
   239 	for (i = 0; i < array_size; i++) {
   240 		if (sections[i].type != i)
   241 			continue;
   242 		a = (void *) set + sections[i].offset;
   243 		header->sections[i].type = i;
   244 		header->sections[i].offset = offset;
   245 		header->sections[i].size = a->size;
   246 		offset += ALIGN(a->size, 4096);
   247 	}
   248 
   249 	header->sections[i].type = ~0;
   250 	header->sections[i].offset = 0;
   251 	header->sections[i].size = 0;
   252 
   253 	razor_write(fd, data, sizeof data);
   254 	memset(data, 0, sizeof data);
   255 	for (i = 0; i < array_size; i++) {
   256 		if (sections[i].type != i)
   257 			continue;
   258 		a = (void *) set + sections[i].offset;
   259 		razor_write(fd, a->data, a->size);
   260 		razor_write(fd, data, ALIGN(a->size, 4096) - a->size);
   261 	}
   262 
   263 	return 0;
   264 }
   265 
   266 int
   267 razor_set_write_to_fd(struct razor_set *set, int fd,
   268 		      enum razor_repo_file_type type)
   269 {
   270 	switch (type) {
   271 	case RAZOR_REPO_FILE_MAIN:
   272 		return razor_set_write_sections_to_fd(set, fd, RAZOR_MAGIC,
   273 						      razor_sections,
   274 						      ARRAY_SIZE(razor_sections));
   275 
   276 	case RAZOR_REPO_FILE_DETAILS:
   277 		return razor_set_write_sections_to_fd(set, fd, RAZOR_DETAILS_MAGIC,
   278 						      razor_details_sections,
   279 						      ARRAY_SIZE(razor_details_sections));
   280 	case RAZOR_REPO_FILE_FILES:
   281 		return razor_set_write_sections_to_fd(set, fd, RAZOR_FILES_MAGIC,
   282 						      razor_files_sections,
   283 						      ARRAY_SIZE(razor_files_sections));
   284 	default:
   285 		return -1;
   286 	}
   287 }
   288 
   289 int
   290 razor_set_write(struct razor_set *set, const char *filename,
   291 		enum razor_repo_file_type type)
   292 {
   293 	int fd, status;
   294 
   295 	fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);
   296 	if (fd < 0)
   297 		return -1;
   298 
   299 	status = razor_set_write_to_fd(set, fd, type);
   300 	if (status) {
   301 	    close(fd);
   302 	    return status;
   303 	}
   304 
   305 	return close(fd);
   306 }
   307 void
   308 razor_build_evr(char *evr_buf, int size, const char *epoch,
   309 		const char *version, const char *release)
   310 {
   311 	int len;
   312 
   313 	if (!version || !*version) {
   314 		*evr_buf = '\0';
   315 		return;
   316 	}
   317 
   318 	if (epoch && *epoch && strcmp(epoch, "0") != 0) {
   319 		len = snprintf(evr_buf, size, "%s:", epoch);
   320 		evr_buf += len;
   321 		size -= len;
   322 	}
   323 	len = snprintf(evr_buf, size, "%s", version);
   324 	evr_buf += len;
   325 	size -= len;
   326 	if (release && *release)
   327 		snprintf(evr_buf, size, "-%s", release);
   328 }
   329 
   330 int
   331 razor_versioncmp(const char *s1, const char *s2)
   332 {
   333 	const char *p1, *p2;
   334 	long n1, n2;
   335 	int res;
   336 
   337 	n1 = strtol(s1, (char **) &p1, 10);
   338 	n2 = strtol(s2, (char **) &p2, 10);
   339 
   340 	/* Epoch; if one but not the other has an epoch set, default
   341 	 * the epoch-less version to 0. */
   342 	res = (*p1 == ':') - (*p2 == ':');
   343 	if (res < 0) {
   344 		n1 = 0;
   345 		p1 = s1;
   346 		p2++;
   347 	} else if (res > 0) {
   348 		p1++;
   349 		n2 = 0;
   350 		p2 = s2;
   351 	}
   352 
   353 	if (n1 != n2)
   354 		return n1 - n2;
   355 	while (*p1 && *p2) {
   356 		if (*p1 != *p2)
   357 			return *p1 - *p2;
   358 		p1++;
   359 		p2++;
   360 		if (isdigit(*p1) && isdigit(*p2))
   361 			return razor_versioncmp(p1, p2);
   362 	}
   363 
   364 	return *p1 - *p2;
   365 }
   366 
   367 struct razor_package *
   368 razor_set_get_package(struct razor_set *set, const char *package)
   369 {
   370 	struct razor_package_iterator *pi;
   371 	struct razor_package *p;
   372 	const char *name, *version, *arch;
   373 
   374 	pi = razor_package_iterator_create(set);
   375 	while (razor_package_iterator_next(pi, &p, &name, &version, &arch)) {
   376 		if (strcmp(package, name) == 0)
   377 			break;
   378 	}
   379 	razor_package_iterator_destroy(pi);
   380 
   381 	return p;
   382 }
   383 
   384 void
   385 razor_package_get_details(struct razor_set *set, struct razor_package *package,
   386 			  const char **summary, const char **description,
   387 			  const char **url, const char **license)
   388 {
   389 	const char *pool = set->details_string_pool.data;
   390 
   391 	*summary = &pool[package->summary];
   392 	*description = &pool[package->description];
   393 	*url = &pool[package->url];
   394 	*license = &pool[package->license];
   395 }
   396 
   397 struct razor_entry *
   398 razor_set_find_entry(struct razor_set *set,
   399 		     struct razor_entry *dir, const char *pattern)
   400 {
   401 	struct razor_entry *e;
   402 	const char *n, *pool = set->string_pool.data;
   403 	int len;
   404 
   405 	e = (struct razor_entry *) set->files.data + dir->start;
   406 	do {
   407 		n = pool + e->name;
   408 		if (strcmp(pattern + 1, n) == 0)
   409 			return e;
   410 		len = strlen(n);
   411 		if (e->start != 0 && strncmp(pattern + 1, n, len) == 0 &&
   412 		    pattern[len + 1] == '/') {
   413 			return razor_set_find_entry(set, e, pattern + len + 1);
   414 		}
   415 	} while (!((e++)->flags & RAZOR_ENTRY_LAST));
   416 
   417 	return NULL;
   418 }
   419 
   420 static void
   421 list_dir(struct razor_set *set, struct razor_entry *dir,
   422 	 char *prefix, const char *pattern)
   423 {
   424 	struct razor_entry *e;
   425 	const char *n, *pool = set->string_pool.data;
   426 
   427 	e = (struct razor_entry *) set->files.data + dir->start;
   428 	do {
   429 		n = pool + e->name;
   430 		if (pattern && pattern[0] && fnmatch(pattern, n, 0) != 0)
   431 			continue;
   432 		printf("%s/%s\n", prefix, n);
   433 		if (e->start) {
   434 			char *sub = prefix + strlen (prefix);
   435 			*sub = '/';
   436 			strcpy (sub + 1, n);
   437 			list_dir(set, e, prefix, pattern);
   438 			*sub = '\0';
   439 		}
   440 	} while (!((e++)->flags & RAZOR_ENTRY_LAST));
   441 }
   442 
   443 void
   444 razor_set_list_files(struct razor_set *set, const char *pattern)
   445 {
   446 	struct razor_entry *e;
   447 	char buffer[512], *p, *base;
   448 
   449 	if (pattern == NULL || !strcmp (pattern, "/")) {
   450 		buffer[0] = '\0';
   451 		list_dir(set, set->files.data, buffer, NULL);
   452 		return;
   453 	}
   454 
   455 	strcpy(buffer, pattern);
   456 	e = razor_set_find_entry(set, set->files.data, buffer);
   457 	if (e && e->start > 0) {
   458 		base = NULL;
   459 	} else {
   460 		p = strrchr(buffer, '/');
   461 		if (p) {
   462 			*p = '\0';
   463 			base = p + 1;
   464 		} else {
   465 			base = NULL;
   466 		}
   467 	}
   468 	e = razor_set_find_entry(set, set->files.data, buffer);
   469 	if (e->start != 0)
   470 		list_dir(set, e, buffer, base);
   471 }
   472 
   473 static struct list *
   474 list_package_files(struct razor_set *set, struct list *r,
   475 		   struct razor_entry *dir, uint32_t end,
   476 		   char *prefix)
   477 {
   478 	struct razor_entry *e, *f, *entries;
   479 	uint32_t next, file;
   480 	char *pool;
   481 	int len;
   482 
   483 	entries = (struct razor_entry *) set->files.data;
   484 	pool = set->string_pool.data;
   485 
   486 	e = entries + dir->start;
   487 	do {
   488 		if (entries + r->data == e) {
   489 			printf("%s/%s\n", prefix, pool + e->name);
   490 			r = list_next(r);
   491 			if (!r)
   492 				return NULL;
   493 			if (r->data >= end)
   494 				return r;
   495 		}
   496 	} while (!((e++)->flags & RAZOR_ENTRY_LAST));
   497 
   498 	e = entries + dir->start;
   499 	do {
   500 		if (e->start == 0)
   501 			continue;
   502 
   503 		if (e->flags & RAZOR_ENTRY_LAST)
   504 			next = end;
   505 		else {
   506 			f = e + 1;
   507 			while (f->start == 0 && !(f->flags & RAZOR_ENTRY_LAST))
   508 				f++;
   509 			if (f->start == 0)
   510 				next = end;
   511 			else
   512 				next = f->start;
   513 		}
   514 
   515 		file = r->data;
   516 		if (e->start <= file && file < next) {
   517 			len = strlen(prefix);
   518 			prefix[len] = '/';
   519 			strcpy(prefix + len + 1, pool + e->name);
   520 			r = list_package_files(set, r, e, next, prefix);
   521 			prefix[len] = '\0';
   522 		}
   523 	} while (!((e++)->flags & RAZOR_ENTRY_LAST) && r != NULL);
   524 
   525 	return r;
   526 }
   527 
   528 void
   529 razor_set_list_package_files(struct razor_set *set, const char *name)
   530 {
   531 	struct razor_package *package;
   532 	struct list *r;
   533 	uint32_t end;
   534 	char buffer[512];
   535 
   536 	package = razor_set_get_package(set, name);
   537 
   538 	r = list_first(&package->files, &set->file_pool);
   539 	end = set->files.size / sizeof (struct razor_entry);
   540 	buffer[0] = '\0';
   541 	list_package_files(set, r, set->files.data, end, buffer);
   542 }
   543 
   544 /* The diff order matters.  We should sort the packages so that a
   545  * REMOVE of a package comes before the INSTALL, and so that all
   546  * requires for a package have been installed before the package.
   547  **/
   548 
   549 void
   550 razor_set_diff(struct razor_set *set, struct razor_set *upstream,
   551 	       razor_diff_callback_t callback, void *data)
   552 {
   553  	struct razor_package_iterator *pi1, *pi2;
   554  	struct razor_package *p1, *p2;
   555 	const char *name1, *name2, *version1, *version2, *arch1, *arch2;
   556 	int res;
   557 
   558 	pi1 = razor_package_iterator_create(set);
   559 	pi2 = razor_package_iterator_create(upstream);
   560 
   561 	razor_package_iterator_next(pi1, &p1, &name1, &version1, &arch1);
   562 	razor_package_iterator_next(pi2, &p2, &name2, &version2, &arch2);
   563 
   564 	while (p1 || p2) {
   565 		if (p1 && p2) {
   566 			res = strcmp(name1, name2);
   567 			if (res == 0)
   568 				res = razor_versioncmp(version1, version2);
   569 		} else {
   570 			res = 0;
   571 		}
   572 
   573 		if (p2 == NULL || res < 0)
   574 			callback(RAZOR_DIFF_ACTION_REMOVE,
   575 				 p1, name1, version1, arch1, data);
   576 		else if (p1 == NULL || res > 0)
   577 			callback(RAZOR_DIFF_ACTION_ADD,
   578 				 p2, name2, version2, arch2, data);
   579 
   580 		if (p1 != NULL && res <= 0)
   581 			razor_package_iterator_next(pi1, &p1,
   582 						    &name1, &version1, &arch1);
   583 		if (p2 != NULL && res >= 0)
   584 			razor_package_iterator_next(pi2, &p2,
   585 						    &name2, &version2, &arch2);
   586 	}
   587 
   588 	razor_package_iterator_destroy(pi1);
   589 	razor_package_iterator_destroy(pi2);
   590 }
   591 
   592 static void
   593 add_new_package(enum razor_diff_action action,
   594 		struct razor_package *package,
   595 		const char *name,
   596 		const char *version,
   597 		const char *arch,
   598 		void *data)
   599 {
   600 	if (action == RAZOR_DIFF_ACTION_ADD)
   601 		razor_package_query_add_package(data, package);
   602 }
   603 
   604 struct razor_package_iterator *
   605 razor_set_create_remove_iterator(struct razor_set *set,
   606 				 struct razor_set *next)
   607 {
   608 	struct razor_package_query *query;
   609 	struct razor_package_iterator *pi;
   610 
   611 	query = razor_package_query_create(set);
   612 	razor_set_diff(next, set, add_new_package, query);
   613 
   614 	pi = razor_package_query_finish(query);
   615 
   616 	/* FIXME: We need to figure out the right install order here,
   617 	 * so the post and pre scripts can run. */
   618 
   619 	/* sort */
   620 
   621 	return pi;
   622 }
   623 
   624 struct razor_package_iterator *
   625 razor_set_create_install_iterator(struct razor_set *set,
   626 				  struct razor_set *next)
   627 {
   628 	struct razor_package_query *query;
   629 	struct razor_package_iterator *pi;
   630 
   631 	query = razor_package_query_create(next);
   632 	razor_set_diff(set, next, add_new_package, query);
   633 
   634 	pi = razor_package_query_finish(query);
   635 
   636 	/* FIXME: We need to figure out the right install order here,
   637 	 * so the post and pre scripts can run. */
   638 
   639 	/* sort */
   640 
   641 	return pi;
   642 }