rpm.c
changeset 81 7bc11dfb4bbd
parent 75 93278d8ec39c
child 84 6883222205ad
     1.1 --- a/rpm.c	Thu Nov 08 17:14:19 2007 -0500
     1.2 +++ b/rpm.c	Tue Nov 13 01:08:08 2007 -0500
     1.3 @@ -6,6 +6,7 @@
     1.4  #include <unistd.h>
     1.5  #include <arpa/inet.h>
     1.6  #include <rpm/rpmlib.h>
     1.7 +#include <zlib.h>
     1.8  
     1.9  #include "razor.h"
    1.10  
    1.11 @@ -43,7 +44,7 @@
    1.12  	struct rpm_header_index *flags;
    1.13  };
    1.14  
    1.15 -struct rpm {
    1.16 +struct razor_rpm {
    1.17  	struct rpm_header *signature;
    1.18  	struct rpm_header *header;
    1.19  
    1.20 @@ -54,6 +55,10 @@
    1.21  	struct rpm_header_index *dirnames;
    1.22  	struct rpm_header_index *dirindexes;
    1.23  	struct rpm_header_index *basenames;
    1.24 +	struct rpm_header_index *filesizes;
    1.25 +	struct rpm_header_index *filemodes;
    1.26 +	struct rpm_header_index *filestates;
    1.27 +	const char **dirs;
    1.28  
    1.29  	struct properties provides;
    1.30  	struct properties requires;
    1.31 @@ -63,6 +68,7 @@
    1.32  	const char *pool;
    1.33  	void *map;
    1.34  	size_t size;
    1.35 +	void *payload;
    1.36  };
    1.37  
    1.38  #define ALIGN(value, base) (((value) + (base - 1)) & ~((base) - 1))
    1.39 @@ -91,9 +97,9 @@
    1.40  }
    1.41  
    1.42  static void
    1.43 -import_files(struct razor_importer *importer, struct rpm *rpm)
    1.44 +import_files(struct razor_importer *importer, struct razor_rpm *rpm)
    1.45  {
    1.46 -	const char *name, **dir;
    1.47 +	const char *name;
    1.48  	unsigned long *index;
    1.49  	int i, count;
    1.50  	char buffer[256];
    1.51 @@ -103,49 +109,44 @@
    1.52  	if (rpm->dirnames == NULL)
    1.53  		return;
    1.54  
    1.55 -	count = ntohl(rpm->dirnames->count);
    1.56 -	dir = calloc(count, sizeof *dir);
    1.57 -	name = rpm->pool + ntohl(rpm->dirnames->offset);
    1.58 -	for (i = 0; i < count; i++) {
    1.59 -		dir[i] = name;
    1.60 -		name += strlen(name) + 1;
    1.61 -	}
    1.62 -
    1.63  	count = ntohl(rpm->basenames->count);
    1.64  	index = (unsigned long *) (rpm->pool + ntohl(rpm->dirindexes->offset));
    1.65  	name = rpm->pool + ntohl(rpm->basenames->offset);
    1.66  	for (i = 0; i < count; i++) {
    1.67  		snprintf(buffer, sizeof buffer,
    1.68 -			 "%s%s", dir[ntohl(*index)], name);
    1.69 +			 "%s%s", rpm->dirs[ntohl(*index)], name);
    1.70  		razor_importer_add_file(importer, buffer);
    1.71  		name += strlen(name) + 1;
    1.72  		index++;
    1.73  	}
    1.74  }
    1.75  
    1.76 -static int
    1.77 -razor_rpm_open(struct rpm *rpm, const char *filename)
    1.78 +struct razor_rpm *
    1.79 +razor_rpm_open(const char *filename)
    1.80  {
    1.81 +	struct razor_rpm *rpm;
    1.82  	struct rpm_header_index *base, *index;
    1.83  	struct stat buf;
    1.84 -	int fd, nindex, hsize, i;
    1.85 +	int fd, nindex, hsize, i, count;
    1.86 +	const char *name;
    1.87  
    1.88 +	rpm = malloc(sizeof *rpm);
    1.89  	memset(rpm, 0, sizeof *rpm);
    1.90  	if (stat(filename, &buf) < 0) {
    1.91  		fprintf(stderr, "no such file %s (%m)\n", filename);
    1.92 -		return -1;
    1.93 +		return NULL;
    1.94  	}
    1.95  
    1.96  	fd = open(filename, O_RDONLY);
    1.97  	if (fd < 0) {
    1.98  		fprintf(stderr, "couldn't open %s\n", filename);
    1.99 -		return -1;
   1.100 +		return NULL;
   1.101  	}
   1.102  	rpm->size = buf.st_size;
   1.103  	rpm->map = mmap(NULL, rpm->size, PROT_READ, MAP_PRIVATE, fd, 0);
   1.104  	if (rpm->map == MAP_FAILED) {
   1.105  		fprintf(stderr, "couldn't mmap %s\n", filename);
   1.106 -		return -1;
   1.107 +		return NULL;
   1.108  	}
   1.109  	close(fd);
   1.110  
   1.111 @@ -154,8 +155,11 @@
   1.112  	hsize = ntohl(rpm->signature->hsize);
   1.113  	rpm->header = (void *) (rpm->signature + 1) +
   1.114  		ALIGN(nindex * sizeof *index + hsize, 8);
   1.115 +	nindex = ntohl(rpm->header->nindex);
   1.116 +	hsize = ntohl(rpm->header->hsize);
   1.117 +	rpm->payload = (void *) (rpm->header + 1) +
   1.118 +		nindex * sizeof *index + hsize;
   1.119  
   1.120 -	nindex = ntohl(rpm->header->nindex);
   1.121  	base = (struct rpm_header_index *) (rpm->header + 1);
   1.122  	rpm->pool = (void *) base + nindex * sizeof *index;
   1.123  
   1.124 @@ -221,45 +225,263 @@
   1.125  		case RPMTAG_DIRNAMES:
   1.126  			rpm->dirnames = index;
   1.127  			break;
   1.128 +		case RPMTAG_FILESIZES:
   1.129 +			rpm->filesizes = index;
   1.130 +			break;
   1.131 +		case RPMTAG_FILEMODES:
   1.132 +			rpm->filemodes = index;
   1.133 +			break;
   1.134 +		case RPMTAG_FILESTATES:
   1.135 +			rpm->filestates = index;
   1.136 +			break;
   1.137  		}
   1.138  	}
   1.139  
   1.140 +	/* Look up dir names now so we can index them directly. */
   1.141 +	if (rpm->dirnames != NULL) {
   1.142 +		count = ntohl(rpm->dirnames->count);
   1.143 +		rpm->dirs = calloc(count, sizeof *rpm->dirs);
   1.144 +		name = rpm->pool + ntohl(rpm->dirnames->offset);
   1.145 +		for (i = 0; i < count; i++) {
   1.146 +			rpm->dirs[i] = name;
   1.147 +			name += strlen(name) + 1;
   1.148 +		}
   1.149 +	}
   1.150 +
   1.151 +	return rpm;
   1.152 +}
   1.153 +
   1.154 +struct cpio_file_header {
   1.155 +	char magic[6];
   1.156 +	char inode[8];
   1.157 +	char mode[8];
   1.158 +	char uid[8];
   1.159 +	char gid[8];
   1.160 +	char nlink[8];
   1.161 +	char mtime[8];
   1.162 +	char filesize[8];
   1.163 +	char devmajor[8];
   1.164 +	char devminor[8];
   1.165 +	char rdevmajor[8];
   1.166 +	char rdevminor[8];
   1.167 +	char namesize[8];
   1.168 +	char checksum[8];
   1.169 +	char filename[0];
   1.170 +};
   1.171 +
   1.172 +/* gzip flags */
   1.173 +#define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
   1.174 +#define HEAD_CRC     0x02 /* bit 1 set: header CRC present */
   1.175 +#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
   1.176 +#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
   1.177 +#define COMMENT      0x10 /* bit 4 set: file comment present */
   1.178 +#define RESERVED     0xE0 /* bits 5..7: reserved */
   1.179 +
   1.180 +static int
   1.181 +create_path(const char *root, const char *path,
   1.182 +	    const char *name, unsigned mode, int *fd)
   1.183 +{
   1.184 +	char buffer[256], *p;
   1.185 +	const char *slash, *next;
   1.186 +	struct stat buf;
   1.187 +
   1.188 +	/* Create all sub-directories in dir and then create name. We
   1.189 +	 * know root exists and is a dir, root does not end in a '/',
   1.190 +	 * and path has a leading '/'. */
   1.191 +
   1.192 +	strcpy(buffer, root);
   1.193 +	p = buffer + strlen(buffer);
   1.194 +	slash = path;
   1.195 +	for (slash = path; slash[1] != '\0'; slash = next) {
   1.196 +		next = strchr(slash + 1, '/');
   1.197 +		memcpy(p, slash, next - slash);
   1.198 +		p += next - slash;
   1.199 +		*p = '\0';
   1.200 +
   1.201 +		if (stat(buffer, &buf) == 0) {
   1.202 +			if (!S_ISDIR(buf.st_mode)) {
   1.203 +				fprintf(stderr,
   1.204 +					"%s exists but is not a directory\n",
   1.205 +					buffer);
   1.206 +				return -1;
   1.207 +			}
   1.208 +		} else if (mkdir(buffer, 0777) < 0) {
   1.209 +			fprintf(stderr, "failed to make directory %s: %m\n",
   1.210 +				buffer);
   1.211 +			return -1;
   1.212 +		}
   1.213 +		/* FIXME: permissions */
   1.214 +	}
   1.215 +
   1.216 +	*p++ = '/';
   1.217 +	strcpy(p, name);
   1.218 +
   1.219 +	switch (mode >> 12) {
   1.220 +	case REG:
   1.221 +	default:
   1.222 +		*fd = open(buffer, O_WRONLY | O_CREAT | O_TRUNC, mode & 0x1ff);
   1.223 +		return *fd;
   1.224 +	case XDIR:
   1.225 +		*fd = -1;
   1.226 +		return mkdir(buffer, mode & 0x1ff);
   1.227 +	}
   1.228 +}
   1.229 +
   1.230 +int
   1.231 +razor_rpm_install(struct razor_rpm *rpm, const char *root)
   1.232 +{
   1.233 +	z_stream stream;
   1.234 +	unsigned char payload[32768], *gz_header;
   1.235 +	char buffer[256];
   1.236 +	int err, method, flags, count, i, fd, written;
   1.237 +	struct cpio_file_header *header;
   1.238 +	unsigned long *size, *index, rest, length;
   1.239 +	unsigned short *mode;
   1.240 +	const char *name, *dir;
   1.241 +	struct stat buf;
   1.242 +
   1.243 +	if (stat(root, &buf) < 0 || !S_ISDIR(buf.st_mode)) {
   1.244 +		fprintf(stderr,
   1.245 +			"root installation directory \"%s\" does not exist\n",
   1.246 +			root);
   1.247 +		return -1;
   1.248 +	}
   1.249 +
   1.250 +	gz_header = rpm->payload;
   1.251 +	if (gz_header[0] != 0x1f || gz_header[1] != 0x8b) {
   1.252 +		fprintf(stderr, "payload section doesn't have gz header\n");
   1.253 +		return -1;
   1.254 +	}
   1.255 +
   1.256 +	method = gz_header[2];
   1.257 +	flags = gz_header[3];
   1.258 +
   1.259 +	if (method != Z_DEFLATED || flags != 0) {
   1.260 +		fprintf(stderr,
   1.261 +			"unknown payload compression method or flags set\n");
   1.262 +		return -1;
   1.263 +	}
   1.264 +
   1.265 +	stream.zalloc = NULL;
   1.266 +	stream.zfree = NULL;
   1.267 +	stream.opaque = NULL;
   1.268 +
   1.269 +	stream.next_in  = gz_header + 10;
   1.270 +	stream.avail_in = (rpm->map + rpm->size) - (void *) stream.next_in;
   1.271 +	stream.next_out = NULL;
   1.272 +	stream.avail_out = 0;
   1.273 +
   1.274 +	err = inflateInit2(&stream, -MAX_WBITS);
   1.275 +	if (err != Z_OK) {
   1.276 +		fprintf(stderr, "inflateInit error: %d\n", err);
   1.277 +		return -1;
   1.278 +	}
   1.279 +
   1.280 +	count = ntohl(rpm->basenames->count);
   1.281 +	size = (unsigned long *) (rpm->pool + ntohl(rpm->filesizes->offset));
   1.282 +	index = (unsigned long *) (rpm->pool + ntohl(rpm->dirindexes->offset));
   1.283 +	mode = (unsigned short *) (rpm->pool + ntohl(rpm->filemodes->offset));
   1.284 +	name = rpm->pool + ntohl(rpm->basenames->offset);
   1.285 +	for (i = 0; i < count; i++) {
   1.286 +		dir = rpm->dirs[ntohl(*index)];
   1.287 +		snprintf(buffer, sizeof buffer, "%s%s", dir, name);
   1.288 +
   1.289 +		stream.next_out = payload;
   1.290 +		/* Plus two for the leading '.' and the terminating NUL. */
   1.291 +		stream.avail_out =
   1.292 +			ALIGN(sizeof *header + strlen(buffer) + 2, 4);
   1.293 +		err = inflate(&stream, Z_SYNC_FLUSH);
   1.294 +		if (err != Z_OK) {
   1.295 +			fprintf(stderr, "inflate error: %d\n", err);
   1.296 +			return -1;
   1.297 +		}
   1.298 +	    
   1.299 +		header = (struct cpio_file_header *) payload;
   1.300 +
   1.301 +		/* FIXME: Figure out if it's a symlink, device file,
   1.302 +		 * directorys or whatever.  Maybe do this upfront. */
   1.303 +		if (create_path(root, dir, name, ntohs(*mode), &fd) < 0)
   1.304 +			return -1;
   1.305 +		if (ntohs(*mode) >> 12 == XDIR)
   1.306 +			rest = 0;
   1.307 +		else
   1.308 +			rest = ntohl(*size);
   1.309 +
   1.310 +		while (rest > 0) {
   1.311 +			if (ALIGN(rest, 4) > sizeof payload)
   1.312 +				length = sizeof payload;
   1.313 +			else
   1.314 +				length = rest;
   1.315 +			stream.next_out = payload;
   1.316 +			stream.avail_out = ALIGN(length, 4);
   1.317 +			err = inflate(&stream, Z_SYNC_FLUSH);
   1.318 +			if (err != Z_OK && err != Z_STREAM_END) {
   1.319 +				fprintf(stderr,
   1.320 +					"inflate error: %d (%m)\n", err);
   1.321 +				return -1;
   1.322 +			}
   1.323 +			rest -= length;
   1.324 +			stream.next_out = payload;
   1.325 +			while (length > 0) {
   1.326 +				written = write(fd, stream.next_out, length);
   1.327 +				if (written < 0) {
   1.328 +					fprintf(stderr, "write error: %m\n");
   1.329 +					return -1;
   1.330 +				}
   1.331 +				length -= written;
   1.332 +			}
   1.333 +		}
   1.334 +		if (fd > 0 && close(fd) < 0) {
   1.335 +			fprintf(stderr, "failed to close \"%s/%s%s\": %m\n",
   1.336 +				root, dir, name);
   1.337 +			return -1;
   1.338 +		}
   1.339 +		name += strlen(name) + 1;
   1.340 +		index++;
   1.341 +		size++;
   1.342 +		mode++;
   1.343 +	}
   1.344 +
   1.345 +	err = inflateEnd(&stream);
   1.346 +
   1.347 +	if (err != Z_OK) {
   1.348 +		fprintf(stderr, "inflateEnd error: %d\n", err);
   1.349 +		return -1;
   1.350 +	}	    
   1.351 +
   1.352  	return 0;
   1.353  }
   1.354  
   1.355 -static int
   1.356 -razor_rpm_close(struct rpm *rpm)
   1.357 +int
   1.358 +razor_rpm_close(struct razor_rpm *rpm)
   1.359  {
   1.360 -	return munmap(rpm->map, rpm->size);
   1.361 +	int err;
   1.362 +
   1.363 +	free(rpm->dirs);
   1.364 +	err = munmap(rpm->map, rpm->size);
   1.365 +	free(rpm);
   1.366 +
   1.367 +	return err;
   1.368  }
   1.369  
   1.370  int
   1.371 -razor_importer_add_rpm(struct razor_importer *importer, const char *filename)
   1.372 +razor_importer_add_rpm(struct razor_importer *importer, struct razor_rpm *rpm)
   1.373  {
   1.374 -	struct rpm rpm;
   1.375 +	razor_importer_begin_package(importer,
   1.376 +				     rpm->pool + ntohl(rpm->name->offset),
   1.377 +				     rpm->pool + ntohl(rpm->version->offset));
   1.378  
   1.379 -	if (razor_rpm_open(&rpm, filename) < 0) {
   1.380 -		fprintf(stderr, "failed to open rpm %s (%m)\n", filename);
   1.381 -		return -1;
   1.382 -	}
   1.383 -
   1.384 -	razor_importer_begin_package(importer,
   1.385 -				     rpm.pool + ntohl(rpm.name->offset),
   1.386 -				     rpm.pool + ntohl(rpm.version->offset));
   1.387 -
   1.388 -	import_properties(importer, &rpm.requires,
   1.389 -			  rpm.pool, RAZOR_PROPERTY_REQUIRES);
   1.390 -	import_properties(importer, &rpm.provides,
   1.391 -			  rpm.pool, RAZOR_PROPERTY_PROVIDES);
   1.392 -	import_properties(importer, &rpm.conflicts,
   1.393 -			  rpm.pool, RAZOR_PROPERTY_CONFLICTS);
   1.394 -	import_properties(importer, &rpm.obsoletes,
   1.395 -			  rpm.pool, RAZOR_PROPERTY_OBSOLETES);
   1.396 -	import_files(importer, &rpm);
   1.397 +	import_properties(importer, &rpm->requires,
   1.398 +			  rpm->pool, RAZOR_PROPERTY_REQUIRES);
   1.399 +	import_properties(importer, &rpm->provides,
   1.400 +			  rpm->pool, RAZOR_PROPERTY_PROVIDES);
   1.401 +	import_properties(importer, &rpm->conflicts,
   1.402 +			  rpm->pool, RAZOR_PROPERTY_CONFLICTS);
   1.403 +	import_properties(importer, &rpm->obsoletes,
   1.404 +			  rpm->pool, RAZOR_PROPERTY_OBSOLETES);
   1.405 +	import_files(importer, rpm);
   1.406  
   1.407  	razor_importer_finish_package(importer);
   1.408  
   1.409 -	razor_rpm_close(&rpm);
   1.410 -
   1.411  	return 0;
   1.412  }