First steps towards rpm installation.
authorKristian H?gsberg <krh@redhat.com>
Thu Nov 08 22:45:27 2007 -0500 (2007-11-08)
changeset 773d14834c56ea
parent 76 773e6a26707f
child 78 89c06e68824a
First steps towards rpm installation.
main.c
razor.h
rpm.c
     1.1 --- a/main.c	Thu Nov 08 22:36:16 2007 -0500
     1.2 +++ b/main.c	Thu Nov 08 22:45:27 2007 -0500
     1.3 @@ -298,6 +298,7 @@
     1.4  	struct dirent *de;
     1.5  	struct razor_importer *importer;
     1.6  	struct razor_set *set;
     1.7 +	struct razor_rpm *rpm;
     1.8  	int len;
     1.9  	char filename[256];
    1.10  	const char *dirname = argv[0];
    1.11 @@ -321,10 +322,17 @@
    1.12  		    continue;
    1.13  		snprintf(filename, sizeof filename,
    1.14  			 "%s/%s", dirname, de->d_name);
    1.15 -		if (razor_importer_add_rpm(importer, filename)) {
    1.16 +		rpm = razor_rpm_open(filename);
    1.17 +		if (rpm == NULL) {
    1.18 +			fprintf(stderr,
    1.19 +				"failed to open rpm \"%s\"\n", filename);
    1.20 +			continue;
    1.21 +		}
    1.22 +		if (razor_importer_add_rpm(importer, rpm)) {
    1.23  			fprintf(stderr, "couldn't import %s\n", filename);
    1.24  			break;
    1.25  		}
    1.26 +		razor_rpm_close(rpm);
    1.27  	}
    1.28  
    1.29  	if (de != NULL) {
    1.30 @@ -341,6 +349,44 @@
    1.31  	return 0;
    1.32  }
    1.33  
    1.34 +static int
    1.35 +command_install(int argc, const char *argv[])
    1.36 +{
    1.37 +	struct razor_rpm *rpm;
    1.38 +	const char *filename = argv[0];
    1.39 +	struct stat buf;
    1.40 +	const char root[] = "install";
    1.41 +
    1.42 +	if (stat(root, &buf) < 0) {
    1.43 +		if (mkdir(root, 0777) < 0) {
    1.44 +			fprintf(stderr,
    1.45 +				"could not create install root \"%s\"\n",
    1.46 +				root);
    1.47 +			return -1;
    1.48 +		}
    1.49 +		fprintf(stderr, "created install root \"%s\"\n", root);
    1.50 +	} else if (!S_ISDIR(buf.st_mode)) {
    1.51 +		fprintf(stderr,
    1.52 +			"install root \"%s\" exists, but is not a directory\n",
    1.53 +			root);
    1.54 +		return -1;
    1.55 +	}
    1.56 +
    1.57 +	rpm = razor_rpm_open(filename);
    1.58 +	if (rpm == NULL) {
    1.59 +		fprintf(stderr, "failed to open rpm %s\n", filename);
    1.60 +		return -1;
    1.61 +	}
    1.62 +	if (razor_rpm_install(rpm, root) < 0) {
    1.63 +		fprintf(stderr, "failed to install rpm %s\n", filename);
    1.64 +		return -1;
    1.65 +	}
    1.66 +	
    1.67 +	razor_rpm_close(rpm);
    1.68 +
    1.69 +	return 0;
    1.70 +}
    1.71 +
    1.72  static struct {
    1.73  	const char *name;
    1.74  	const char *description;
    1.75 @@ -361,7 +407,8 @@
    1.76  	{ "import-rpms", "import rpms from the given directory", command_import_rpms },
    1.77  	{ "validate", "validate a package set", command_validate },
    1.78  	{ "update", "update all or specified packages", command_update },
    1.79 -	{ "diff", "show diff between two package sets", command_diff }
    1.80 +	{ "diff", "show diff between two package sets", command_diff },
    1.81 +	{ "install", "install rpm", command_install }
    1.82  };
    1.83  
    1.84  static int
     2.1 --- a/razor.h	Thu Nov 08 22:36:16 2007 -0500
     2.2 +++ b/razor.h	Thu Nov 08 22:45:27 2007 -0500
     2.3 @@ -45,6 +45,7 @@
     2.4   * like yum, rpmdb or razor package files. */
     2.5  
     2.6  struct razor_importer;
     2.7 +struct razor_rpm;
     2.8  
     2.9  struct razor_importer *razor_importer_new(void);
    2.10  void razor_importer_destroy(struct razor_importer *importer);
    2.11 @@ -58,7 +59,7 @@
    2.12  void razor_importer_finish_package(struct razor_importer *importer);
    2.13  
    2.14  int razor_importer_add_rpm(struct razor_importer *importer,
    2.15 -			   const char *filename);
    2.16 +			   struct razor_rpm *rpm);
    2.17  
    2.18  struct razor_set *razor_importer_finish(struct razor_importer *importer);
    2.19  
    2.20 @@ -66,7 +67,9 @@
    2.21  struct razor_set *razor_set_create_from_rpmdb(void);
    2.22  
    2.23  /* RPM functions */
    2.24 -void
    2.25 -razor_rpm_dump(const char *filename);
    2.26 +
    2.27 +struct razor_rpm *razor_rpm_open(const char *filename);
    2.28 +int razor_rpm_install(struct razor_rpm *rpm, const char *root);
    2.29 +int razor_rpm_close(struct razor_rpm *rpm);
    2.30  
    2.31  #endif /* _RAZOR_H_ */
     3.1 --- a/rpm.c	Thu Nov 08 22:36:16 2007 -0500
     3.2 +++ b/rpm.c	Thu Nov 08 22:45:27 2007 -0500
     3.3 @@ -6,6 +6,7 @@
     3.4  #include <unistd.h>
     3.5  #include <arpa/inet.h>
     3.6  #include <rpm/rpmlib.h>
     3.7 +#include <zlib.h>
     3.8  
     3.9  #include "razor.h"
    3.10  
    3.11 @@ -43,7 +44,7 @@
    3.12  	struct rpm_header_index *flags;
    3.13  };
    3.14  
    3.15 -struct rpm {
    3.16 +struct razor_rpm {
    3.17  	struct rpm_header *signature;
    3.18  	struct rpm_header *header;
    3.19  
    3.20 @@ -54,6 +55,10 @@
    3.21  	struct rpm_header_index *dirnames;
    3.22  	struct rpm_header_index *dirindexes;
    3.23  	struct rpm_header_index *basenames;
    3.24 +	struct rpm_header_index *filesizes;
    3.25 +	struct rpm_header_index *filemodes;
    3.26 +	struct rpm_header_index *filestates;
    3.27 +	const char **dirs;
    3.28  
    3.29  	struct properties provides;
    3.30  	struct properties requires;
    3.31 @@ -63,6 +68,7 @@
    3.32  	const char *pool;
    3.33  	void *map;
    3.34  	size_t size;
    3.35 +	void *payload;
    3.36  };
    3.37  
    3.38  #define ALIGN(value, base) (((value) + (base - 1)) & ~((base) - 1))
    3.39 @@ -91,9 +97,9 @@
    3.40  }
    3.41  
    3.42  static void
    3.43 -import_files(struct razor_importer *importer, struct rpm *rpm)
    3.44 +import_files(struct razor_importer *importer, struct razor_rpm *rpm)
    3.45  {
    3.46 -	const char *name, **dir;
    3.47 +	const char *name;
    3.48  	unsigned long *index;
    3.49  	int i, count;
    3.50  	char buffer[256];
    3.51 @@ -103,49 +109,44 @@
    3.52  	if (rpm->dirnames == NULL)
    3.53  		return;
    3.54  
    3.55 -	count = ntohl(rpm->dirnames->count);
    3.56 -	dir = calloc(count, sizeof *dir);
    3.57 -	name = rpm->pool + ntohl(rpm->dirnames->offset);
    3.58 -	for (i = 0; i < count; i++) {
    3.59 -		dir[i] = name;
    3.60 -		name += strlen(name) + 1;
    3.61 -	}
    3.62 -
    3.63  	count = ntohl(rpm->basenames->count);
    3.64  	index = (unsigned long *) (rpm->pool + ntohl(rpm->dirindexes->offset));
    3.65  	name = rpm->pool + ntohl(rpm->basenames->offset);
    3.66  	for (i = 0; i < count; i++) {
    3.67  		snprintf(buffer, sizeof buffer,
    3.68 -			 "%s%s", dir[ntohl(*index)], name);
    3.69 +			 "%s%s", rpm->dirs[ntohl(*index)], name);
    3.70  		razor_importer_add_file(importer, buffer);
    3.71  		name += strlen(name) + 1;
    3.72  		index++;
    3.73  	}
    3.74  }
    3.75  
    3.76 -static int
    3.77 -razor_rpm_open(struct rpm *rpm, const char *filename)
    3.78 +struct razor_rpm *
    3.79 +razor_rpm_open(const char *filename)
    3.80  {
    3.81 +	struct razor_rpm *rpm;
    3.82  	struct rpm_header_index *base, *index;
    3.83  	struct stat buf;
    3.84 -	int fd, nindex, hsize, i;
    3.85 +	int fd, nindex, hsize, i, count;
    3.86 +	const char *name;
    3.87  
    3.88 +	rpm = malloc(sizeof *rpm);
    3.89  	memset(rpm, 0, sizeof *rpm);
    3.90  	if (stat(filename, &buf) < 0) {
    3.91  		fprintf(stderr, "no such file %s (%m)\n", filename);
    3.92 -		return -1;
    3.93 +		return NULL;
    3.94  	}
    3.95  
    3.96  	fd = open(filename, O_RDONLY);
    3.97  	if (fd < 0) {
    3.98  		fprintf(stderr, "couldn't open %s\n", filename);
    3.99 -		return -1;
   3.100 +		return NULL;
   3.101  	}
   3.102  	rpm->size = buf.st_size;
   3.103  	rpm->map = mmap(NULL, rpm->size, PROT_READ, MAP_PRIVATE, fd, 0);
   3.104  	if (rpm->map == MAP_FAILED) {
   3.105  		fprintf(stderr, "couldn't mmap %s\n", filename);
   3.106 -		return -1;
   3.107 +		return NULL;
   3.108  	}
   3.109  	close(fd);
   3.110  
   3.111 @@ -154,8 +155,11 @@
   3.112  	hsize = ntohl(rpm->signature->hsize);
   3.113  	rpm->header = (void *) (rpm->signature + 1) +
   3.114  		ALIGN(nindex * sizeof *index + hsize, 8);
   3.115 +	nindex = ntohl(rpm->header->nindex);
   3.116 +	hsize = ntohl(rpm->header->hsize);
   3.117 +	rpm->payload = (void *) (rpm->header + 1) +
   3.118 +		nindex * sizeof *index + hsize;
   3.119  
   3.120 -	nindex = ntohl(rpm->header->nindex);
   3.121  	base = (struct rpm_header_index *) (rpm->header + 1);
   3.122  	rpm->pool = (void *) base + nindex * sizeof *index;
   3.123  
   3.124 @@ -221,45 +225,263 @@
   3.125  		case RPMTAG_DIRNAMES:
   3.126  			rpm->dirnames = index;
   3.127  			break;
   3.128 +		case RPMTAG_FILESIZES:
   3.129 +			rpm->filesizes = index;
   3.130 +			break;
   3.131 +		case RPMTAG_FILEMODES:
   3.132 +			rpm->filemodes = index;
   3.133 +			break;
   3.134 +		case RPMTAG_FILESTATES:
   3.135 +			rpm->filestates = index;
   3.136 +			break;
   3.137  		}
   3.138  	}
   3.139  
   3.140 +	/* Look up dir names now so we can index them directly. */
   3.141 +	if (rpm->dirnames != NULL) {
   3.142 +		count = ntohl(rpm->dirnames->count);
   3.143 +		rpm->dirs = calloc(count, sizeof *rpm->dirs);
   3.144 +		name = rpm->pool + ntohl(rpm->dirnames->offset);
   3.145 +		for (i = 0; i < count; i++) {
   3.146 +			rpm->dirs[i] = name;
   3.147 +			name += strlen(name) + 1;
   3.148 +		}
   3.149 +	}
   3.150 +
   3.151 +	return rpm;
   3.152 +}
   3.153 +
   3.154 +struct cpio_file_header {
   3.155 +	char magic[6];
   3.156 +	char inode[8];
   3.157 +	char mode[8];
   3.158 +	char uid[8];
   3.159 +	char gid[8];
   3.160 +	char nlink[8];
   3.161 +	char mtime[8];
   3.162 +	char filesize[8];
   3.163 +	char devmajor[8];
   3.164 +	char devminor[8];
   3.165 +	char rdevmajor[8];
   3.166 +	char rdevminor[8];
   3.167 +	char namesize[8];
   3.168 +	char checksum[8];
   3.169 +	char filename[0];
   3.170 +};
   3.171 +
   3.172 +/* gzip flags */
   3.173 +#define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
   3.174 +#define HEAD_CRC     0x02 /* bit 1 set: header CRC present */
   3.175 +#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
   3.176 +#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
   3.177 +#define COMMENT      0x10 /* bit 4 set: file comment present */
   3.178 +#define RESERVED     0xE0 /* bits 5..7: reserved */
   3.179 +
   3.180 +static int
   3.181 +create_path(const char *root, const char *path,
   3.182 +	    const char *name, unsigned mode, int *fd)
   3.183 +{
   3.184 +	char buffer[256], *p;
   3.185 +	const char *slash, *next;
   3.186 +	struct stat buf;
   3.187 +
   3.188 +	/* Create all sub-directories in dir and then create name. We
   3.189 +	 * know root exists and is a dir, root does not end in a '/',
   3.190 +	 * and path has a leading '/'. */
   3.191 +
   3.192 +	strcpy(buffer, root);
   3.193 +	p = buffer + strlen(buffer);
   3.194 +	slash = path;
   3.195 +	for (slash = path; slash[1] != '\0'; slash = next) {
   3.196 +		next = strchr(slash + 1, '/');
   3.197 +		memcpy(p, slash, next - slash);
   3.198 +		p += next - slash;
   3.199 +		*p = '\0';
   3.200 +
   3.201 +		if (stat(buffer, &buf) == 0) {
   3.202 +			if (!S_ISDIR(buf.st_mode)) {
   3.203 +				fprintf(stderr,
   3.204 +					"%s exists but is not a directory\n",
   3.205 +					buffer);
   3.206 +				return -1;
   3.207 +			}
   3.208 +		} else if (mkdir(buffer, 0777) < 0) {
   3.209 +			fprintf(stderr, "failed to make directory %s: %m\n",
   3.210 +				buffer);
   3.211 +			return -1;
   3.212 +		}
   3.213 +		/* FIXME: permissions */
   3.214 +	}
   3.215 +
   3.216 +	*p++ = '/';
   3.217 +	strcpy(p, name);
   3.218 +
   3.219 +	switch (mode >> 12) {
   3.220 +	case REG:
   3.221 +	default:
   3.222 +		*fd = open(buffer, O_WRONLY | O_CREAT | O_TRUNC, mode & 0x1ff);
   3.223 +		return *fd;
   3.224 +	case XDIR:
   3.225 +		*fd = -1;
   3.226 +		return mkdir(buffer, mode & 0x1ff);
   3.227 +	}
   3.228 +}
   3.229 +
   3.230 +int
   3.231 +razor_rpm_install(struct razor_rpm *rpm, const char *root)
   3.232 +{
   3.233 +	z_stream stream;
   3.234 +	unsigned char payload[32768], *gz_header;
   3.235 +	char buffer[256];
   3.236 +	int err, method, flags, count, i, fd, written;
   3.237 +	struct cpio_file_header *header;
   3.238 +	unsigned long *size, *index, rest, length;
   3.239 +	unsigned short *mode;
   3.240 +	const char *name, *dir;
   3.241 +	struct stat buf;
   3.242 +
   3.243 +	if (stat(root, &buf) < 0 || !S_ISDIR(buf.st_mode)) {
   3.244 +		fprintf(stderr,
   3.245 +			"root installation directory \"%s\" does not exist\n",
   3.246 +			root);
   3.247 +		return -1;
   3.248 +	}
   3.249 +
   3.250 +	gz_header = rpm->payload;
   3.251 +	if (gz_header[0] != 0x1f || gz_header[1] != 0x8b) {
   3.252 +		fprintf(stderr, "payload section doesn't have gz header\n");
   3.253 +		return -1;
   3.254 +	}
   3.255 +
   3.256 +	method = gz_header[2];
   3.257 +	flags = gz_header[3];
   3.258 +
   3.259 +	if (method != Z_DEFLATED || flags != 0) {
   3.260 +		fprintf(stderr,
   3.261 +			"unknown payload compression method or flags set\n");
   3.262 +		return -1;
   3.263 +	}
   3.264 +
   3.265 +	stream.zalloc = NULL;
   3.266 +	stream.zfree = NULL;
   3.267 +	stream.opaque = NULL;
   3.268 +
   3.269 +	stream.next_in  = gz_header + 10;
   3.270 +	stream.avail_in = (rpm->map + rpm->size) - (void *) stream.next_in;
   3.271 +	stream.next_out = NULL;
   3.272 +	stream.avail_out = 0;
   3.273 +
   3.274 +	err = inflateInit2(&stream, -MAX_WBITS);
   3.275 +	if (err != Z_OK) {
   3.276 +		fprintf(stderr, "inflateInit error: %d\n", err);
   3.277 +		return -1;
   3.278 +	}
   3.279 +
   3.280 +	count = ntohl(rpm->basenames->count);
   3.281 +	size = (unsigned long *) (rpm->pool + ntohl(rpm->filesizes->offset));
   3.282 +	index = (unsigned long *) (rpm->pool + ntohl(rpm->dirindexes->offset));
   3.283 +	mode = (unsigned short *) (rpm->pool + ntohl(rpm->filemodes->offset));
   3.284 +	name = rpm->pool + ntohl(rpm->basenames->offset);
   3.285 +	for (i = 0; i < count; i++) {
   3.286 +		dir = rpm->dirs[ntohl(*index)];
   3.287 +		snprintf(buffer, sizeof buffer, "%s%s", dir, name);
   3.288 +
   3.289 +		stream.next_out = payload;
   3.290 +		/* Plus two for the leading '.' and the terminating NUL. */
   3.291 +		stream.avail_out =
   3.292 +			ALIGN(sizeof *header + strlen(buffer) + 2, 4);
   3.293 +		err = inflate(&stream, Z_SYNC_FLUSH);
   3.294 +		if (err != Z_OK) {
   3.295 +			fprintf(stderr, "inflate error: %d\n", err);
   3.296 +			return -1;
   3.297 +		}
   3.298 +	    
   3.299 +		header = (struct cpio_file_header *) payload;
   3.300 +
   3.301 +		/* FIXME: Figure out if it's a symlink, device file,
   3.302 +		 * directorys or whatever.  Maybe do this upfront. */
   3.303 +		if (create_path(root, dir, name, ntohs(*mode), &fd) < 0)
   3.304 +			return -1;
   3.305 +		if (ntohs(*mode) >> 12 == XDIR)
   3.306 +			rest = 0;
   3.307 +		else
   3.308 +			rest = ntohl(*size);
   3.309 +
   3.310 +		while (rest > 0) {
   3.311 +			if (ALIGN(rest, 4) > sizeof payload)
   3.312 +				length = sizeof payload;
   3.313 +			else
   3.314 +				length = rest;
   3.315 +			stream.next_out = payload;
   3.316 +			stream.avail_out = ALIGN(length, 4);
   3.317 +			err = inflate(&stream, Z_SYNC_FLUSH);
   3.318 +			if (err != Z_OK && err != Z_STREAM_END) {
   3.319 +				fprintf(stderr,
   3.320 +					"inflate error: %d (%m)\n", err);
   3.321 +				return -1;
   3.322 +			}
   3.323 +			rest -= length;
   3.324 +			stream.next_out = payload;
   3.325 +			while (length > 0) {
   3.326 +				written = write(fd, stream.next_out, length);
   3.327 +				if (written < 0) {
   3.328 +					fprintf(stderr, "write error: %m\n");
   3.329 +					return -1;
   3.330 +				}
   3.331 +				length -= written;
   3.332 +			}
   3.333 +		}
   3.334 +		if (fd > 0 && close(fd) < 0) {
   3.335 +			fprintf(stderr, "failed to close \"%s/%s%s\": %m\n",
   3.336 +				root, dir, name);
   3.337 +			return -1;
   3.338 +		}
   3.339 +		name += strlen(name) + 1;
   3.340 +		index++;
   3.341 +		size++;
   3.342 +		mode++;
   3.343 +	}
   3.344 +
   3.345 +	err = inflateEnd(&stream);
   3.346 +
   3.347 +	if (err != Z_OK) {
   3.348 +		fprintf(stderr, "inflateEnd error: %d\n", err);
   3.349 +		return -1;
   3.350 +	}	    
   3.351 +
   3.352  	return 0;
   3.353  }
   3.354  
   3.355 -static int
   3.356 -razor_rpm_close(struct rpm *rpm)
   3.357 +int
   3.358 +razor_rpm_close(struct razor_rpm *rpm)
   3.359  {
   3.360 -	return munmap(rpm->map, rpm->size);
   3.361 +	int err;
   3.362 +
   3.363 +	free(rpm->dirs);
   3.364 +	err = munmap(rpm->map, rpm->size);
   3.365 +	free(rpm);
   3.366 +
   3.367 +	return err;
   3.368  }
   3.369  
   3.370  int
   3.371 -razor_importer_add_rpm(struct razor_importer *importer, const char *filename)
   3.372 +razor_importer_add_rpm(struct razor_importer *importer, struct razor_rpm *rpm)
   3.373  {
   3.374 -	struct rpm rpm;
   3.375 +	razor_importer_begin_package(importer,
   3.376 +				     rpm->pool + ntohl(rpm->name->offset),
   3.377 +				     rpm->pool + ntohl(rpm->version->offset));
   3.378  
   3.379 -	if (razor_rpm_open(&rpm, filename) < 0) {
   3.380 -		fprintf(stderr, "failed to open rpm %s (%m)\n", filename);
   3.381 -		return -1;
   3.382 -	}
   3.383 -
   3.384 -	razor_importer_begin_package(importer,
   3.385 -				     rpm.pool + ntohl(rpm.name->offset),
   3.386 -				     rpm.pool + ntohl(rpm.version->offset));
   3.387 -
   3.388 -	import_properties(importer, &rpm.requires,
   3.389 -			  rpm.pool, RAZOR_PROPERTY_REQUIRES);
   3.390 -	import_properties(importer, &rpm.provides,
   3.391 -			  rpm.pool, RAZOR_PROPERTY_PROVIDES);
   3.392 -	import_properties(importer, &rpm.conflicts,
   3.393 -			  rpm.pool, RAZOR_PROPERTY_CONFLICTS);
   3.394 -	import_properties(importer, &rpm.obsoletes,
   3.395 -			  rpm.pool, RAZOR_PROPERTY_OBSOLETES);
   3.396 -	import_files(importer, &rpm);
   3.397 +	import_properties(importer, &rpm->requires,
   3.398 +			  rpm->pool, RAZOR_PROPERTY_REQUIRES);
   3.399 +	import_properties(importer, &rpm->provides,
   3.400 +			  rpm->pool, RAZOR_PROPERTY_PROVIDES);
   3.401 +	import_properties(importer, &rpm->conflicts,
   3.402 +			  rpm->pool, RAZOR_PROPERTY_CONFLICTS);
   3.403 +	import_properties(importer, &rpm->obsoletes,
   3.404 +			  rpm->pool, RAZOR_PROPERTY_OBSOLETES);
   3.405 +	import_files(importer, rpm);
   3.406  
   3.407  	razor_importer_finish_package(importer);
   3.408  
   3.409 -	razor_rpm_close(&rpm);
   3.410 -
   3.411  	return 0;
   3.412  }