librazor/rpm.c
author Kristian H?gsberg <krh@redhat.com>
Fri Jun 20 21:56:43 2008 -0400 (2008-06-20)
changeset 254 ccb1c11968ab
parent 246 f92d8239324e
child 256 3603b635d6c9
child 258 29d5002bd17f
permissions -rw-r--r--
Introduce install/remove iterators.

These iterator constructors lets you pass in two sets and creates
an iterator for the packages to remove or the packages to install.
The iterators will step through the packages in a sequence that respects
the pre, post, preun and postun modifiers.

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