krh@74: #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@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@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@77: int fd, nindex, hsize, i, 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@75: switch (ntohl(index->tag)) { krh@74: case RPMTAG_NAME: krh@75: rpm->name = index; krh@74: break; krh@74: case RPMTAG_VERSION: krh@75: rpm->version = index; krh@74: break; krh@74: case RPMTAG_RELEASE: krh@75: rpm->release = index; krh@74: break; krh@75: krh@74: case RPMTAG_REQUIRENAME: krh@75: rpm->requires.name = index; krh@74: break; krh@75: case RPMTAG_REQUIREVERSION: krh@75: rpm->requires.version = index; krh@74: break; krh@75: case RPMTAG_REQUIREFLAGS: krh@75: rpm->requires.flags = index; krh@75: break; krh@74: krh@75: case RPMTAG_PROVIDENAME: krh@75: rpm->provides.name = index; krh@74: break; krh@75: case RPMTAG_PROVIDEVERSION: krh@75: rpm->provides.version = index; krh@75: break; krh@75: case RPMTAG_PROVIDEFLAGS: krh@75: rpm->provides.flags = index; krh@75: break; krh@75: krh@75: case RPMTAG_OBSOLETENAME: krh@75: rpm->obsoletes.name = index; krh@75: break; krh@75: case RPMTAG_OBSOLETEVERSION: krh@75: rpm->obsoletes.version = index; krh@75: break; krh@75: case RPMTAG_OBSOLETEFLAGS: krh@75: rpm->obsoletes.flags = index; krh@75: break; krh@75: krh@75: case RPMTAG_CONFLICTNAME: krh@75: rpm->conflicts.name = index; krh@75: break; krh@75: case RPMTAG_CONFLICTVERSION: krh@75: rpm->conflicts.version = index; krh@75: break; krh@75: case RPMTAG_CONFLICTFLAGS: krh@75: rpm->conflicts.flags = index; krh@75: break; krh@75: krh@75: case RPMTAG_DIRINDEXES: krh@75: rpm->dirindexes = index; krh@75: break; krh@75: case RPMTAG_BASENAMES: krh@75: rpm->basenames = index; krh@75: break; krh@75: case RPMTAG_DIRNAMES: krh@75: rpm->dirnames = index; krh@74: break; krh@77: case RPMTAG_FILESIZES: krh@77: rpm->filesizes = index; krh@77: break; krh@77: case RPMTAG_FILEMODES: krh@77: rpm->filemodes = index; krh@77: break; krh@77: case RPMTAG_FILESTATES: krh@77: rpm->filestates = index; krh@77: break; krh@74: } 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: }