librazor/rpm.c
author James Bowes <jbowes@redhat.com>
Wed Jul 09 10:11:13 2008 -0400 (2008-07-09)
changeset 318 829d6711b316
parent 289 dc69c55cf462
child 322 66c281524c98
permissions -rw-r--r--
Use strings to identify section types in the on-disk repo format.

Previously, a given razor file type had a fixed number of sections in a
fixed order, identified by an integer type. Now, sections are identified
by a named string (stored in a string pool after the section lists).

This will allow for razor files to contain arbitrary sections.

For bonus points, also drop the 4k section alignment and change the
magic byte string to "RZDB".

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