import.c
author Kristian H?gsberg <krh@redhat.com>
Thu Nov 08 17:14:19 2007 -0500 (2007-11-08)
changeset 75 93278d8ec39c
parent 68 0587f1759f09
child 76 773e6a26707f
permissions -rw-r--r--
Extend rpm dumper into an rpm importer.
     1 #define _GNU_SOURCE
     2 
     3 #include <string.h>
     4 #include <stdio.h>
     5 #include <sys/stat.h>
     6 #include <sys/mman.h>
     7 #include <unistd.h>
     8 #include <fcntl.h>
     9 #include <errno.h>
    10 
    11 #include <expat.h>
    12 #include <zlib.h>
    13 #include <rpm/rpmlib.h>
    14 #include <rpm/rpmdb.h>
    15 #include "sha1.h"
    16 #include "razor.h"
    17 
    18 static void
    19 parse_package(struct razor_importer *importer, const char **atts, void *data)
    20 {
    21 	const char *name = NULL, *version = NULL;
    22 	int i;
    23 
    24 	for (i = 0; atts[i]; i += 2) {
    25 		if (strcmp(atts[i], "name") == 0)
    26 			name = atts[i + 1];
    27 		else if (strcmp(atts[i], "version") == 0)
    28 			version = atts[i + 1];
    29 	}
    30 
    31 	if (name == NULL || version == NULL) {
    32 		fprintf(stderr, "invalid package tag, "
    33 			"missing name or version attributes\n");
    34 		return;
    35 	}
    36 
    37 	razor_importer_begin_package(importer, name, version);
    38 }
    39 
    40 enum {
    41 	RZR_REQUIRES, RZR_PROVIDES
    42 };
    43 
    44 static void
    45 parse_property(struct razor_importer *importer, const char **atts, void *data)
    46 {
    47 	const char *name = NULL, *version = NULL;
    48 	int i;
    49 
    50 	for (i = 0; atts[i]; i += 2) {
    51 		if (strcmp(atts[i], "name") == 0)
    52 			name = atts[i + 1];
    53 		if (strcmp(atts[i], "version") == 0)
    54 			version = atts[i + 1];
    55 	}
    56 	
    57 	if (name == NULL) {
    58 		fprintf(stderr, "invalid tag, missing name attribute\n");
    59 		return;
    60 	}
    61 
    62 	switch ((int) data) {
    63 	case RZR_REQUIRES:
    64 		razor_importer_add_property(importer, name, version,
    65 					    RAZOR_PROPERTY_REQUIRES);
    66 		break;
    67 	case RZR_PROVIDES:
    68 		razor_importer_add_property(importer, name, version,
    69 					    RAZOR_PROPERTY_PROVIDES);
    70 		break;
    71 	}
    72 }
    73 
    74 static void
    75 start_element(void *data, const char *name, const char **atts)
    76 {
    77 	struct razor_importer *importer = data;
    78 
    79 	if (strcmp(name, "package") == 0)
    80 		parse_package(importer, atts, NULL);
    81 	else if (strcmp(name, "requires") == 0)
    82 		parse_property(importer, atts, (void *) RZR_REQUIRES);
    83 	else if (strcmp(name, "provides") == 0)
    84 		parse_property(importer, atts, (void*) RZR_PROVIDES);
    85 }
    86 
    87 static void
    88 end_element (void *data, const char *name)
    89 {
    90 	struct razor_importer *importer = data;
    91 
    92 	if (strcmp(name, "package") == 0)
    93 		razor_importer_finish_package(importer);
    94 }
    95 
    96 static int
    97 import_rzr_file(struct razor_importer *importer, const char *filename)
    98 {
    99 	SHA_CTX sha1;
   100 	XML_Parser parser;
   101 	int fd;
   102 	void *p;
   103 	struct stat stat;
   104 	unsigned char hash[20];
   105 
   106 	fd = open(filename, O_RDONLY);
   107 	if (fstat(fd, &stat) < 0)
   108 		return -1;
   109 	p = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
   110 	if (p == MAP_FAILED)
   111 		return -1;
   112 
   113 	parser = XML_ParserCreate(NULL);
   114 	XML_SetUserData(parser, importer);
   115 	XML_SetElementHandler(parser, start_element, end_element);
   116 	if (XML_Parse(parser, p, stat.st_size, 1) == XML_STATUS_ERROR) {
   117 		fprintf(stderr,
   118 			"%s at line %ld, %s\n",
   119 			XML_ErrorString(XML_GetErrorCode(parser)),
   120 			XML_GetCurrentLineNumber(parser),
   121 			filename);
   122 		return 1;
   123 	}
   124 
   125 	XML_ParserFree(parser);
   126 
   127 	SHA1_Init(&sha1);
   128 	SHA1_Update(&sha1, p, stat.st_size);
   129 	SHA1_Final(hash, &sha1);
   130 
   131 	close(fd);
   132 
   133 	munmap(p, stat.st_size);
   134 
   135 	return 0;
   136 }
   137 
   138 struct razor_set *
   139 razor_import_rzr_files(int count, const char *files[])
   140 {
   141 	struct razor_importer *importer;
   142 	int i;
   143 
   144 	importer = razor_importer_new();
   145 
   146 	for (i = 0; i < count; i++) {
   147 		if (import_rzr_file(importer, files[i]) < 0) {
   148 			fprintf(stderr, "failed to import %s\n", files[i]);
   149 			exit(-1);
   150 		}
   151 	}
   152 
   153 	return razor_importer_finish(importer);
   154 }
   155 
   156 /* Import a yum filelist as a razor package set. */
   157 
   158 enum {
   159 	YUM_STATE_BEGIN,
   160 	YUM_STATE_PACKAGE_NAME,
   161 	YUM_STATE_CHECKSUM,
   162 	YUM_STATE_REQUIRES,
   163 	YUM_STATE_PROVIDES,
   164 	YUM_STATE_OBSOLETES,
   165 	YUM_STATE_CONFLICTS,
   166 	YUM_STATE_FILE
   167 };
   168 
   169 struct yum_context {
   170 	XML_Parser primary_parser;
   171 	XML_Parser filelists_parser;
   172 	XML_Parser current_parser;
   173 
   174 	struct razor_importer *importer;
   175 	struct import_property_context *current_property_context;
   176 	char name[256], buffer[512], *p;
   177 	char pkgid[128];
   178 	int state;
   179 };
   180 
   181 static void
   182 yum_primary_start_element(void *data, const char *name, const char **atts)
   183 {
   184 	struct yum_context *ctx = data;
   185 	const char *n, *version, *release;
   186 	char buffer[128];
   187 	int i;
   188 
   189 	if (strcmp(name, "name") == 0) {
   190 		ctx->state = YUM_STATE_PACKAGE_NAME;
   191 		ctx->p = ctx->name;
   192 	} else if (strcmp(name, "version") == 0) {
   193 		version = NULL;
   194 		release = NULL;
   195 		for (i = 0; atts[i]; i += 2) {
   196 			if (strcmp(atts[i], "ver") == 0)
   197 				version = atts[i + 1];
   198 			else if (strcmp(atts[i], "rel") == 0)
   199 				release = atts[i + 1];
   200 		}
   201 		if (version == NULL || release == NULL) {
   202 			fprintf(stderr, "invalid version tag, "
   203 				"missing version or  release attribute\n");
   204 			return;
   205 		}
   206 
   207 		snprintf(buffer, sizeof buffer, "%s-%s", version, release);
   208 		razor_importer_begin_package(ctx->importer, ctx->name, buffer);
   209 	} else if (strcmp(name, "checksum") == 0) {
   210 		ctx->p = ctx->pkgid;
   211 		ctx->state = YUM_STATE_CHECKSUM;
   212 	} else if (strcmp(name, "rpm:requires") == 0) {
   213 		ctx->state = YUM_STATE_REQUIRES;
   214 	} else if (strcmp(name, "rpm:provides") == 0) {
   215 		ctx->state = YUM_STATE_PROVIDES;
   216 	} else if (strcmp(name, "rpm:obsoletes") == 0) {
   217 		ctx->state = YUM_STATE_OBSOLETES;
   218 	} else if (strcmp(name, "rpm:conflicts") == 0) {
   219 		ctx->state = YUM_STATE_CONFLICTS;
   220 	} else if (strcmp(name, "rpm:entry") == 0 &&
   221 		   ctx->state != YUM_STATE_BEGIN) {
   222 		n = NULL;
   223 		version = NULL;
   224 		release = NULL;
   225 		for (i = 0; atts[i]; i += 2) {
   226 			if (strcmp(atts[i], "name") == 0)
   227 				n = atts[i + 1];
   228 			else if (strcmp(atts[i], "ver") == 0)
   229 				version = atts[i + 1];
   230 			else if (strcmp(atts[i], "rel") == 0)
   231 				release = atts[i + 1];
   232 		}
   233 
   234 		if (n == NULL) {
   235 			fprintf(stderr, "invalid rpm:entry, "
   236 				"missing name or version attributes\n");
   237 			return;
   238 		}
   239 
   240 		if (version && release)
   241 			snprintf(buffer, sizeof buffer,
   242 				 "%s-%s", version, release);
   243 		else if (version)
   244 			strcpy(buffer, version);
   245 		else
   246 			buffer[0] = '\0';
   247 			
   248 		switch (ctx->state) {
   249 		case YUM_STATE_REQUIRES:
   250 			razor_importer_add_property(ctx->importer, n, buffer,
   251 						    RAZOR_PROPERTY_REQUIRES);
   252 			break;
   253 		case YUM_STATE_PROVIDES:
   254 			razor_importer_add_property(ctx->importer, n, buffer,
   255 						    RAZOR_PROPERTY_PROVIDES);
   256 			break;
   257 		case YUM_STATE_OBSOLETES:
   258 			razor_importer_add_property(ctx->importer, n, buffer,
   259 						    RAZOR_PROPERTY_OBSOLETES);
   260 			break;
   261 		case YUM_STATE_CONFLICTS:
   262 			razor_importer_add_property(ctx->importer, n, buffer,
   263 						    RAZOR_PROPERTY_CONFLICTS);
   264 			break;
   265 		}
   266 	}
   267 }
   268 
   269 static void
   270 yum_primary_end_element (void *data, const char *name)
   271 {
   272 	struct yum_context *ctx = data;
   273 
   274 	switch (ctx->state) {
   275 	case YUM_STATE_PACKAGE_NAME:
   276 	case YUM_STATE_CHECKSUM:
   277 	case YUM_STATE_FILE:
   278 		ctx->state = YUM_STATE_BEGIN;
   279 		break;
   280 	}
   281 
   282 	if (strcmp(name, "package") == 0) {
   283 		XML_StopParser(ctx->current_parser, XML_TRUE);
   284 		ctx->current_parser = ctx->filelists_parser;
   285 	}
   286 }
   287 
   288 static void
   289 yum_character_data (void *data, const XML_Char *s, int len)
   290 {
   291 	struct yum_context *ctx = data;
   292 
   293 	switch (ctx->state) {
   294 	case YUM_STATE_PACKAGE_NAME:
   295 	case YUM_STATE_CHECKSUM:
   296 	case YUM_STATE_FILE:
   297 		memcpy(ctx->p, s, len);
   298 		ctx->p += len;
   299 		*ctx->p = '\0';
   300 		break;
   301 	}
   302 }
   303 
   304 static void
   305 yum_filelists_start_element(void *data, const char *name, const char **atts)
   306 {
   307 	struct yum_context *ctx = data;
   308 	const char *pkg, *pkgid;
   309 	int i;
   310 
   311 	if (strcmp(name, "package") == 0) {
   312 		pkg = NULL;
   313 		pkgid = NULL;
   314 		for (i = 0; atts[i]; i += 2) {
   315 			if (strcmp(atts[i], "name") == 0)
   316 				pkg = atts[i + 1];
   317 			else if (strcmp(atts[i], "pkgid") == 0)
   318 				pkgid = atts[i + 1];
   319 		}
   320 		if (strcmp(pkgid, ctx->pkgid) != 0)
   321 			fprintf(stderr, "primary.xml and filelists.xml "
   322 				"mismatch for %s: %s vs %s",
   323 				pkg, pkgid, ctx->pkgid);
   324 	} else if (strcmp(name, "file") == 0) {
   325 		ctx->state = YUM_STATE_FILE;
   326 		ctx->p = ctx->buffer;
   327 	}
   328 }
   329 
   330 
   331 static void
   332 yum_filelists_end_element (void *data, const char *name)
   333 {
   334 	struct yum_context *ctx = data;
   335 
   336 	ctx->state = YUM_STATE_BEGIN;
   337 	if (strcmp(name, "package") == 0) {
   338 		XML_StopParser(ctx->current_parser, XML_TRUE);
   339 		ctx->current_parser = ctx->primary_parser;
   340 		razor_importer_finish_package(ctx->importer);
   341 	} else if (strcmp(name, "file") == 0)
   342 		razor_importer_add_file(ctx->importer, ctx->buffer);
   343 
   344 }
   345 
   346 #define XML_BUFFER_SIZE 4096
   347 
   348 struct razor_set *
   349 razor_set_create_from_yum(void)
   350 {
   351 	struct yum_context ctx;
   352 	void *buf;
   353 	int len, ret;
   354 	gzFile primary, filelists;
   355 	XML_ParsingStatus status;
   356 
   357 	ctx.importer = razor_importer_new();	
   358 	ctx.state = YUM_STATE_BEGIN;
   359 
   360 	ctx.primary_parser = XML_ParserCreate(NULL);
   361 	XML_SetUserData(ctx.primary_parser, &ctx);
   362 	XML_SetElementHandler(ctx.primary_parser,
   363 			      yum_primary_start_element,
   364 			      yum_primary_end_element);
   365 	XML_SetCharacterDataHandler(ctx.primary_parser,
   366 				    yum_character_data);
   367 
   368 	ctx.filelists_parser = XML_ParserCreate(NULL);
   369 	XML_SetUserData(ctx.filelists_parser, &ctx);
   370 	XML_SetElementHandler(ctx.filelists_parser,
   371 			      yum_filelists_start_element,
   372 			      yum_filelists_end_element);
   373 	XML_SetCharacterDataHandler(ctx.filelists_parser,
   374 				    yum_character_data);
   375 
   376 	primary = gzopen("primary.xml.gz", "rb");
   377 	if (primary == NULL)
   378 		return NULL;
   379 	filelists = gzopen("filelists.xml.gz", "rb");
   380 	if (filelists == NULL)
   381 		return NULL;
   382 
   383 	ctx.current_parser = ctx.primary_parser;
   384 
   385 	do {
   386 		XML_GetParsingStatus(ctx.current_parser, &status);
   387 		switch (status.parsing) {
   388 		case XML_SUSPENDED:
   389 			ret = XML_ResumeParser(ctx.current_parser);
   390 			break;
   391 		case XML_PARSING:
   392 		case XML_INITIALIZED:
   393 			buf = XML_GetBuffer(ctx.current_parser,
   394 					    XML_BUFFER_SIZE);
   395 			if (ctx.current_parser == ctx.primary_parser)
   396 				len = gzread(primary, buf, XML_BUFFER_SIZE);
   397 			else
   398 				len = gzread(filelists, buf, XML_BUFFER_SIZE);
   399 			if (len < 0) {
   400 				fprintf(stderr,
   401 					"couldn't read input: %s\n",
   402 					strerror(errno));
   403 				return NULL;
   404 			}
   405 
   406 			XML_ParseBuffer(ctx.current_parser, len, len == 0);
   407 			break;
   408 		case XML_FINISHED:
   409 			break;
   410 		}
   411 	} while (status.parsing != XML_FINISHED);
   412 
   413 
   414 	XML_ParserFree(ctx.primary_parser);
   415 	XML_ParserFree(ctx.filelists_parser);
   416 
   417 	gzclose(primary);
   418 	gzclose(filelists);
   419 
   420 	return razor_importer_finish(ctx.importer);
   421 }
   422 
   423 union rpm_entry {
   424 	void *p;
   425 	char *string;
   426 	char **list;
   427 	unsigned int *flags;
   428 };
   429 
   430 struct razor_set *
   431 razor_set_create_from_rpmdb(void)
   432 {
   433 	struct razor_importer *importer;
   434 	rpmdbMatchIterator iter;
   435 	Header h;
   436 	int_32 type, count, i;
   437 	union rpm_entry name, version, release;
   438 	union rpm_entry property_names, property_versions, property_flags;
   439 	union rpm_entry basenames, dirnames, dirindexes;
   440 	char filename[PATH_MAX];
   441 	rpmdb db;
   442 
   443 	rpmReadConfigFiles(NULL, NULL);
   444 
   445 	if (rpmdbOpen("", &db, O_RDONLY, 0644) != 0) {
   446 		fprintf(stderr, "cannot open rpm database\n");
   447 		exit(1);
   448 	}
   449 
   450 	importer = razor_importer_new();
   451 
   452 	iter = rpmdbInitIterator(db, 0, NULL, 0);
   453 	while (h = rpmdbNextIterator(iter), h != NULL) {
   454 		headerGetEntry(h, RPMTAG_NAME, &type, &name.p, &count);
   455 		headerGetEntry(h, RPMTAG_VERSION, &type, &version.p, &count);
   456 		headerGetEntry(h, RPMTAG_RELEASE, &type, &release.p, &count);
   457 		snprintf(filename, sizeof filename, "%s-%s",
   458 			 version.string, release.string);
   459 		razor_importer_begin_package(importer, name.string, filename);
   460 
   461 		headerGetEntry(h, RPMTAG_REQUIRENAME, &type,
   462 			       &property_names.p, &count);
   463 		headerGetEntry(h, RPMTAG_REQUIREVERSION, &type,
   464 			       &property_versions.p, &count);
   465 		headerGetEntry(h, RPMTAG_REQUIREFLAGS, &type,
   466 			       &property_flags.p, &count);
   467 		for (i = 0; i < count; i++)
   468 			razor_importer_add_property(importer,
   469 						    property_names.list[i],
   470 						    property_versions.list[i],
   471 						    RAZOR_PROPERTY_REQUIRES);
   472 
   473 		headerGetEntry(h, RPMTAG_PROVIDENAME, &type,
   474 			       &property_names.p, &count);
   475 		headerGetEntry(h, RPMTAG_PROVIDEVERSION, &type,
   476 			       &property_versions.p, &count);
   477 		headerGetEntry(h, RPMTAG_PROVIDEFLAGS, &type,
   478 			       &property_flags.p, &count);
   479 		for (i = 0; i < count; i++)
   480 			razor_importer_add_property(importer,
   481 						    property_names.list[i],
   482 						    property_versions.list[i],
   483 						    RAZOR_PROPERTY_PROVIDES);
   484 
   485 		headerGetEntry(h, RPMTAG_OBSOLETENAME, &type,
   486 			       &property_names.p, &count);
   487 		headerGetEntry(h, RPMTAG_OBSOLETEVERSION, &type,
   488 			       &property_versions.p, &count);
   489 		headerGetEntry(h, RPMTAG_OBSOLETEFLAGS, &type,
   490 			       &property_flags.p, &count);
   491 		for (i = 0; i < count; i++)
   492 			razor_importer_add_property(importer,
   493 						    property_names.list[i],
   494 						    property_versions.list[i],
   495 						    RAZOR_PROPERTY_OBSOLETES);
   496 
   497 		headerGetEntry(h, RPMTAG_CONFLICTNAME, &type,
   498 			       &property_names.p, &count);
   499 		headerGetEntry(h, RPMTAG_CONFLICTVERSION, &type,
   500 			       &property_versions.p, &count);
   501 		headerGetEntry(h, RPMTAG_CONFLICTFLAGS, &type,
   502 			       &property_flags.p, &count);
   503 		for (i = 0; i < count; i++)
   504 			razor_importer_add_property(importer,
   505 						    property_names.list[i],
   506 						    property_versions.list[i],
   507 						    RAZOR_PROPERTY_CONFLICTS);
   508 
   509 		headerGetEntry(h, RPMTAG_BASENAMES, &type,
   510 			       &basenames.p, &count);
   511 		headerGetEntry(h, RPMTAG_DIRNAMES, &type,
   512 			       &dirnames.p, &count);
   513 		headerGetEntry(h, RPMTAG_DIRINDEXES, &type,
   514 			       &dirindexes.p, &count);
   515 		for (i = 0; i < count; i++) {
   516 			snprintf(filename, sizeof filename, "%s%s",
   517 				 dirnames.list[dirindexes.flags[i]],
   518 				 basenames.list[i]);
   519 			razor_importer_add_file(importer, filename);
   520 		}
   521 
   522 		razor_importer_finish_package(importer);
   523 	}
   524 
   525 	rpmdbClose(db);
   526 
   527 	return razor_importer_finish(importer);
   528 }