1.1 --- a/rpm.c Thu Nov 08 17:14:19 2007 -0500
1.2 +++ b/rpm.c Thu Nov 08 22:45:27 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 }