librazor/razor.c
author Kristian H?gsberg <krh@redhat.com>
Fri Jun 20 20:57:51 2008 -0400 (2008-06-20)
changeset 251 d8b3c713aa42
parent 247 63444a10fb8e
child 253 338a577cdfd2
child 258 29d5002bd17f
permissions -rw-r--r--
Fix package queries for empty sets.
     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.h"
    37 #include "razor-internal.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_package_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(name1, version1, NULL, arch1, data);
   450 		else if (p1 == NULL || res > 0)
   451 			callback(name2, NULL, version2, arch2, data);
   452 
   453 		if (p1 != NULL && res <= 0)
   454 			razor_package_iterator_next(pi1, &p1,
   455 						    &name1, &version1, &arch1);
   456 		if (p2 != NULL && res >= 0)
   457 			razor_package_iterator_next(pi2, &p2,
   458 						    &name2, &version2, &arch2);
   459 	}
   460 
   461 	razor_package_iterator_destroy(pi1);
   462 	razor_package_iterator_destroy(pi2);
   463 }