First steps towards rpm installation.
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 }