Extend installer implementation further.
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 }