krh@74: #include krh@84: #include krh@74: #include krh@74: #include krh@74: #include krh@74: #include krh@74: #include krh@74: #include krh@74: #include krh@77: #include krh@74: krh@75: #include "razor.h" krh@75: krh@74: #define RPM_LEAD_SIZE 96 krh@74: krh@74: struct rpm_lead { krh@74: unsigned char magic[4]; krh@74: unsigned char major; krh@74: unsigned char minor; krh@74: short type; krh@74: short archnum; krh@74: char name[66]; krh@74: short osnum; krh@74: short signature_type; krh@74: char reserved[16]; krh@74: }; krh@74: krh@74: struct rpm_header { krh@74: unsigned char magic[4]; krh@74: unsigned char reserved[4]; krh@74: int nindex; krh@74: int hsize; krh@74: }; krh@74: krh@74: struct rpm_header_index { krh@74: int tag; krh@74: int type; krh@74: int offset; krh@74: int count; krh@74: }; krh@74: krh@75: struct properties { krh@75: struct rpm_header_index *name; krh@75: struct rpm_header_index *version; krh@75: struct rpm_header_index *flags; krh@75: }; krh@75: krh@77: struct razor_rpm { krh@75: struct rpm_header *signature; krh@75: struct rpm_header *header; krh@75: krh@75: struct rpm_header_index *name; krh@75: struct rpm_header_index *version; krh@75: struct rpm_header_index *release; krh@75: krh@75: struct rpm_header_index *dirnames; krh@75: struct rpm_header_index *dirindexes; krh@75: struct rpm_header_index *basenames; krh@77: struct rpm_header_index *filesizes; krh@77: struct rpm_header_index *filemodes; krh@77: struct rpm_header_index *filestates; krh@77: const char **dirs; krh@75: krh@84: struct rpm_header_index *prein; krh@84: struct rpm_header_index *postin; krh@84: struct rpm_header_index *preun; krh@84: struct rpm_header_index *postun; krh@84: krh@75: struct properties provides; krh@75: struct properties requires; krh@75: struct properties obsoletes; krh@75: struct properties conflicts; krh@75: krh@75: const char *pool; krh@75: void *map; krh@75: size_t size; krh@77: void *payload; krh@75: }; krh@75: krh@74: #define ALIGN(value, base) (((value) + (base - 1)) & ~((base) - 1)) krh@74: krh@75: static void krh@75: import_properties(struct razor_importer *importer, krh@75: struct properties *properties, krh@75: const char *pool, unsigned long type) krh@75: { krh@75: const char *name, *version; krh@75: int i, count; krh@75: krh@75: /* assert: count is the same for all arrays */ krh@75: krh@75: if (properties->name == NULL) krh@75: return; krh@75: krh@75: count = ntohl(properties->name->count); krh@75: name = pool + ntohl(properties->name->offset); krh@75: version = pool + ntohl(properties->version->offset); krh@75: for (i = 0; i < count; i++) { krh@75: razor_importer_add_property(importer, name, version, type); krh@75: name += strlen(name) + 1; krh@75: version += strlen(version) + 1; krh@75: } krh@75: } krh@75: krh@75: static void krh@77: import_files(struct razor_importer *importer, struct razor_rpm *rpm) krh@75: { krh@77: const char *name; krh@75: unsigned long *index; krh@75: int i, count; krh@75: char buffer[256]; krh@75: krh@75: /* assert: count is the same for all arrays */ krh@75: krh@75: if (rpm->dirnames == NULL) krh@75: return; krh@75: krh@75: count = ntohl(rpm->basenames->count); krh@75: index = (unsigned long *) (rpm->pool + ntohl(rpm->dirindexes->offset)); krh@75: name = rpm->pool + ntohl(rpm->basenames->offset); krh@75: for (i = 0; i < count; i++) { krh@75: snprintf(buffer, sizeof buffer, krh@77: "%s%s", rpm->dirs[ntohl(*index)], name); krh@75: razor_importer_add_file(importer, buffer); krh@75: name += strlen(name) + 1; krh@75: index++; krh@75: } krh@75: } krh@75: krh@84: #define MAP_ENTRY(field, tag) { offsetof(struct razor_rpm, field), tag } krh@84: krh@84: static struct index_map { krh@84: unsigned int offset; krh@84: unsigned int tag; krh@84: } index_map[] = { krh@84: MAP_ENTRY(prein, RPMTAG_PREIN), krh@84: MAP_ENTRY(prein, RPMTAG_PREIN), krh@84: MAP_ENTRY(postin, RPMTAG_POSTIN), krh@84: MAP_ENTRY(preun, RPMTAG_PREUN), krh@84: MAP_ENTRY(postun, RPMTAG_POSTUN), krh@84: MAP_ENTRY(name, RPMTAG_NAME), krh@84: MAP_ENTRY(version, RPMTAG_VERSION), krh@84: MAP_ENTRY(release, RPMTAG_RELEASE), krh@84: MAP_ENTRY(requires.name, RPMTAG_REQUIRENAME), krh@84: MAP_ENTRY(requires.version, RPMTAG_REQUIREVERSION), krh@84: MAP_ENTRY(requires.flags, RPMTAG_REQUIREFLAGS), krh@84: MAP_ENTRY(provides.name, RPMTAG_PROVIDENAME), krh@84: MAP_ENTRY(provides.version, RPMTAG_PROVIDEVERSION), krh@84: MAP_ENTRY(provides.flags, RPMTAG_PROVIDEFLAGS), krh@84: MAP_ENTRY(obsoletes.name, RPMTAG_OBSOLETENAME), krh@84: MAP_ENTRY(obsoletes.version, RPMTAG_OBSOLETEVERSION), krh@84: MAP_ENTRY(obsoletes.flags, RPMTAG_OBSOLETEFLAGS), krh@84: MAP_ENTRY(conflicts.name, RPMTAG_CONFLICTNAME), krh@84: MAP_ENTRY(conflicts.version, RPMTAG_CONFLICTVERSION), krh@84: MAP_ENTRY(conflicts.flags, RPMTAG_CONFLICTFLAGS), krh@84: MAP_ENTRY(dirindexes, RPMTAG_DIRINDEXES), krh@84: MAP_ENTRY(basenames, RPMTAG_BASENAMES), krh@84: MAP_ENTRY(dirnames, RPMTAG_DIRNAMES), krh@84: MAP_ENTRY(filesizes, RPMTAG_FILESIZES), krh@84: MAP_ENTRY(filemodes, RPMTAG_FILEMODES), krh@84: MAP_ENTRY(filestates, RPMTAG_FILESTATES), krh@84: }; krh@84: krh@77: struct razor_rpm * krh@77: razor_rpm_open(const char *filename) krh@74: { krh@77: struct razor_rpm *rpm; krh@74: struct rpm_header_index *base, *index; krh@75: struct stat buf; krh@84: int fd, nindex, hsize, i, j, count; krh@77: const char *name; krh@74: krh@77: rpm = malloc(sizeof *rpm); krh@75: memset(rpm, 0, sizeof *rpm); krh@75: if (stat(filename, &buf) < 0) { krh@75: fprintf(stderr, "no such file %s (%m)\n", filename); krh@77: return NULL; krh@75: } krh@74: krh@75: fd = open(filename, O_RDONLY); krh@75: if (fd < 0) { krh@75: fprintf(stderr, "couldn't open %s\n", filename); krh@77: return NULL; krh@75: } krh@75: rpm->size = buf.st_size; krh@75: rpm->map = mmap(NULL, rpm->size, PROT_READ, MAP_PRIVATE, fd, 0); krh@75: if (rpm->map == MAP_FAILED) { krh@75: fprintf(stderr, "couldn't mmap %s\n", filename); krh@77: return NULL; krh@75: } krh@75: close(fd); krh@75: krh@75: rpm->signature = rpm->map + RPM_LEAD_SIZE; krh@75: nindex = ntohl(rpm->signature->nindex); krh@75: hsize = ntohl(rpm->signature->hsize); krh@75: rpm->header = (void *) (rpm->signature + 1) + krh@75: ALIGN(nindex * sizeof *index + hsize, 8); krh@77: nindex = ntohl(rpm->header->nindex); krh@77: hsize = ntohl(rpm->header->hsize); krh@77: rpm->payload = (void *) (rpm->header + 1) + krh@77: nindex * sizeof *index + hsize; krh@75: krh@75: base = (struct rpm_header_index *) (rpm->header + 1); krh@75: rpm->pool = (void *) base + nindex * sizeof *index; krh@75: krh@74: for (i = 0; i < nindex; i++) { krh@74: index = base + i; krh@84: for (j = 0; j < ARRAY_SIZE(index_map); j++) { krh@84: struct rpm_header_index **p; krh@84: if (index_map[j].tag == ntohl(index->tag)) { krh@84: p = (void *) rpm + index_map[j].offset; krh@84: *p = index; krh@84: } krh@84: } krh@74: } krh@75: krh@77: /* Look up dir names now so we can index them directly. */ krh@77: if (rpm->dirnames != NULL) { krh@77: count = ntohl(rpm->dirnames->count); krh@77: rpm->dirs = calloc(count, sizeof *rpm->dirs); krh@77: name = rpm->pool + ntohl(rpm->dirnames->offset); krh@77: for (i = 0; i < count; i++) { krh@77: rpm->dirs[i] = name; krh@77: name += strlen(name) + 1; krh@77: } krh@77: } krh@77: krh@77: return rpm; krh@77: } krh@77: krh@77: struct cpio_file_header { krh@77: char magic[6]; krh@77: char inode[8]; krh@77: char mode[8]; krh@77: char uid[8]; krh@77: char gid[8]; krh@77: char nlink[8]; krh@77: char mtime[8]; krh@77: char filesize[8]; krh@77: char devmajor[8]; krh@77: char devminor[8]; krh@77: char rdevmajor[8]; krh@77: char rdevminor[8]; krh@77: char namesize[8]; krh@77: char checksum[8]; krh@77: char filename[0]; krh@77: }; krh@77: krh@77: /* gzip flags */ krh@77: #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ krh@77: #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ krh@77: #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ krh@77: #define ORIG_NAME 0x08 /* bit 3 set: original file name present */ krh@77: #define COMMENT 0x10 /* bit 4 set: file comment present */ krh@77: #define RESERVED 0xE0 /* bits 5..7: reserved */ krh@77: krh@77: static int krh@77: create_path(const char *root, const char *path, krh@77: const char *name, unsigned mode, int *fd) krh@77: { krh@77: char buffer[256], *p; krh@77: const char *slash, *next; krh@77: struct stat buf; krh@77: krh@77: /* Create all sub-directories in dir and then create name. We krh@77: * know root exists and is a dir, root does not end in a '/', krh@77: * and path has a leading '/'. */ krh@77: krh@77: strcpy(buffer, root); krh@77: p = buffer + strlen(buffer); krh@77: slash = path; krh@77: for (slash = path; slash[1] != '\0'; slash = next) { krh@77: next = strchr(slash + 1, '/'); krh@77: memcpy(p, slash, next - slash); krh@77: p += next - slash; krh@77: *p = '\0'; krh@77: krh@77: if (stat(buffer, &buf) == 0) { krh@77: if (!S_ISDIR(buf.st_mode)) { krh@77: fprintf(stderr, krh@77: "%s exists but is not a directory\n", krh@77: buffer); krh@77: return -1; krh@77: } krh@77: } else if (mkdir(buffer, 0777) < 0) { krh@77: fprintf(stderr, "failed to make directory %s: %m\n", krh@77: buffer); krh@77: return -1; krh@77: } krh@77: /* FIXME: permissions */ krh@77: } krh@77: krh@77: *p++ = '/'; krh@77: strcpy(p, name); krh@77: krh@77: switch (mode >> 12) { krh@77: case REG: krh@77: default: krh@77: *fd = open(buffer, O_WRONLY | O_CREAT | O_TRUNC, mode & 0x1ff); krh@77: return *fd; krh@77: case XDIR: krh@77: *fd = -1; krh@77: return mkdir(buffer, mode & 0x1ff); krh@77: } krh@77: } krh@77: krh@77: int krh@77: razor_rpm_install(struct razor_rpm *rpm, const char *root) krh@77: { krh@77: z_stream stream; krh@77: unsigned char payload[32768], *gz_header; krh@77: char buffer[256]; krh@77: int err, method, flags, count, i, fd, written; krh@77: struct cpio_file_header *header; krh@77: unsigned long *size, *index, rest, length; krh@77: unsigned short *mode; krh@77: const char *name, *dir; krh@77: struct stat buf; krh@77: krh@77: if (stat(root, &buf) < 0 || !S_ISDIR(buf.st_mode)) { krh@77: fprintf(stderr, krh@77: "root installation directory \"%s\" does not exist\n", krh@77: root); krh@77: return -1; krh@77: } krh@77: krh@77: gz_header = rpm->payload; krh@77: if (gz_header[0] != 0x1f || gz_header[1] != 0x8b) { krh@77: fprintf(stderr, "payload section doesn't have gz header\n"); krh@77: return -1; krh@77: } krh@77: krh@77: method = gz_header[2]; krh@77: flags = gz_header[3]; krh@77: krh@77: if (method != Z_DEFLATED || flags != 0) { krh@77: fprintf(stderr, krh@77: "unknown payload compression method or flags set\n"); krh@77: return -1; krh@77: } krh@77: krh@77: stream.zalloc = NULL; krh@77: stream.zfree = NULL; krh@77: stream.opaque = NULL; krh@77: krh@77: stream.next_in = gz_header + 10; krh@77: stream.avail_in = (rpm->map + rpm->size) - (void *) stream.next_in; krh@77: stream.next_out = NULL; krh@77: stream.avail_out = 0; krh@77: krh@77: err = inflateInit2(&stream, -MAX_WBITS); krh@77: if (err != Z_OK) { krh@77: fprintf(stderr, "inflateInit error: %d\n", err); krh@77: return -1; krh@77: } krh@77: krh@77: count = ntohl(rpm->basenames->count); krh@77: size = (unsigned long *) (rpm->pool + ntohl(rpm->filesizes->offset)); krh@77: index = (unsigned long *) (rpm->pool + ntohl(rpm->dirindexes->offset)); krh@77: mode = (unsigned short *) (rpm->pool + ntohl(rpm->filemodes->offset)); krh@77: name = rpm->pool + ntohl(rpm->basenames->offset); krh@77: for (i = 0; i < count; i++) { krh@77: dir = rpm->dirs[ntohl(*index)]; krh@77: snprintf(buffer, sizeof buffer, "%s%s", dir, name); krh@77: krh@77: stream.next_out = payload; krh@77: /* Plus two for the leading '.' and the terminating NUL. */ krh@77: stream.avail_out = krh@77: ALIGN(sizeof *header + strlen(buffer) + 2, 4); krh@77: err = inflate(&stream, Z_SYNC_FLUSH); krh@77: if (err != Z_OK) { krh@77: fprintf(stderr, "inflate error: %d\n", err); krh@77: return -1; krh@77: } krh@77: krh@77: header = (struct cpio_file_header *) payload; krh@77: krh@77: /* FIXME: Figure out if it's a symlink, device file, krh@77: * directorys or whatever. Maybe do this upfront. */ krh@77: if (create_path(root, dir, name, ntohs(*mode), &fd) < 0) krh@77: return -1; krh@77: if (ntohs(*mode) >> 12 == XDIR) krh@77: rest = 0; krh@77: else krh@77: rest = ntohl(*size); krh@77: krh@77: while (rest > 0) { krh@77: if (ALIGN(rest, 4) > sizeof payload) krh@77: length = sizeof payload; krh@77: else krh@77: length = rest; krh@77: stream.next_out = payload; krh@77: stream.avail_out = ALIGN(length, 4); krh@77: err = inflate(&stream, Z_SYNC_FLUSH); krh@77: if (err != Z_OK && err != Z_STREAM_END) { krh@77: fprintf(stderr, krh@77: "inflate error: %d (%m)\n", err); krh@77: return -1; krh@77: } krh@77: rest -= length; krh@77: stream.next_out = payload; krh@77: while (length > 0) { krh@77: written = write(fd, stream.next_out, length); krh@77: if (written < 0) { krh@77: fprintf(stderr, "write error: %m\n"); krh@77: return -1; krh@77: } krh@77: length -= written; krh@77: } krh@77: } krh@77: if (fd > 0 && close(fd) < 0) { krh@77: fprintf(stderr, "failed to close \"%s/%s%s\": %m\n", krh@77: root, dir, name); krh@77: return -1; krh@77: } krh@77: name += strlen(name) + 1; krh@77: index++; krh@77: size++; krh@77: mode++; krh@77: } krh@77: krh@77: err = inflateEnd(&stream); krh@77: krh@77: if (err != Z_OK) { krh@77: fprintf(stderr, "inflateEnd error: %d\n", err); krh@77: return -1; krh@77: } krh@77: krh@75: return 0; krh@74: } krh@74: krh@77: int krh@77: razor_rpm_close(struct razor_rpm *rpm) krh@74: { krh@77: int err; krh@77: krh@77: free(rpm->dirs); krh@77: err = munmap(rpm->map, rpm->size); krh@77: free(rpm); krh@77: krh@77: return err; krh@75: } krh@74: krh@75: int krh@77: razor_importer_add_rpm(struct razor_importer *importer, struct razor_rpm *rpm) krh@75: { krh@77: razor_importer_begin_package(importer, krh@77: rpm->pool + ntohl(rpm->name->offset), krh@77: rpm->pool + ntohl(rpm->version->offset)); krh@75: krh@77: import_properties(importer, &rpm->requires, krh@77: rpm->pool, RAZOR_PROPERTY_REQUIRES); krh@77: import_properties(importer, &rpm->provides, krh@77: rpm->pool, RAZOR_PROPERTY_PROVIDES); krh@77: import_properties(importer, &rpm->conflicts, krh@77: rpm->pool, RAZOR_PROPERTY_CONFLICTS); krh@77: import_properties(importer, &rpm->obsoletes, krh@77: rpm->pool, RAZOR_PROPERTY_OBSOLETES); krh@77: import_files(importer, rpm); krh@74: krh@75: razor_importer_finish_package(importer); krh@74: krh@75: return 0; krh@74: }