First steps towards rpm installation.
authorKristian Høgsberg <krh@redhat.com>
Fri, 9 Nov 2007 03:45:27 +0000 (22:45 -0500)
committerKristian Høgsberg <krh@redhat.com>
Fri, 9 Nov 2007 03:45:27 +0000 (22:45 -0500)
main.c
razor.h
rpm.c

diff --git a/main.c b/main.c
index 5a47b5f..5835332 100644 (file)
--- a/main.c
+++ b/main.c
@@ -298,6 +298,7 @@ command_import_rpms(int argc, const char *argv[])
        struct dirent *de;
        struct razor_importer *importer;
        struct razor_set *set;
+       struct razor_rpm *rpm;
        int len;
        char filename[256];
        const char *dirname = argv[0];
@@ -321,10 +322,17 @@ command_import_rpms(int argc, const char *argv[])
                    continue;
                snprintf(filename, sizeof filename,
                         "%s/%s", dirname, de->d_name);
-               if (razor_importer_add_rpm(importer, filename)) {
+               rpm = razor_rpm_open(filename);
+               if (rpm == NULL) {
+                       fprintf(stderr,
+                               "failed to open rpm \"%s\"\n", filename);
+                       continue;
+               }
+               if (razor_importer_add_rpm(importer, rpm)) {
                        fprintf(stderr, "couldn't import %s\n", filename);
                        break;
                }
+               razor_rpm_close(rpm);
        }
 
        if (de != NULL) {
@@ -341,6 +349,44 @@ command_import_rpms(int argc, const char *argv[])
        return 0;
 }
 
+static int
+command_install(int argc, const char *argv[])
+{
+       struct razor_rpm *rpm;
+       const char *filename = argv[0];
+       struct stat buf;
+       const char root[] = "install";
+
+       if (stat(root, &buf) < 0) {
+               if (mkdir(root, 0777) < 0) {
+                       fprintf(stderr,
+                               "could not create install root \"%s\"\n",
+                               root);
+                       return -1;
+               }
+               fprintf(stderr, "created install root \"%s\"\n", root);
+       } else if (!S_ISDIR(buf.st_mode)) {
+               fprintf(stderr,
+                       "install root \"%s\" exists, but is not a directory\n",
+                       root);
+               return -1;
+       }
+
+       rpm = razor_rpm_open(filename);
+       if (rpm == NULL) {
+               fprintf(stderr, "failed to open rpm %s\n", filename);
+               return -1;
+       }
+       if (razor_rpm_install(rpm, root) < 0) {
+               fprintf(stderr, "failed to install rpm %s\n", filename);
+               return -1;
+       }
+       
+       razor_rpm_close(rpm);
+
+       return 0;
+}
+
 static struct {
        const char *name;
        const char *description;
@@ -361,7 +407,8 @@ static struct {
        { "import-rpms", "import rpms from the given directory", command_import_rpms },
        { "validate", "validate a package set", command_validate },
        { "update", "update all or specified packages", command_update },
-       { "diff", "show diff between two package sets", command_diff }
+       { "diff", "show diff between two package sets", command_diff },
+       { "install", "install rpm", command_install }
 };
 
 static int
diff --git a/razor.h b/razor.h
index 9009da9..a37b17c 100644 (file)
--- a/razor.h
+++ b/razor.h
@@ -45,6 +45,7 @@ razor_set_diff(struct razor_set *set, struct razor_set *upstream,
  * like yum, rpmdb or razor package files. */
 
 struct razor_importer;
+struct razor_rpm;
 
 struct razor_importer *razor_importer_new(void);
 void razor_importer_destroy(struct razor_importer *importer);
@@ -58,7 +59,7 @@ void razor_importer_add_file(struct razor_importer *importer,
 void razor_importer_finish_package(struct razor_importer *importer);
 
 int razor_importer_add_rpm(struct razor_importer *importer,
-                          const char *filename);
+                          struct razor_rpm *rpm);
 
 struct razor_set *razor_importer_finish(struct razor_importer *importer);
 
@@ -66,7 +67,9 @@ struct razor_set *razor_set_create_from_yum(void);
 struct razor_set *razor_set_create_from_rpmdb(void);
 
 /* RPM functions */
-void
-razor_rpm_dump(const char *filename);
+
+struct razor_rpm *razor_rpm_open(const char *filename);
+int razor_rpm_install(struct razor_rpm *rpm, const char *root);
+int razor_rpm_close(struct razor_rpm *rpm);
 
 #endif /* _RAZOR_H_ */
diff --git a/rpm.c b/rpm.c
index 614f957..326267b 100644 (file)
--- a/rpm.c
+++ b/rpm.c
@@ -6,6 +6,7 @@
 #include <unistd.h>
 #include <arpa/inet.h>
 #include <rpm/rpmlib.h>
+#include <zlib.h>
 
 #include "razor.h"
 
@@ -43,7 +44,7 @@ struct properties {
        struct rpm_header_index *flags;
 };
 
-struct rpm {
+struct razor_rpm {
        struct rpm_header *signature;
        struct rpm_header *header;
 
@@ -54,6 +55,10 @@ struct rpm {
        struct rpm_header_index *dirnames;
        struct rpm_header_index *dirindexes;
        struct rpm_header_index *basenames;
+       struct rpm_header_index *filesizes;
+       struct rpm_header_index *filemodes;
+       struct rpm_header_index *filestates;
+       const char **dirs;
 
        struct properties provides;
        struct properties requires;
@@ -63,6 +68,7 @@ struct rpm {
        const char *pool;
        void *map;
        size_t size;
+       void *payload;
 };
 
 #define ALIGN(value, base) (((value) + (base - 1)) & ~((base) - 1))
@@ -91,9 +97,9 @@ import_properties(struct razor_importer *importer,
 }
 
 static void
-import_files(struct razor_importer *importer, struct rpm *rpm)
+import_files(struct razor_importer *importer, struct razor_rpm *rpm)
 {
-       const char *name, **dir;
+       const char *name;
        unsigned long *index;
        int i, count;
        char buffer[256];
@@ -103,49 +109,44 @@ import_files(struct razor_importer *importer, struct rpm *rpm)
        if (rpm->dirnames == NULL)
                return;
 
-       count = ntohl(rpm->dirnames->count);
-       dir = calloc(count, sizeof *dir);
-       name = rpm->pool + ntohl(rpm->dirnames->offset);
-       for (i = 0; i < count; i++) {
-               dir[i] = name;
-               name += strlen(name) + 1;
-       }
-
        count = ntohl(rpm->basenames->count);
        index = (unsigned long *) (rpm->pool + ntohl(rpm->dirindexes->offset));
        name = rpm->pool + ntohl(rpm->basenames->offset);
        for (i = 0; i < count; i++) {
                snprintf(buffer, sizeof buffer,
-                        "%s%s", dir[ntohl(*index)], name);
+                        "%s%s", rpm->dirs[ntohl(*index)], name);
                razor_importer_add_file(importer, buffer);
                name += strlen(name) + 1;
                index++;
        }
 }
 
-static int
-razor_rpm_open(struct rpm *rpm, const char *filename)
+struct razor_rpm *
+razor_rpm_open(const char *filename)
 {
+       struct razor_rpm *rpm;
        struct rpm_header_index *base, *index;
        struct stat buf;
-       int fd, nindex, hsize, i;
+       int fd, nindex, hsize, i, count;
+       const char *name;
 
+       rpm = malloc(sizeof *rpm);
        memset(rpm, 0, sizeof *rpm);
        if (stat(filename, &buf) < 0) {
                fprintf(stderr, "no such file %s (%m)\n", filename);
-               return -1;
+               return NULL;
        }
 
        fd = open(filename, O_RDONLY);
        if (fd < 0) {
                fprintf(stderr, "couldn't open %s\n", filename);
-               return -1;
+               return NULL;
        }
        rpm->size = buf.st_size;
        rpm->map = mmap(NULL, rpm->size, PROT_READ, MAP_PRIVATE, fd, 0);
        if (rpm->map == MAP_FAILED) {
                fprintf(stderr, "couldn't mmap %s\n", filename);
-               return -1;
+               return NULL;
        }
        close(fd);
 
@@ -154,8 +155,11 @@ razor_rpm_open(struct rpm *rpm, const char *filename)
        hsize = ntohl(rpm->signature->hsize);
        rpm->header = (void *) (rpm->signature + 1) +
                ALIGN(nindex * sizeof *index + hsize, 8);
-
        nindex = ntohl(rpm->header->nindex);
+       hsize = ntohl(rpm->header->hsize);
+       rpm->payload = (void *) (rpm->header + 1) +
+               nindex * sizeof *index + hsize;
+
        base = (struct rpm_header_index *) (rpm->header + 1);
        rpm->pool = (void *) base + nindex * sizeof *index;
 
@@ -221,45 +225,263 @@ razor_rpm_open(struct rpm *rpm, const char *filename)
                case RPMTAG_DIRNAMES:
                        rpm->dirnames = index;
                        break;
+               case RPMTAG_FILESIZES:
+                       rpm->filesizes = index;
+                       break;
+               case RPMTAG_FILEMODES:
+                       rpm->filemodes = index;
+                       break;
+               case RPMTAG_FILESTATES:
+                       rpm->filestates = index;
+                       break;
                }
        }
 
-       return 0;
+       /* Look up dir names now so we can index them directly. */
+       if (rpm->dirnames != NULL) {
+               count = ntohl(rpm->dirnames->count);
+               rpm->dirs = calloc(count, sizeof *rpm->dirs);
+               name = rpm->pool + ntohl(rpm->dirnames->offset);
+               for (i = 0; i < count; i++) {
+                       rpm->dirs[i] = name;
+                       name += strlen(name) + 1;
+               }
+       }
+
+       return rpm;
 }
 
+struct cpio_file_header {
+       char magic[6];
+       char inode[8];
+       char mode[8];
+       char uid[8];
+       char gid[8];
+       char nlink[8];
+       char mtime[8];
+       char filesize[8];
+       char devmajor[8];
+       char devminor[8];
+       char rdevmajor[8];
+       char rdevminor[8];
+       char namesize[8];
+       char checksum[8];
+       char filename[0];
+};
+
+/* gzip flags */
+#define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
+#define HEAD_CRC     0x02 /* bit 1 set: header CRC present */
+#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
+#define COMMENT      0x10 /* bit 4 set: file comment present */
+#define RESERVED     0xE0 /* bits 5..7: reserved */
+
 static int
-razor_rpm_close(struct rpm *rpm)
+create_path(const char *root, const char *path,
+           const char *name, unsigned mode, int *fd)
 {
-       return munmap(rpm->map, rpm->size);
+       char buffer[256], *p;
+       const char *slash, *next;
+       struct stat buf;
+
+       /* Create all sub-directories in dir and then create name. We
+        * know root exists and is a dir, root does not end in a '/',
+        * and path has a leading '/'. */
+
+       strcpy(buffer, root);
+       p = buffer + strlen(buffer);
+       slash = path;
+       for (slash = path; slash[1] != '\0'; slash = next) {
+               next = strchr(slash + 1, '/');
+               memcpy(p, slash, next - slash);
+               p += next - slash;
+               *p = '\0';
+
+               if (stat(buffer, &buf) == 0) {
+                       if (!S_ISDIR(buf.st_mode)) {
+                               fprintf(stderr,
+                                       "%s exists but is not a directory\n",
+                                       buffer);
+                               return -1;
+                       }
+               } else if (mkdir(buffer, 0777) < 0) {
+                       fprintf(stderr, "failed to make directory %s: %m\n",
+                               buffer);
+                       return -1;
+               }
+               /* FIXME: permissions */
+       }
+
+       *p++ = '/';
+       strcpy(p, name);
+
+       switch (mode >> 12) {
+       case REG:
+       default:
+               *fd = open(buffer, O_WRONLY | O_CREAT | O_TRUNC, mode & 0x1ff);
+               return *fd;
+       case XDIR:
+               *fd = -1;
+               return mkdir(buffer, mode & 0x1ff);
+       }
 }
 
 int
-razor_importer_add_rpm(struct razor_importer *importer, const char *filename)
+razor_rpm_install(struct razor_rpm *rpm, const char *root)
 {
-       struct rpm rpm;
+       z_stream stream;
+       unsigned char payload[32768], *gz_header;
+       char buffer[256];
+       int err, method, flags, count, i, fd, written;
+       struct cpio_file_header *header;
+       unsigned long *size, *index, rest, length;
+       unsigned short *mode;
+       const char *name, *dir;
+       struct stat buf;
+
+       if (stat(root, &buf) < 0 || !S_ISDIR(buf.st_mode)) {
+               fprintf(stderr,
+                       "root installation directory \"%s\" does not exist\n",
+                       root);
+               return -1;
+       }
+
+       gz_header = rpm->payload;
+       if (gz_header[0] != 0x1f || gz_header[1] != 0x8b) {
+               fprintf(stderr, "payload section doesn't have gz header\n");
+               return -1;
+       }
+
+       method = gz_header[2];
+       flags = gz_header[3];
 
-       if (razor_rpm_open(&rpm, filename) < 0) {
-               fprintf(stderr, "failed to open rpm %s (%m)\n", filename);
+       if (method != Z_DEFLATED || flags != 0) {
+               fprintf(stderr,
+                       "unknown payload compression method or flags set\n");
                return -1;
        }
 
+       stream.zalloc = NULL;
+       stream.zfree = NULL;
+       stream.opaque = NULL;
+
+       stream.next_in  = gz_header + 10;
+       stream.avail_in = (rpm->map + rpm->size) - (void *) stream.next_in;
+       stream.next_out = NULL;
+       stream.avail_out = 0;
+
+       err = inflateInit2(&stream, -MAX_WBITS);
+       if (err != Z_OK) {
+               fprintf(stderr, "inflateInit error: %d\n", err);
+               return -1;
+       }
+
+       count = ntohl(rpm->basenames->count);
+       size = (unsigned long *) (rpm->pool + ntohl(rpm->filesizes->offset));
+       index = (unsigned long *) (rpm->pool + ntohl(rpm->dirindexes->offset));
+       mode = (unsigned short *) (rpm->pool + ntohl(rpm->filemodes->offset));
+       name = rpm->pool + ntohl(rpm->basenames->offset);
+       for (i = 0; i < count; i++) {
+               dir = rpm->dirs[ntohl(*index)];
+               snprintf(buffer, sizeof buffer, "%s%s", dir, name);
+
+               stream.next_out = payload;
+               /* Plus two for the leading '.' and the terminating NUL. */
+               stream.avail_out =
+                       ALIGN(sizeof *header + strlen(buffer) + 2, 4);
+               err = inflate(&stream, Z_SYNC_FLUSH);
+               if (err != Z_OK) {
+                       fprintf(stderr, "inflate error: %d\n", err);
+                       return -1;
+               }
+           
+               header = (struct cpio_file_header *) payload;
+
+               /* FIXME: Figure out if it's a symlink, device file,
+                * directorys or whatever.  Maybe do this upfront. */
+               if (create_path(root, dir, name, ntohs(*mode), &fd) < 0)
+                       return -1;
+               if (ntohs(*mode) >> 12 == XDIR)
+                       rest = 0;
+               else
+                       rest = ntohl(*size);
+
+               while (rest > 0) {
+                       if (ALIGN(rest, 4) > sizeof payload)
+                               length = sizeof payload;
+                       else
+                               length = rest;
+                       stream.next_out = payload;
+                       stream.avail_out = ALIGN(length, 4);
+                       err = inflate(&stream, Z_SYNC_FLUSH);
+                       if (err != Z_OK && err != Z_STREAM_END) {
+                               fprintf(stderr,
+                                       "inflate error: %d (%m)\n", err);
+                               return -1;
+                       }
+                       rest -= length;
+                       stream.next_out = payload;
+                       while (length > 0) {
+                               written = write(fd, stream.next_out, length);
+                               if (written < 0) {
+                                       fprintf(stderr, "write error: %m\n");
+                                       return -1;
+                               }
+                               length -= written;
+                       }
+               }
+               if (fd > 0 && close(fd) < 0) {
+                       fprintf(stderr, "failed to close \"%s/%s%s\": %m\n",
+                               root, dir, name);
+                       return -1;
+               }
+               name += strlen(name) + 1;
+               index++;
+               size++;
+               mode++;
+       }
+
+       err = inflateEnd(&stream);
+
+       if (err != Z_OK) {
+               fprintf(stderr, "inflateEnd error: %d\n", err);
+               return -1;
+       }           
+
+       return 0;
+}
+
+int
+razor_rpm_close(struct razor_rpm *rpm)
+{
+       int err;
+
+       free(rpm->dirs);
+       err = munmap(rpm->map, rpm->size);
+       free(rpm);
+
+       return err;
+}
+
+int
+razor_importer_add_rpm(struct razor_importer *importer, struct razor_rpm *rpm)
+{
        razor_importer_begin_package(importer,
-                                    rpm.pool + ntohl(rpm.name->offset),
-                                    rpm.pool + ntohl(rpm.version->offset));
-
-       import_properties(importer, &rpm.requires,
-                         rpm.pool, RAZOR_PROPERTY_REQUIRES);
-       import_properties(importer, &rpm.provides,
-                         rpm.pool, RAZOR_PROPERTY_PROVIDES);
-       import_properties(importer, &rpm.conflicts,
-                         rpm.pool, RAZOR_PROPERTY_CONFLICTS);
-       import_properties(importer, &rpm.obsoletes,
-                         rpm.pool, RAZOR_PROPERTY_OBSOLETES);
-       import_files(importer, &rpm);
+                                    rpm->pool + ntohl(rpm->name->offset),
+                                    rpm->pool + ntohl(rpm->version->offset));
+
+       import_properties(importer, &rpm->requires,
+                         rpm->pool, RAZOR_PROPERTY_REQUIRES);
+       import_properties(importer, &rpm->provides,
+                         rpm->pool, RAZOR_PROPERTY_PROVIDES);
+       import_properties(importer, &rpm->conflicts,
+                         rpm->pool, RAZOR_PROPERTY_CONFLICTS);
+       import_properties(importer, &rpm->obsoletes,
+                         rpm->pool, RAZOR_PROPERTY_OBSOLETES);
+       import_files(importer, rpm);
 
        razor_importer_finish_package(importer);
 
-       razor_rpm_close(&rpm);
-
        return 0;
 }