librazor/rpm.c
author J. Ali Harlow <ali@juiblex.co.uk>
Wed Apr 29 17:00:01 2009 +0100 (2009-04-29)
changeset 361 2523d03a840e
parent 353 90b63682fdd7
child 362 cf88b5df2884
permissions -rw-r--r--
Add support for preloading lua modules. This is useful both when
providing lua bindings to applications based on librazor and when
producing static binaries using librazor (where otherwise the lua
POSIX library would need to be included as an additional dynamic
object).
     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 #include "config.h"
    22 
    23 #include <stdio.h>
    24 #include <stddef.h>
    25 #include <stdlib.h>
    26 #include <string.h>
    27 #include <errno.h>
    28 #include <sys/stat.h>
    29 #include <sys/types.h>
    30 #if HAVE_SYS_WAIT_H
    31 #include <sys/wait.h>
    32 #endif
    33 #include <fcntl.h>
    34 #include <dirent.h>
    35 #include <unistd.h>
    36 #if MSWIN_API
    37 #include <winsock2.h>	/* For ntohl() */
    38 #else
    39 #include <arpa/inet.h>
    40 #endif
    41 #include <limits.h>
    42 #include <zlib.h>
    43 #include <assert.h>
    44 
    45 #include "razor.h"
    46 #include "razor-internal.h"
    47 
    48 #ifndef O_BINARY
    49 #define O_BINARY	0
    50 #endif
    51 
    52 #define	RPM_LEAD_SIZE 96
    53 
    54 enum {
    55     PIPE	=  1,	/*!< pipe/fifo */
    56     CDEV	=  2,	/*!< character device */
    57     XDIR	=  4,	/*!< directory */
    58     BDEV	=  6,	/*!< block device */
    59     REG		=  8,	/*!< regular file */
    60     LINK	= 10,	/*!< hard link */
    61     SOCK	= 12	/*!< socket */
    62 };
    63 
    64 enum {
    65     RPMSENSE_LESS		= 1 << 1,
    66     RPMSENSE_GREATER		= 1 << 2,
    67     RPMSENSE_EQUAL		= 1 << 3,
    68     RPMSENSE_PREREQ		= 1 << 6,
    69     RPMSENSE_SCRIPT_PRE		= 1 << 9,
    70     RPMSENSE_SCRIPT_POST	= 1 << 10,
    71     RPMSENSE_SCRIPT_PREUN	= 1 << 11,
    72     RPMSENSE_SCRIPT_POSTUN	= 1 << 12,
    73 };
    74 
    75 enum {
    76     RPMTAG_NAME  		= 1000,	/* s */
    77     RPMTAG_VERSION		= 1001,	/* s */
    78     RPMTAG_RELEASE		= 1002,	/* s */
    79     RPMTAG_EPOCH   		= 1003,	/* i */
    80     RPMTAG_SUMMARY		= 1004,	/* s{} */
    81     RPMTAG_DESCRIPTION		= 1005,	/* s{} */
    82     RPMTAG_BUILDTIME		= 1006,	/* i */
    83     RPMTAG_BUILDHOST		= 1007,	/* s */
    84     RPMTAG_INSTALLTIME		= 1008,	/* i */
    85     RPMTAG_SIZE			= 1009,	/* i */
    86     RPMTAG_DISTRIBUTION		= 1010,	/* s */
    87     RPMTAG_VENDOR		= 1011,	/* s */
    88     RPMTAG_GIF			= 1012,	/* x */
    89     RPMTAG_XPM			= 1013,	/* x */
    90     RPMTAG_LICENSE		= 1014,	/* s */
    91     RPMTAG_PACKAGER		= 1015,	/* s */
    92     RPMTAG_GROUP		= 1016,	/* s{} */
    93     RPMTAG_CHANGELOG		= 1017, /*!< s[] internal */
    94     RPMTAG_SOURCE		= 1018,	/* s[] */
    95     RPMTAG_PATCH		= 1019,	/* s[] */
    96     RPMTAG_URL			= 1020,	/* s */
    97     RPMTAG_OS			= 1021,	/* s legacy used int */
    98     RPMTAG_ARCH			= 1022,	/* s legacy used int */
    99     RPMTAG_PREIN		= 1023,	/* s */
   100     RPMTAG_POSTIN		= 1024,	/* s */
   101     RPMTAG_PREUN		= 1025,	/* s */
   102     RPMTAG_POSTUN		= 1026,	/* s */
   103     RPMTAG_OLDFILENAMES		= 1027, /* s[] obsolete */
   104     RPMTAG_FILESIZES		= 1028,	/* i */
   105     RPMTAG_FILESTATES		= 1029, /* c */
   106     RPMTAG_FILEMODES		= 1030,	/* h */
   107     RPMTAG_FILEUIDS		= 1031, /*!< internal */
   108     RPMTAG_FILEGIDS		= 1032, /*!< internal */
   109     RPMTAG_FILERDEVS		= 1033,	/* h */
   110     RPMTAG_FILEMTIMES		= 1034, /* i */
   111     RPMTAG_FILEMD5S		= 1035,	/* s[] */
   112     RPMTAG_FILELINKTOS		= 1036,	/* s[] */
   113     RPMTAG_FILEFLAGS		= 1037,	/* i */
   114     RPMTAG_ROOT			= 1038, /*!< internal - obsolete */
   115     RPMTAG_FILEUSERNAME		= 1039,	/* s[] */
   116     RPMTAG_FILEGROUPNAME	= 1040,	/* s[] */
   117     RPMTAG_EXCLUDE		= 1041, /*!< internal - obsolete */
   118     RPMTAG_EXCLUSIVE		= 1042, /*!< internal - obsolete */
   119     RPMTAG_ICON			= 1043,
   120     RPMTAG_SOURCERPM		= 1044,	/* s */
   121     RPMTAG_FILEVERIFYFLAGS	= 1045,	/* i */
   122     RPMTAG_ARCHIVESIZE		= 1046,	/* i */
   123     RPMTAG_PROVIDENAME		= 1047,	/* s[] */
   124     RPMTAG_REQUIREFLAGS		= 1048,	/* i */
   125     RPMTAG_REQUIRENAME		= 1049,	/* s[] */
   126     RPMTAG_REQUIREVERSION	= 1050,	/* s[] */
   127     RPMTAG_NOSOURCE		= 1051, /*!< internal */
   128     RPMTAG_NOPATCH		= 1052, /*!< internal */
   129     RPMTAG_CONFLICTFLAGS	= 1053, /* i */
   130     RPMTAG_CONFLICTNAME		= 1054,	/* s[] */
   131     RPMTAG_CONFLICTVERSION	= 1055,	/* s[] */
   132     RPMTAG_DEFAULTPREFIX	= 1056, /*!< internal - deprecated */
   133     RPMTAG_BUILDROOT		= 1057, /*!< internal */
   134     RPMTAG_INSTALLPREFIX	= 1058, /*!< internal - deprecated */
   135     RPMTAG_EXCLUDEARCH		= 1059,
   136     RPMTAG_EXCLUDEOS		= 1060,
   137     RPMTAG_EXCLUSIVEARCH	= 1061,
   138     RPMTAG_EXCLUSIVEOS		= 1062,
   139     RPMTAG_AUTOREQPROV		= 1063, /*!< internal */
   140     RPMTAG_RPMVERSION		= 1064,	/* s */
   141     RPMTAG_TRIGGERSCRIPTS	= 1065,	/* s[] */
   142     RPMTAG_TRIGGERNAME		= 1066,	/* s[] */
   143     RPMTAG_TRIGGERVERSION	= 1067,	/* s[] */
   144     RPMTAG_TRIGGERFLAGS		= 1068,	/* i */
   145     RPMTAG_TRIGGERINDEX		= 1069,	/* i */
   146     RPMTAG_VERIFYSCRIPT		= 1079,	/* s */
   147     RPMTAG_CHANGELOGTIME	= 1080,	/* i */
   148     RPMTAG_CHANGELOGNAME	= 1081,	/* s[] */
   149     RPMTAG_CHANGELOGTEXT	= 1082,	/* s[] */
   150     RPMTAG_BROKENMD5		= 1083, /*!< internal - obsolete */
   151     RPMTAG_PREREQ		= 1084, /*!< internal */
   152     RPMTAG_PREINPROG		= 1085,	/* s */
   153     RPMTAG_POSTINPROG		= 1086,	/* s */
   154     RPMTAG_PREUNPROG		= 1087,	/* s */
   155     RPMTAG_POSTUNPROG		= 1088,	/* s */
   156     RPMTAG_BUILDARCHS		= 1089,
   157     RPMTAG_OBSOLETENAME		= 1090,	/* s[] */
   158     RPMTAG_VERIFYSCRIPTPROG	= 1091,	/* s */
   159     RPMTAG_TRIGGERSCRIPTPROG	= 1092,	/* s */
   160     RPMTAG_DOCDIR		= 1093, /*!< internal */
   161     RPMTAG_COOKIE		= 1094,	/* s */
   162     RPMTAG_FILEDEVICES		= 1095,	/* i */
   163     RPMTAG_FILEINODES		= 1096,	/* i */
   164     RPMTAG_FILELANGS		= 1097,	/* s[] */
   165     RPMTAG_PREFIXES		= 1098,	/* s[] */
   166     RPMTAG_INSTPREFIXES		= 1099,	/* s[] */
   167     RPMTAG_TRIGGERIN		= 1100, /*!< internal */
   168     RPMTAG_TRIGGERUN		= 1101, /*!< internal */
   169     RPMTAG_TRIGGERPOSTUN	= 1102, /*!< internal */
   170     RPMTAG_AUTOREQ		= 1103, /*!< internal */
   171     RPMTAG_AUTOPROV		= 1104, /*!< internal */
   172     RPMTAG_CAPABILITY		= 1105, /*!< internal - obsolete */
   173     RPMTAG_SOURCEPACKAGE	= 1106, /*!< i src.rpm header marker */
   174     RPMTAG_OLDORIGFILENAMES	= 1107, /*!< internal - obsolete */
   175     RPMTAG_BUILDPREREQ		= 1108, /*!< internal */
   176     RPMTAG_BUILDREQUIRES	= 1109, /*!< internal */
   177     RPMTAG_BUILDCONFLICTS	= 1110, /*!< internal */
   178     RPMTAG_BUILDMACROS		= 1111, /*!< internal - unused */
   179     RPMTAG_PROVIDEFLAGS		= 1112,	/* i */
   180     RPMTAG_PROVIDEVERSION	= 1113,	/* s[] */
   181     RPMTAG_OBSOLETEFLAGS	= 1114,	/* i */
   182     RPMTAG_OBSOLETEVERSION	= 1115,	/* s[] */
   183     RPMTAG_DIRINDEXES		= 1116,	/* i */
   184     RPMTAG_BASENAMES		= 1117,	/* s[] */
   185     RPMTAG_DIRNAMES		= 1118,	/* s[] */
   186     RPMTAG_ORIGDIRINDEXES	= 1119, /*!< internal */
   187     RPMTAG_ORIGBASENAMES	= 1120, /*!< internal */
   188     RPMTAG_ORIGDIRNAMES		= 1121, /*!< internal */
   189     RPMTAG_OPTFLAGS		= 1122,	/* s */
   190     RPMTAG_DISTURL		= 1123,	/* s */
   191     RPMTAG_PAYLOADFORMAT	= 1124,	/* s */
   192     RPMTAG_PAYLOADCOMPRESSOR	= 1125,	/* s */
   193     RPMTAG_PAYLOADFLAGS		= 1126,	/* s */
   194     RPMTAG_INSTALLCOLOR		= 1127, /*!< i transaction color when installed */
   195     RPMTAG_INSTALLTID		= 1128,	/* i */
   196     RPMTAG_REMOVETID		= 1129,	/* i */
   197     RPMTAG_SHA1RHN		= 1130, /*!< internal - obsolete */
   198     RPMTAG_RHNPLATFORM		= 1131,	/* s */
   199     RPMTAG_PLATFORM		= 1132,	/* s */
   200     RPMTAG_PATCHESNAME		= 1133, /*!< placeholder (SuSE) */
   201     RPMTAG_PATCHESFLAGS		= 1134, /*!< placeholder (SuSE) */
   202     RPMTAG_PATCHESVERSION	= 1135, /*!< placeholder (SuSE) */
   203     RPMTAG_CACHECTIME		= 1136,	/* i */
   204     RPMTAG_CACHEPKGPATH		= 1137,	/* s */
   205     RPMTAG_CACHEPKGSIZE		= 1138,	/* i */
   206     RPMTAG_CACHEPKGMTIME	= 1139,	/* i */
   207     RPMTAG_FILECOLORS		= 1140,	/* i */
   208     RPMTAG_FILECLASS		= 1141,	/* i */
   209     RPMTAG_CLASSDICT		= 1142,	/* s[] */
   210     RPMTAG_FILEDEPENDSX		= 1143,	/* i */
   211     RPMTAG_FILEDEPENDSN		= 1144,	/* i */
   212     RPMTAG_DEPENDSDICT		= 1145,	/* i */
   213     RPMTAG_SOURCEPKGID		= 1146,	/* x */
   214     RPMTAG_FILECONTEXTS		= 1147,	/* s[] */
   215     RPMTAG_FSCONTEXTS		= 1148,	/*!< s[] extension */
   216     RPMTAG_RECONTEXTS		= 1149,	/*!< s[] extension */
   217     RPMTAG_POLICIES		= 1150,	/*!< s[] selinux *.te policy file. */
   218     RPMTAG_PRETRANS		= 1151,	/* s */
   219     RPMTAG_POSTTRANS		= 1152,	/* s */
   220     RPMTAG_PRETRANSPROG		= 1153,	/* s */
   221     RPMTAG_POSTTRANSPROG	= 1154,	/* s */
   222     RPMTAG_DISTTAG		= 1155,	/* s */
   223     RPMTAG_SUGGESTSNAME		= 1156,	/* s[] extension placeholder */
   224     RPMTAG_SUGGESTSVERSION	= 1157,	/* s[] extension placeholder */
   225     RPMTAG_SUGGESTSFLAGS	= 1158,	/* i   extension placeholder */
   226     RPMTAG_ENHANCESNAME		= 1159,	/* s[] extension placeholder */
   227     RPMTAG_ENHANCESVERSION	= 1160,	/* s[] extension placeholder */
   228     RPMTAG_ENHANCESFLAGS	= 1161,	/* i   extension placeholder */
   229     RPMTAG_PRIORITY		= 1162, /* i   extension placeholder */
   230     RPMTAG_CVSID		= 1163, /* s */
   231     RPMTAG_TRIGGERPREIN		= 1171, /*!< internal */
   232 };
   233 
   234 struct rpm_header {
   235 	unsigned char magic[4];
   236 	unsigned char reserved[4];
   237 	int nindex;
   238 	int hsize;
   239 };
   240 
   241 struct rpm_header_index {
   242 	int tag;
   243 	int type;
   244 	int offset;
   245 	int count;
   246 };
   247 
   248 struct razor_rpm {
   249 	struct rpm_header *signature;
   250 	struct rpm_header *header;
   251 	const char **dirs;
   252 	unsigned int n_prefixes;
   253 	const char **prefixes;
   254 	const char *pool;
   255 	void *map;
   256 	size_t size;
   257 	void *payload;
   258 	struct razor_relocations *relocations;
   259 };
   260 
   261 enum razor_relocation_flags {
   262 	RAZOR_RELOCATION_ACTIVE		= 1 << 0,
   263 };
   264 
   265 struct razor_relocation {
   266 	enum razor_relocation_flags flags;
   267 	size_t oldlen;
   268 	size_t newlen;
   269 	char *oldpath;
   270 	char *newpath;
   271 };
   272 
   273 struct razor_relocations {
   274 	/* Ordered such that if oldpath 1 starts with oldpath 2, then
   275 	 * oldpath 1 is listed first (ie., /usr/bin comes before /usr)
   276 	 * and terminated with a NULL oldpath.
   277 	 */
   278 	struct razor_relocation *relocations;
   279 	int n_relocations;
   280 	char *path;
   281 };
   282 
   283 RAZOR_EXPORT struct razor_relocations *razor_relocations_create(void)
   284 {
   285 	return calloc(1, sizeof(struct razor_relocations));
   286 }
   287 
   288 RAZOR_EXPORT void razor_relocations_add(struct razor_relocations *rr,
   289 			   const char *oldpath, const char *newpath)
   290 {
   291 	int i, found = 0;
   292 	size_t len;
   293 
   294 	if (newpath && !strcmp(oldpath, newpath))
   295 		newpath = NULL;
   296 
   297 	for (i = 0; i < rr->n_relocations; i++) {
   298 		len = rr->relocations[i].oldlen;
   299 		if (!strncmp(rr->relocations[i].oldpath, oldpath, len)) {
   300 			found = !strcmp(rr->relocations[i].oldpath, oldpath);
   301 			break;
   302 		}
   303 	}
   304 
   305 	if (!newpath) {
   306 		if (found) {
   307 			free(rr->relocations[i].oldpath);
   308 			free(rr->relocations[i].newpath);
   309 			do {
   310 				rr->relocations[i] = rr->relocations[i + 1];
   311 			} while (rr->relocations[++i].oldpath);
   312 		}
   313 		return;
   314 	}
   315 
   316 	if (found) {
   317 		free(rr->relocations[i].newpath);
   318 		rr->relocations[i].newpath = strdup(newpath);
   319 		rr->relocations[i].newlen = strlen(newpath);
   320 		return;
   321 	}
   322 
   323 	if (!rr->n_relocations++)
   324 		rr->relocations = calloc(1, sizeof *rr->relocations);
   325 	else {
   326 		rr->relocations = realloc(rr->relocations,
   327 					  rr->n_relocations * sizeof *rr->relocations);
   328 		memmove(rr->relocations + i + 1, rr->relocations + i,
   329 			(rr->n_relocations - i - 1) * sizeof *rr->relocations);
   330 	}
   331 
   332 	rr->relocations[i].flags = 0;
   333 	rr->relocations[i].oldpath = strdup(oldpath);
   334 	rr->relocations[i].newpath = strdup(newpath);
   335 	rr->relocations[i].oldlen = strlen(oldpath);
   336 	rr->relocations[i].newlen = strlen(newpath);
   337 }
   338 
   339 RAZOR_EXPORT void
   340 razor_relocations_set_rpm(struct razor_relocations *rr, struct razor_rpm *rpm)
   341 {
   342 	int i, j;
   343 
   344 	for (i = 0; i < rr->n_relocations; i++) {
   345 		rr->relocations[i].flags &= ~RAZOR_RELOCATION_ACTIVE;
   346 		for (j = 0; j < rpm->n_prefixes; j++)
   347 			if (!strcmp(rpm->prefixes[j],
   348 				    rr->relocations[i].oldpath)) {
   349 				rr->relocations[i].flags |= RAZOR_RELOCATION_ACTIVE;
   350 				break;
   351 			}
   352 	}
   353 }
   354 
   355 RAZOR_EXPORT const char *
   356 razor_relocations_apply(struct razor_relocations *rr, const char *path)
   357 {
   358 	int i;
   359 	size_t len;
   360 
   361 	for (i = 0; i < rr->n_relocations; i++)
   362 		if (rr->relocations[i].flags & RAZOR_RELOCATION_ACTIVE &&
   363 		    !strncmp(path, rr->relocations[i].oldpath,
   364 			     rr->relocations[i].oldlen))
   365 			break;
   366 
   367 	if (i < rr->n_relocations) {
   368 		free(rr->path);
   369 		len = strlen(path + rr->relocations[i].oldlen) +
   370 		      rr->relocations[i].newlen;
   371 		rr->path = malloc(len + 1);
   372 		memcpy(rr->path, rr->relocations[i].newpath,
   373 		       rr->relocations[i].newlen);
   374 		strcpy(rr->path + rr->relocations[i].newlen,
   375 		       path + rr->relocations[i].oldlen);
   376 		return rr->path;
   377 	} else
   378 		return path;
   379 }
   380 
   381 RAZOR_EXPORT void razor_relocations_destroy(struct razor_relocations *rr)
   382 {
   383 	free(rr->path);
   384 	free(rr->relocations);
   385 	free(rr);
   386 }
   387 
   388 static struct rpm_header_index *
   389 razor_rpm_get_header(struct razor_rpm *rpm, unsigned int tag)
   390 {
   391 	struct rpm_header_index *index, *end;
   392 
   393 	index = (struct rpm_header_index *) (rpm->header + 1);
   394 	end = index + ntohl(rpm->header->nindex);
   395 	while (index < end) {
   396 		if (ntohl(index->tag) == tag)
   397 			return index;
   398 		index++;
   399 	}
   400 
   401 	return NULL;
   402 }
   403 
   404 static const void *
   405 razor_rpm_get_indirect(struct razor_rpm *rpm,
   406 		       unsigned int tag, unsigned int *count)
   407 {
   408 	struct rpm_header_index *index;
   409 
   410 	index = razor_rpm_get_header(rpm, tag);
   411 	if (index != NULL) {
   412 		if (count)
   413 			*count = ntohl(index->count);
   414 
   415 		return rpm->pool + ntohl(index->offset);
   416 	}
   417 
   418 	return NULL;
   419 }
   420 
   421 static uint32_t
   422 rpm_to_razor_flags(uint32_t flags)
   423 {
   424 	uint32_t razor_flags;
   425 
   426 	razor_flags = 0;
   427 	if (flags & RPMSENSE_LESS)
   428 		razor_flags |= RAZOR_PROPERTY_LESS;
   429 	if (flags & RPMSENSE_EQUAL)
   430 		razor_flags |= RAZOR_PROPERTY_EQUAL;
   431 	if (flags & RPMSENSE_GREATER)
   432 		razor_flags |= RAZOR_PROPERTY_GREATER;
   433 
   434 	if (flags & RPMSENSE_SCRIPT_PRE)
   435 		razor_flags |= RAZOR_PROPERTY_PRE;
   436 	if (flags & RPMSENSE_SCRIPT_POST)
   437 		razor_flags |= RAZOR_PROPERTY_POST;
   438 	if (flags & RPMSENSE_SCRIPT_PREUN)
   439 		razor_flags |= RAZOR_PROPERTY_PREUN;
   440 	if (flags & RPMSENSE_SCRIPT_POSTUN)
   441 		razor_flags |= RAZOR_PROPERTY_POSTUN;
   442 	
   443 	return razor_flags;
   444 }
   445 
   446 static void
   447 import_properties(struct razor_importer *importer, uint32_t type,
   448 		  struct razor_rpm *rpm,
   449 		  int name_tag, int version_tag, int flags_tag)
   450 {
   451 	const char *name, *version;
   452 	const uint32_t *flags;
   453 	uint32_t f;
   454 	unsigned int i, count;
   455 
   456 	name = razor_rpm_get_indirect(rpm, name_tag, &count);
   457 	if (name == NULL)
   458 		return;
   459 
   460 	flags = razor_rpm_get_indirect(rpm, flags_tag, &count);
   461 
   462 	version = razor_rpm_get_indirect(rpm, version_tag, &count);
   463 	for (i = 0; i < count; i++) {
   464 		f = rpm_to_razor_flags(ntohl(flags[i]));
   465 		razor_importer_add_property(importer, name, f | type, version);
   466 		name += strlen(name) + 1;
   467 		version += strlen(version) + 1;
   468 	}
   469 }
   470 
   471 static void
   472 import_files(struct razor_importer *importer, struct razor_rpm *rpm)
   473 {
   474 	const char *name;
   475 	const uint32_t *index;
   476 	unsigned int i, count;
   477 	char buffer[256];
   478 
   479 	if (rpm->dirs == NULL)
   480 		return;
   481 
   482 	/* assert: count is the same for all arrays */
   483 	index = razor_rpm_get_indirect(rpm, RPMTAG_DIRINDEXES, &count);
   484 	name = razor_rpm_get_indirect(rpm, RPMTAG_BASENAMES, &count);
   485 	for (i = 0; i < count; i++) {
   486 		snprintf(buffer, sizeof buffer,
   487 			 "%s%s", rpm->dirs[ntohl(*index)], name);
   488 		razor_importer_add_file(importer, buffer);
   489 		name += strlen(name) + 1;
   490 		index++;
   491 	}
   492 }
   493 
   494 RAZOR_EXPORT struct razor_rpm *
   495 razor_rpm_open(const char *filename)
   496 {
   497 	struct razor_rpm *rpm;
   498 	struct rpm_header_index *base, *index;
   499 	unsigned int count, i, nindex, hsize;
   500 	const char *name, *prefix;
   501 
   502 	assert (filename != NULL);
   503 
   504 	rpm = malloc(sizeof *rpm);
   505 	if (rpm == NULL)
   506 		return NULL;
   507 	memset(rpm, 0, sizeof *rpm);
   508 
   509 	rpm->map = razor_file_get_contents(filename, &rpm->size);
   510 	if (!rpm->map) {
   511 		fprintf(stderr, "couldn't get contents of %s (%s)\n", filename,
   512 			strerror(errno));
   513 		return NULL;
   514 	}
   515 
   516 	rpm->signature = rpm->map + RPM_LEAD_SIZE;
   517 	nindex = ntohl(rpm->signature->nindex);
   518 	hsize = ntohl(rpm->signature->hsize);
   519 	rpm->header = (void *) (rpm->signature + 1) +
   520 		ALIGN(nindex * sizeof *index + hsize, 8);
   521 	nindex = ntohl(rpm->header->nindex);
   522 	hsize = ntohl(rpm->header->hsize);
   523 	rpm->payload = (void *) (rpm->header + 1) +
   524 		nindex * sizeof *index + hsize;
   525 
   526 	base = (struct rpm_header_index *) (rpm->header + 1);
   527 	rpm->pool = (void *) base + nindex * sizeof *index;
   528 
   529 	/* Look up dir names now so we can index them directly. */
   530 	name = razor_rpm_get_indirect(rpm, RPMTAG_DIRNAMES, &count);
   531 	if (name) {
   532 		rpm->dirs = calloc(count, sizeof *rpm->dirs);
   533 		for (i = 0; i < count; i++) {
   534 			rpm->dirs[i] = name;
   535 			name += strlen(name) + 1;
   536 		}
   537 	} else {
   538 		name = razor_rpm_get_indirect(rpm, RPMTAG_OLDFILENAMES,
   539 					      &count);
   540 		if (name) {
   541 			fprintf(stderr, "old filenames not supported\n");
   542 			return NULL;
   543 		}
   544 	}
   545 
   546 	prefix = razor_rpm_get_indirect(rpm, RPMTAG_PREFIXES, &count);
   547 	if (prefix) {
   548 		rpm->prefixes = calloc(count, sizeof *rpm->prefixes);
   549 		for (i = 0; i < count; i++) {
   550 			rpm->prefixes[i] = prefix;
   551 			prefix += strlen(prefix) + 1;
   552 		}
   553 		rpm->n_prefixes = count;
   554 	} else {
   555 		prefix = razor_rpm_get_indirect(rpm, RPMTAG_DEFAULTPREFIX,
   556 						&count);
   557 		if (prefix) {
   558 			fprintf(stderr, "default prefix not supported\n");
   559 			return NULL;
   560 		}
   561 	}
   562 
   563 	return rpm;
   564 }
   565 
   566 RAZOR_EXPORT void razor_rpm_set_relocations(struct razor_rpm *rpm,
   567 					    struct razor_relocations *rr)
   568 {
   569 	assert (rpm != NULL);
   570 
   571 	rpm->relocations = rr;
   572 }
   573 
   574 struct cpio_file_header {
   575 	char magic[6];
   576 	char inode[8];
   577 	char mode[8];
   578 	char uid[8];
   579 	char gid[8];
   580 	char nlink[8];
   581 	char mtime[8];
   582 	char filesize[8];
   583 	char devmajor[8];
   584 	char devminor[8];
   585 	char rdevmajor[8];
   586 	char rdevminor[8];
   587 	char namesize[8];
   588 	char checksum[8];
   589 	char filename[0];
   590 };
   591 
   592 /* gzip flags */
   593 #define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
   594 #define HEAD_CRC     0x02 /* bit 1 set: header CRC present */
   595 #define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
   596 #define ORIG_NAME    0x08 /* bit 3 set: original file name present */
   597 #define COMMENT      0x10 /* bit 4 set: file comment present */
   598 #define RESERVED     0xE0 /* bits 5..7: reserved */
   599 
   600 struct installer {
   601 	const char *root;
   602 	struct razor_rpm *rpm;
   603 	z_stream stream;
   604 	unsigned char buffer[32768];
   605 	size_t rest, length;
   606 };
   607 
   608 static int
   609 installer_inflate(struct installer *installer)
   610 {
   611 	size_t length;
   612 	int err;
   613 
   614 	if (installer->rest > sizeof installer->buffer)
   615 		length = sizeof installer->buffer;
   616 	else
   617 		length = installer->rest;
   618 
   619 	installer->stream.next_out = installer->buffer;
   620 	installer->stream.avail_out = length;
   621 	err = inflate(&installer->stream, Z_SYNC_FLUSH);
   622 	if (err != Z_OK && err != Z_STREAM_END) {
   623 		fprintf(stderr, "inflate error: %d (%s)\n", err,
   624 			strerror(errno));
   625 		return -1;
   626 	}
   627 
   628 	installer->rest -= length;
   629 	installer->length = length;
   630 
   631 	return 0;
   632 }
   633 
   634 static int
   635 installer_align(struct installer *installer, size_t size)
   636 {
   637 	unsigned char buffer[4];
   638 	int err;
   639 
   640 	installer->stream.next_out = buffer;
   641 	installer->stream.avail_out =
   642 		(size - installer->stream.total_out) & (size - 1);
   643 
   644 	if (installer->stream.avail_out == 0)
   645 		return 0;
   646 
   647 	err = inflate(&installer->stream, Z_SYNC_FLUSH);
   648 	if (err != Z_OK && err != Z_STREAM_END) {
   649 		fprintf(stderr, "inflate error: %d (%s)\n", err,
   650 			strerror(errno));
   651 		return -1;
   652 	}
   653 
   654 	return 0;
   655 }
   656 
   657 static int
   658 create_path(struct installer *installer, const char *path, unsigned int mode)
   659 {
   660 	char buffer[PATH_MAX];
   661 	struct stat buf;
   662 	int fd, ret;
   663 
   664 	if (razor_create_dir(installer->root, path) < 0)
   665 		return -1;
   666 
   667 	snprintf(buffer, sizeof buffer, "%s%s", installer->root, path);
   668 
   669 	switch (mode >> 12) {
   670 	case REG:
   671 		/* FIXME: handle the case where a file is already there. */
   672 		fd = open(buffer, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
   673 			  mode & 0x1ff);
   674 		if (fd < 0){
   675 			fprintf(stderr, "failed to create file %s\n", buffer);
   676 			return -1;
   677 		}
   678 		while (installer->rest > 0) {
   679 			if (installer_inflate(installer)) {
   680 				fprintf(stderr, "failed to inflate\n");
   681 				return -1;
   682 			}
   683 			if (razor_write(fd, installer->buffer,
   684 					installer->length)) {
   685 				fprintf(stderr, "failed to write payload\n");
   686 				return -1;
   687 			}
   688 		}
   689 		if (close(fd) < 0) {
   690 			fprintf(stderr, "failed to close %s: %s\n", buffer,
   691 				strerror(errno));
   692 			return -1;
   693 		}
   694 		return 0;
   695 	case XDIR:
   696 		ret = mkdir(buffer, mode & 0x1ff);
   697 		if (ret == 0 || errno != EEXIST)
   698 			return ret;
   699 		if (stat(buffer, &buf) || !S_ISDIR(buf.st_mode)) {
   700 			/* FIXME: also check that mode match. */
   701 			fprintf(stderr,
   702 				"%s exists but is not a directory\n", buffer);
   703 			return -1;
   704 		}
   705 		return 0;
   706 	case LINK:
   707 #if HAVE_SYMLINK
   708 		if (installer_inflate(installer)) {
   709 			fprintf(stderr, "failed to inflate\n");
   710 			return -1;
   711 		}
   712 		if (installer->length >= sizeof installer->buffer) {
   713 			fprintf(stderr, "link name too long\n");
   714 			return -1;
   715 		}
   716 		installer->buffer[installer->length] = '\0';
   717 		if (symlink((const char *) installer->buffer, buffer)) {
   718 			perror("failed to create symlink");
   719 			return -1;
   720 		}
   721 		return 0;
   722 #else
   723 		/* fall through */
   724 #endif
   725 	case PIPE:
   726 	case CDEV:
   727 	case BDEV:
   728 	case SOCK:
   729 		printf("%s: unhandled file type %d\n", buffer, mode >> 12);
   730 		return 0;
   731 	default:
   732 		printf("%s: unknown file type %d\n", buffer, mode >> 12);
   733 		return 0;
   734 	}
   735 }
   736 
   737 static int chroot_push(const char *root)
   738 {
   739 	int fd;
   740 #if HAVE_CHROOT
   741 	if (geteuid() == 0) {
   742 		fd = open("/", O_RDONLY, 0);
   743 		if (chroot(root) < 0) {
   744 			fprintf(stderr, "failed to chroot to %s: %s\n",
   745 				root, strerror(errno));
   746 			exit(-1);
   747 		}
   748 	} else
   749 #endif
   750 		fd = -1;
   751 	return fd;
   752 }
   753 
   754 static void chroot_pop(int fd)
   755 {
   756 #if HAVE_CHROOT
   757 	if (fd >= 0) {
   758 		fchdir(fd);
   759 		close(fd);
   760 		chroot(".");
   761 	}
   762 #endif
   763 }
   764 
   765 static int
   766 run_script_lua(const char *root, unsigned int script_tag, const char *script)
   767 {
   768 	int root_fd, retval;
   769 #if HAVE_LUA
   770 	const char *name;
   771 
   772 	switch(script_tag) {
   773 		case RPMTAG_PREIN:
   774 			name = "%pre";
   775 			break;
   776 		case RPMTAG_POSTIN:
   777 			name = "%post";
   778 			break;
   779 		case RPMTAG_PREUN:
   780 			name = "%preun";
   781 			break;
   782 		case RPMTAG_POSTUN:
   783 			name = "%postun";
   784 			break;
   785 		default:
   786 			name = "script";
   787 			break;
   788 	}
   789 	root_fd = chroot_push(root);
   790 	retval = run_lua_script(root_fd < 0 ? root : NULL, name, script, -1);
   791 	chroot_pop(root_fd);
   792 #else	/* HAVE_LUA */
   793 	fprintf(stderr, "lua not available to run script\n");
   794 	retval = -1;
   795 #endif	/* HAVE_LUA */
   796 
   797 	return retval;
   798 }
   799 
   800 static int
   801 run_script_external(const char *root, const char *program, const char *script)
   802 {
   803 	int root_fd, retval;
   804 	FILE *fp;
   805 
   806 	if (program == NULL) {
   807 #if MSWIN_API
   808 		program = getenv("COMSPEC");
   809 		if (program) {
   810 			program = strchr(program, '=');
   811 			if (program)
   812 				program++;
   813 		}
   814 		if (!program)
   815 			program = "c:\\windows\\system32\\cmd.exe";
   816 #else
   817 		program = "/bin/sh";
   818 #endif
   819 	}
   820 
   821 	root_fd = chroot_push(root);
   822 	fp = popen(program, "w");
   823 
   824 	if (!fp) {
   825 		perror(program);
   826 		retval = -1;
   827 	} else if (fwrite(script, strlen(script), 1, fp) != 1) {
   828 		perror("failed to write script to program");
   829 		retval = -1;
   830 	} else
   831 		retval = 0;
   832 
   833 	if (fp)
   834 		pclose(fp);
   835 	chroot_pop(root_fd);
   836 
   837 	return retval;
   838 }
   839 
   840 static int
   841 run_script(struct installer *installer,
   842 	   unsigned int program_tag, unsigned int script_tag)
   843 {
   844 	int retval;
   845 	const char *script = NULL, *program = NULL;
   846 
   847 	program = razor_rpm_get_indirect(installer->rpm, program_tag, NULL);
   848 	script = razor_rpm_get_indirect(installer->rpm, script_tag, NULL);
   849 	if (program == NULL && script == NULL)
   850 		retval = 0;
   851 	else if (strcmp(program, "<lua>") == 0)
   852 		retval = run_script_lua(installer->root, script_tag, script);
   853 	else
   854 		retval = run_script_external(installer->root, program, script);
   855 
   856 	return retval;
   857 }
   858 
   859 static int
   860 installer_init(struct installer *installer)
   861 {
   862 	unsigned char *gz_header;
   863 	int method, flags, err;
   864 
   865 	gz_header = installer->rpm->payload;
   866 	if (gz_header[0] != 0x1f || gz_header[1] != 0x8b) {
   867 		fprintf(stderr, "payload section doesn't have gz header\n");
   868 		return -1;
   869 	}
   870 
   871 	method = gz_header[2];
   872 	flags = gz_header[3];
   873 
   874 	if (method != Z_DEFLATED || flags != 0) {
   875 		fprintf(stderr,
   876 			"unknown payload compression method or flags set\n");
   877 		return -1;
   878 	}
   879 
   880 	installer->stream.zalloc = NULL;
   881 	installer->stream.zfree = NULL;
   882 	installer->stream.opaque = NULL;
   883 
   884 	installer->stream.next_in  = gz_header + 10;
   885 	installer->stream.avail_in =
   886 		(installer->rpm->map + installer->rpm->size) -
   887 		(void *) installer->stream.next_in;
   888 	installer->stream.next_out = NULL;
   889 	installer->stream.avail_out = 0;
   890 
   891 	err = inflateInit2(&installer->stream, -MAX_WBITS);
   892 	if (err != Z_OK) {
   893 		fprintf(stderr, "inflateInit error: %d\n", err);
   894 		return -1;
   895 	}
   896 
   897 	return 0;
   898 }
   899 
   900 static int
   901 installer_finish(struct installer *installer)
   902 {
   903 	int err;
   904 
   905 	err = inflateEnd(&installer->stream);
   906 
   907 	if (err != Z_OK) {
   908 		fprintf(stderr, "inflateEnd error: %d\n", err);
   909 		return -1;
   910 	}
   911 
   912 	return 0;
   913 }
   914 
   915 static unsigned long
   916 fixed_hex_to_ulong(const char *hex, int length)
   917 {
   918 	long l;
   919 	int i;
   920 
   921 	for (i = 0, l = 0; i < length; i++) {
   922 		if (hex[i] < 'a')
   923 			l = l * 16 + hex[i] - '0';
   924 		else
   925 			l = l * 16 + hex[i] - 'a' + 10;
   926 	}
   927 
   928 	return l;
   929 }
   930 
   931 RAZOR_EXPORT int
   932 razor_rpm_install(struct razor_rpm *rpm, const char *root)
   933 {
   934 	struct installer installer;
   935 	struct cpio_file_header *header;
   936 	struct stat buf;
   937 	unsigned int mode;
   938 	const char *path;
   939 	size_t filesize;
   940 
   941 	assert (rpm != NULL);
   942 	assert (root != NULL);
   943 
   944 	installer.rpm = rpm;
   945 	installer.root = root;
   946 
   947 	/* FIXME: Only do this before a transaction, not per rpm. */
   948 	if (*root && (stat(root, &buf) < 0 || !S_ISDIR(buf.st_mode))) {
   949 		fprintf(stderr,
   950 			"root installation directory \"%s\" does not exist\n",
   951 			root);
   952 		return -1;
   953 	}
   954 
   955 	if (rpm->relocations)
   956 		razor_relocations_set_rpm(rpm->relocations, rpm);
   957 
   958 	if (installer_init(&installer))
   959 		return -1;
   960 
   961 	run_script(&installer, RPMTAG_PREINPROG, RPMTAG_PREIN);
   962 
   963 	while (installer.stream.avail_in > 0) {
   964 		installer.rest = sizeof *header;
   965 		if (installer_inflate(&installer))
   966 			return -1;
   967 
   968 		header = (struct cpio_file_header *) installer.buffer;
   969 		mode = fixed_hex_to_ulong(header->mode, sizeof header->mode);
   970 		filesize = fixed_hex_to_ulong(header->filesize,
   971 					      sizeof header->filesize);
   972 
   973 		installer.rest = fixed_hex_to_ulong(header->namesize,
   974 						    sizeof header->namesize);
   975 
   976 		if (installer_inflate(&installer) ||
   977 		    installer_align(&installer, 4))
   978 			return -1;
   979 
   980 		path = (const char *) installer.buffer;
   981 		/* This convention is so lame... */
   982 		if (strcmp(path, "TRAILER!!!") == 0)
   983 			break;
   984 
   985 		installer.rest = filesize;
   986 		path++;
   987 		if (rpm->relocations)
   988 			path = razor_relocations_apply(rpm->relocations, path);
   989 		if (create_path(&installer, path, mode) < 0)
   990 			return -1;
   991 		if (installer_align(&installer, 4))
   992 			return -1;
   993 	}
   994 
   995 	if (installer_finish(&installer))
   996 		return -1;
   997 
   998 	run_script(&installer, RPMTAG_POSTINPROG, RPMTAG_POSTIN);
   999 
  1000 	return 0;
  1001 }
  1002 
  1003 RAZOR_EXPORT int
  1004 razor_rpm_close(struct razor_rpm *rpm)
  1005 {
  1006 	int err;
  1007 
  1008 	assert (rpm != NULL);
  1009 
  1010 	free(rpm->dirs);
  1011 	free(rpm->prefixes);
  1012 	err = razor_file_free_contents(rpm->map, rpm->size);
  1013 	free(rpm);
  1014 
  1015 	return err;
  1016 }
  1017 
  1018 RAZOR_EXPORT int
  1019 razor_importer_add_rpm(struct razor_importer *importer, struct razor_rpm *rpm)
  1020 {
  1021 	const char *name, *version, *release, *arch;
  1022 	const char *summary, *description, *url, *license;
  1023 	const uint32_t *epoch;
  1024 	char evr[128], buf[16];
  1025 
  1026 	assert (importer != NULL);
  1027 	assert (rpm != NULL);
  1028 
  1029 	name = razor_rpm_get_indirect(rpm, RPMTAG_NAME, NULL);
  1030 	epoch = razor_rpm_get_indirect(rpm, RPMTAG_EPOCH, NULL);
  1031 	version = razor_rpm_get_indirect(rpm, RPMTAG_VERSION, NULL);
  1032 	release = razor_rpm_get_indirect(rpm, RPMTAG_RELEASE, NULL);
  1033 	arch = razor_rpm_get_indirect(rpm, RPMTAG_ARCH, NULL);
  1034 
  1035 	summary = razor_rpm_get_indirect(rpm, RPMTAG_SUMMARY, NULL);
  1036 	description = razor_rpm_get_indirect(rpm, RPMTAG_DESCRIPTION, NULL);
  1037 	url = razor_rpm_get_indirect(rpm, RPMTAG_URL, NULL);
  1038 	license = razor_rpm_get_indirect(rpm, RPMTAG_LICENSE, NULL);
  1039 
  1040 	if (epoch) {
  1041 		snprintf(buf, sizeof buf, "%lu", ntohl(*epoch));
  1042 		razor_build_evr(evr, sizeof evr, buf, version, release);
  1043 	} else {
  1044 		razor_build_evr(evr, sizeof evr, NULL, version, release);
  1045 	}
  1046 	razor_importer_begin_package(importer, name, evr, arch);
  1047 
  1048 	razor_importer_add_details(importer, summary, description, url,
  1049 				   license);
  1050 
  1051 	import_properties(importer, RAZOR_PROPERTY_REQUIRES, rpm,
  1052 			  RPMTAG_REQUIRENAME,
  1053 			  RPMTAG_REQUIREVERSION,
  1054 			  RPMTAG_REQUIREFLAGS);
  1055 
  1056 	import_properties(importer, RAZOR_PROPERTY_PROVIDES, rpm,
  1057 			  RPMTAG_PROVIDENAME,
  1058 			  RPMTAG_PROVIDEVERSION,
  1059 			  RPMTAG_PROVIDEFLAGS);
  1060 
  1061 	import_properties(importer, RAZOR_PROPERTY_OBSOLETES, rpm,
  1062 			  RPMTAG_OBSOLETENAME,
  1063 			  RPMTAG_OBSOLETEVERSION,
  1064 			  RPMTAG_OBSOLETEFLAGS);
  1065 
  1066 	import_properties(importer, RAZOR_PROPERTY_CONFLICTS, rpm,
  1067 			  RPMTAG_CONFLICTNAME,
  1068 			  RPMTAG_CONFLICTVERSION,
  1069 			  RPMTAG_CONFLICTFLAGS);
  1070 
  1071 	import_files(importer, rpm);
  1072 
  1073 	razor_importer_finish_package(importer);
  1074 
  1075 	return 0;
  1076 }