rpm.c
changeset 86 e44ccd213756
parent 85 c5041a93b91a
child 87 c1e04d4e3bc9
     1.1 --- a/rpm.c	Thu Dec 27 15:47:09 2007 -0500
     1.2 +++ b/rpm.c	Thu Dec 27 17:45:18 2007 -0500
     1.3 @@ -60,7 +60,7 @@
     1.4  	struct rpm_header_index *basenames;
     1.5  	struct rpm_header_index *filesizes;
     1.6  	struct rpm_header_index *filemodes;
     1.7 -	struct rpm_header_index *filestates;
     1.8 +	struct rpm_header_index *fileflags;
     1.9  	const char **dirs;
    1.10  
    1.11  	struct properties provides;
    1.12 @@ -150,7 +150,7 @@
    1.13  	MAP_ENTRY(dirnames, RPMTAG_DIRNAMES),
    1.14  	MAP_ENTRY(filesizes, RPMTAG_FILESIZES),
    1.15  	MAP_ENTRY(filemodes, RPMTAG_FILEMODES),
    1.16 -	MAP_ENTRY(filestates, RPMTAG_FILESTATES),
    1.17 +	MAP_ENTRY(fileflags, RPMTAG_FILEFLAGS),
    1.18  };
    1.19  
    1.20  static struct rpm_header_index *
    1.21 @@ -262,19 +262,75 @@
    1.22  #define COMMENT      0x10 /* bit 4 set: file comment present */
    1.23  #define RESERVED     0xE0 /* bits 5..7: reserved */
    1.24  
    1.25 +struct installer {
    1.26 +	const char *root;
    1.27 +	struct razor_rpm *rpm;
    1.28 +	z_stream stream;
    1.29 +	unsigned char buffer[32768];
    1.30 +	size_t rest, length;
    1.31 +};
    1.32 +
    1.33  static int
    1.34 -create_path(const char *root, const char *path,
    1.35 -	    const char *name, unsigned mode, int *fd)
    1.36 +installer_inflate(struct installer *installer)
    1.37 +{
    1.38 +	size_t length;
    1.39 +	int err;
    1.40 +
    1.41 +	if (ALIGN(installer->rest, 4) > sizeof installer->buffer)
    1.42 +		length = sizeof installer->buffer;
    1.43 +	else
    1.44 +		length = installer->rest;
    1.45 +
    1.46 +	installer->stream.next_out = installer->buffer;
    1.47 +	installer->stream.avail_out = ALIGN(length, 4);
    1.48 +	err = inflate(&installer->stream, Z_SYNC_FLUSH);
    1.49 +	if (err != Z_OK && err != Z_STREAM_END) {
    1.50 +		fprintf(stderr, "inflate error: %d (%m)\n", err);
    1.51 +		return -1;
    1.52 +	}
    1.53 +
    1.54 +	installer->rest -= length;
    1.55 +	installer->length = length;
    1.56 +
    1.57 +	return 0;
    1.58 +}
    1.59 +
    1.60 +static int
    1.61 +xwrite(int fd, const void *data, size_t size)
    1.62 +{
    1.63 +	size_t rest;
    1.64 +	ssize_t written;
    1.65 +	const unsigned char *p;
    1.66 +
    1.67 +	rest = size;
    1.68 +	p = data;
    1.69 +	while (rest > 0) {
    1.70 +		written = write(fd, p, rest);
    1.71 +		if (written < 0) {
    1.72 +			fprintf(stderr, "write error: %m\n");
    1.73 +			return -1;
    1.74 +		}
    1.75 +		rest -= written;
    1.76 +		p += written;
    1.77 +	}
    1.78 +
    1.79 +	return 0;
    1.80 +}
    1.81 +
    1.82 +static int
    1.83 +create_path(struct installer *installer,
    1.84 +	    const char *path, const char *name, unsigned int mode)
    1.85  {
    1.86  	char buffer[256], *p;
    1.87  	const char *slash, *next;
    1.88  	struct stat buf;
    1.89 +	int fd;
    1.90  
    1.91  	/* Create all sub-directories in dir and then create name. We
    1.92  	 * know root exists and is a dir, root does not end in a '/',
    1.93  	 * and path has a leading '/'. */
    1.94  
    1.95 -	strcpy(buffer, root);
    1.96 +	strcpy(buffer, installer->root);
    1.97  	p = buffer + strlen(buffer);
    1.98  	slash = path;
    1.99  	for (slash = path; slash[1] != '\0'; slash = next) {
   1.100 @@ -303,26 +359,77 @@
   1.101  
   1.102  	switch (mode >> 12) {
   1.103  	case REG:
   1.104 +		fd = open(buffer, O_WRONLY | O_CREAT | O_TRUNC, mode & 0x1ff);
   1.105 +		if (fd < 0){
   1.106 +			fprintf(stderr, "failed to create file %s\n", buffer);
   1.107 +			return -1;
   1.108 +		}
   1.109 +		while (installer->rest > 0) {
   1.110 +			if (installer_inflate(installer)) {
   1.111 +				fprintf(stderr, "failed to inflate\n");
   1.112 +				return -1;
   1.113 +			}
   1.114 +			if (xwrite(fd, installer->buffer, installer->length)) {
   1.115 +				fprintf(stderr, "failed to write payload\n");
   1.116 +				return -1;
   1.117 +			}
   1.118 +		}
   1.119 +		if (close(fd) < 0) {
   1.120 +			fprintf(stderr, "failed to close %s: %m\n", buffer);
   1.121 +			return -1;
   1.122 +		}
   1.123 +		return 0;
   1.124 +	case XDIR:
   1.125 +		return mkdir(buffer, mode & 0x1ff);
   1.126 +	case PIPE:
   1.127 +	case CDEV:
   1.128 +	case BDEV:
   1.129 +	case SOCK:
   1.130 +		printf("%s: unhandled file type %d\n", buffer, mode >> 12);
   1.131 +		return 0;
   1.132 +	case LINK:
   1.133 +		if (installer_inflate(installer)) {
   1.134 +			fprintf(stderr, "failed to inflate\n");
   1.135 +			return -1;
   1.136 +		}
   1.137 +		if (installer->length >= sizeof installer->buffer) {
   1.138 +			fprintf(stderr, "link name too long\n");
   1.139 +			return -1;
   1.140 +		}
   1.141 +		installer->buffer[installer->length] = '\0';
   1.142 +		if (symlink((const char *) installer->buffer, buffer)) {
   1.143 +			fprintf(stderr, "failed to create symlink, %m\n");
   1.144 +			return -1;
   1.145 +		}
   1.146 +		return 0;
   1.147  	default:
   1.148 -		*fd = open(buffer, O_WRONLY | O_CREAT | O_TRUNC, mode & 0x1ff);
   1.149 -		return *fd;
   1.150 -	case XDIR:
   1.151 -		*fd = -1;
   1.152 -		return mkdir(buffer, mode & 0x1ff);
   1.153 +		printf("%s: unknown file type %d\n", buffer, mode >> 12);
   1.154 +		return 0;
   1.155  	}
   1.156  }
   1.157  
   1.158  static int
   1.159 -run_script(struct razor_rpm *rpm, const char *root, unsigned int tag)
   1.160 +run_script(struct installer *installer,
   1.161 +	   unsigned int program_tag, unsigned int script_tag)
   1.162  {
   1.163  	struct rpm_header_index *index;
   1.164  	int pid, status, fd[2];
   1.165 -	const char *script;
   1.166 +	const char *script = NULL, *program = NULL;
   1.167  
   1.168 -	index = razor_rpm_get_header(rpm, tag);
   1.169 -	if (index == NULL) {
   1.170 -		fprintf(stderr, "no script for tag %d\n", tag);
   1.171 -		return 0;
   1.172 +	index = razor_rpm_get_header(installer->rpm, program_tag);
   1.173 +	if (index != NULL)
   1.174 +		program = installer->rpm->pool + ntohl(index->offset);
   1.175 +
   1.176 +	index = razor_rpm_get_header(installer->rpm, script_tag);
   1.177 +	if (index != NULL)
   1.178 +		script = installer->rpm->pool + ntohl(index->offset);
   1.179 +
   1.180 +	if (program == NULL && script == NULL) {
   1.181 +		printf("no script or program for tags %d and %d\n",
   1.182 +		       program_tag, script_tag);
   1.183 +		return -1;
   1.184 +	} else if (program == NULL) {
   1.185 +		program = "/bin/sh";
   1.186  	}
   1.187  
   1.188  	if (pipe(fd) < 0) {
   1.189 @@ -341,18 +448,19 @@
   1.190  			fprintf(stderr, "failed to close pipe, %m\n");
   1.191  			exit(-1);
   1.192  		}
   1.193 -		if (chroot(root) < 0) {
   1.194 -			fprintf(stderr, "failed to chroot to %s, %m\n", root);
   1.195 +		if (chroot(installer->root) < 0) {
   1.196 +			fprintf(stderr, "failed to chroot to %s, %m\n",
   1.197 +				installer->root);
   1.198  			return -1;
   1.199  		}
   1.200 -		printf("executing script for %d\n", tag);
   1.201 -		if (execl("/bin/sh", "/bin/sh", NULL)) {
   1.202 -			fprintf(stderr, "failed to exec /bin/sh, %m\n");
   1.203 +		printf("executing program %s in chroot %s\n",
   1.204 +		       program, installer->root);
   1.205 +		if (execl(program, program, NULL)) {
   1.206 +			fprintf(stderr, "failed to exec %s, %m\n", program);
   1.207  			return -1;
   1.208  		}
   1.209  	} else {
   1.210 -		script = rpm->pool + ntohl(index->offset);
   1.211 -		if (write(fd[1], script, strlen(script)) < 0) {
   1.212 +		if (script && write(fd[1], script, strlen(script)) < 0) {
   1.213  			fprintf(stderr, "failed to pipe script, %m\n");
   1.214  			return -1;
   1.215  		}
   1.216 @@ -370,27 +478,13 @@
   1.217  	return 0;
   1.218  }
   1.219  
   1.220 -int
   1.221 -razor_rpm_install(struct razor_rpm *rpm, const char *root)
   1.222 +static int
   1.223 +installer_init(struct installer *installer)
   1.224  {
   1.225 -	z_stream stream;
   1.226 -	unsigned char payload[32768], *gz_header;
   1.227 -	char buffer[256];
   1.228 -	int err, method, flags, count, i, fd, written;
   1.229 -	struct cpio_file_header *header;
   1.230 -	unsigned long *size, *index, rest, length;
   1.231 -	unsigned short *mode;
   1.232 -	const char *name, *dir;
   1.233 -	struct stat buf;
   1.234 +	unsigned char *gz_header;
   1.235 +	int method, flags, err;
   1.236  
   1.237 -	if (stat(root, &buf) < 0 || !S_ISDIR(buf.st_mode)) {
   1.238 -		fprintf(stderr,
   1.239 -			"root installation directory \"%s\" does not exist\n",
   1.240 -			root);
   1.241 -		return -1;
   1.242 -	}
   1.243 -
   1.244 -	gz_header = rpm->payload;
   1.245 +	gz_header = installer->rpm->payload;
   1.246  	if (gz_header[0] != 0x1f || gz_header[1] != 0x8b) {
   1.247  		fprintf(stderr, "payload section doesn't have gz header\n");
   1.248  		return -1;
   1.249 @@ -405,96 +499,103 @@
   1.250  		return -1;
   1.251  	}
   1.252  
   1.253 -	run_script(rpm, root, RPMTAG_PREIN);
   1.254 +	installer->stream.zalloc = NULL;
   1.255 +	installer->stream.zfree = NULL;
   1.256 +	installer->stream.opaque = NULL;
   1.257  
   1.258 -	stream.zalloc = NULL;
   1.259 -	stream.zfree = NULL;
   1.260 -	stream.opaque = NULL;
   1.261 +	installer->stream.next_in  = gz_header + 10;
   1.262 +	installer->stream.avail_in =
   1.263 +		(installer->rpm->map + installer->rpm->size) -
   1.264 +		(void *) installer->stream.next_in;
   1.265 +	installer->stream.next_out = NULL;
   1.266 +	installer->stream.avail_out = 0;
   1.267  
   1.268 -	stream.next_in  = gz_header + 10;
   1.269 -	stream.avail_in = (rpm->map + rpm->size) - (void *) stream.next_in;
   1.270 -	stream.next_out = NULL;
   1.271 -	stream.avail_out = 0;
   1.272 -
   1.273 -	err = inflateInit2(&stream, -MAX_WBITS);
   1.274 +	err = inflateInit2(&installer->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 +	return 0;
   1.289 +}
   1.290  
   1.291 -		stream.next_out = payload;
   1.292 -		/* Plus two for the leading '.' and the terminating NUL. */
   1.293 -		stream.avail_out =
   1.294 -			ALIGN(sizeof *header + strlen(buffer) + 2, 4);
   1.295 -		err = inflate(&stream, Z_SYNC_FLUSH);
   1.296 -		if (err != Z_OK) {
   1.297 -			fprintf(stderr, "inflate error: %d\n", err);
   1.298 -			return -1;
   1.299 -		}
   1.300 -	    
   1.301 -		header = (struct cpio_file_header *) payload;
   1.302 +static int
   1.303 +installer_finish(struct installer *installer)
   1.304 +{
   1.305 +	int err;
   1.306  
   1.307 -		/* FIXME: Figure out if it's a symlink, device file,
   1.308 -		 * directorys or whatever.  Maybe do this upfront. */
   1.309 -		if (create_path(root, dir, name, ntohs(*mode), &fd) < 0)
   1.310 -			return -1;
   1.311 -		if (ntohs(*mode) >> 12 == XDIR)
   1.312 -			rest = 0;
   1.313 -		else
   1.314 -			rest = ntohl(*size);
   1.315 -
   1.316 -		while (rest > 0) {
   1.317 -			if (ALIGN(rest, 4) > sizeof payload)
   1.318 -				length = sizeof payload;
   1.319 -			else
   1.320 -				length = rest;
   1.321 -			stream.next_out = payload;
   1.322 -			stream.avail_out = ALIGN(length, 4);
   1.323 -			err = inflate(&stream, Z_SYNC_FLUSH);
   1.324 -			if (err != Z_OK && err != Z_STREAM_END) {
   1.325 -				fprintf(stderr,
   1.326 -					"inflate error: %d (%m)\n", err);
   1.327 -				return -1;
   1.328 -			}
   1.329 -			rest -= length;
   1.330 -			stream.next_out = payload;
   1.331 -			while (length > 0) {
   1.332 -				written = write(fd, stream.next_out, length);
   1.333 -				if (written < 0) {
   1.334 -					fprintf(stderr, "write error: %m\n");
   1.335 -					return -1;
   1.336 -				}
   1.337 -				length -= written;
   1.338 -			}
   1.339 -		}
   1.340 -		if (fd > 0 && close(fd) < 0) {
   1.341 -			fprintf(stderr, "failed to close \"%s/%s%s\": %m\n",
   1.342 -				root, dir, name);
   1.343 -			return -1;
   1.344 -		}
   1.345 -		name += strlen(name) + 1;
   1.346 -		index++;
   1.347 -		size++;
   1.348 -		mode++;
   1.349 -	}
   1.350 -
   1.351 -	err = inflateEnd(&stream);
   1.352 +	err = inflateEnd(&installer->stream);
   1.353  
   1.354  	if (err != Z_OK) {
   1.355  		fprintf(stderr, "inflateEnd error: %d\n", err);
   1.356  		return -1;
   1.357  	}	    
   1.358  
   1.359 -	run_script(rpm, root, RPMTAG_POSTIN);
   1.360 +	return 0;
   1.361 +}
   1.362 +
   1.363 +int
   1.364 +razor_rpm_install(struct razor_rpm *rpm, const char *root)
   1.365 +{
   1.366 +	struct installer installer;
   1.367 +	int count, i;
   1.368 +	struct cpio_file_header *header;
   1.369 +	unsigned long *size, *index, length, *flags;
   1.370 +	unsigned short *mode;
   1.371 +	const char *name, *dir;
   1.372 +	struct stat buf;
   1.373 +
   1.374 +	installer.rpm = rpm;
   1.375 +	installer.root = root;
   1.376 +
   1.377 +	/* FIXME: Only do this before a transaction, not per rpm. */
   1.378 +	if (stat(root, &buf) < 0 || !S_ISDIR(buf.st_mode)) {
   1.379 +		fprintf(stderr,
   1.380 +			"root installation directory \"%s\" does not exist\n",
   1.381 +			root);
   1.382 +		return -1;
   1.383 +	}
   1.384 +
   1.385 +	if (installer_init(&installer))
   1.386 +		return -1;
   1.387 +
   1.388 +	run_script(&installer, RPMTAG_PREINPROG, RPMTAG_PREIN);
   1.389 +
   1.390 +	count = ntohl(rpm->basenames->count);
   1.391 +	size = (unsigned long *) (rpm->pool + ntohl(rpm->filesizes->offset));
   1.392 +	index = (unsigned long *) (rpm->pool + ntohl(rpm->dirindexes->offset));
   1.393 +	mode = (unsigned short *) (rpm->pool + ntohl(rpm->filemodes->offset));
   1.394 +	flags = (unsigned long *) (rpm->pool + ntohl(rpm->fileflags->offset));
   1.395 +
   1.396 +	name = rpm->pool + ntohl(rpm->basenames->offset);
   1.397 +	for (i = 0; i < count; i++) {
   1.398 +		dir = rpm->dirs[ntohl(*index)];
   1.399 +
   1.400 +		/* Skip past the cpio header block unless it's a ghost file,
   1.401 +		 * in which case doesn't appear in the cpio archive. */
   1.402 +		if (!(ntohl(*flags) & RPMFILE_GHOST)) {
   1.403 +			/* Plus two for the leading '.' and the terminating NUL. */
   1.404 +			length = sizeof *header + strlen(dir) + strlen(name) + 2;
   1.405 +			installer.rest = ALIGN(length, 4);
   1.406 +			if (installer_inflate(&installer))
   1.407 +				return -1;
   1.408 +		}
   1.409 +
   1.410 +		installer.rest = ntohl(*size);
   1.411 +		if (create_path(&installer, dir, name, ntohs(*mode)) < 0)
   1.412 +			return -1;
   1.413 +
   1.414 +		name += strlen(name) + 1;
   1.415 +		index++;
   1.416 +		size++;
   1.417 +		mode++;
   1.418 +		flags++;
   1.419 +	}
   1.420 +
   1.421 +	if (installer_finish(&installer))
   1.422 +		return -1;
   1.423 +
   1.424 +	run_script(&installer, RPMTAG_POSTINPROG, RPMTAG_POSTIN);
   1.425  
   1.426  	return 0;
   1.427  }