From: Kristian Høgsberg Date: Fri, 9 Nov 2007 03:45:27 +0000 (-0500) Subject: First steps towards rpm installation. X-Git-Tag: 0.1~283 X-Git-Url: http://project.juiblex.co.uk/git/?a=commitdiff_plain;h=a79a393aab2a4a4ab843c9c6388a6911c70860c3;p=razor.git First steps towards rpm installation. --- diff --git a/main.c b/main.c index 5a47b5f..5835332 100644 --- 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 --- 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 --- a/rpm.c +++ b/rpm.c @@ -6,6 +6,7 @@ #include #include #include +#include #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; }