krh@0: #include krh@0: #include krh@0: #include krh@0: #include krh@0: #include krh@0: #include krh@0: #include krh@0: #include krh@0: krh@0: #include krh@0: #include "sha1.h" krh@0: krh@6: struct array { krh@6: void *data; krh@6: int size, alloc; krh@6: }; krh@6: krh@6: static void * krh@6: array_add(struct array *array, int size) krh@6: { krh@6: int alloc; krh@6: void *data, *p; krh@6: krh@6: if (array->alloc > 0) krh@6: alloc = array->alloc; krh@6: else krh@6: alloc = 1024; krh@6: krh@6: while (alloc < array->size + size) krh@6: alloc *= 2; krh@6: krh@6: if (array->alloc < alloc) { krh@6: data = realloc(array->data, alloc); krh@6: if (data == NULL) krh@6: return 0; krh@6: array->data = data; krh@6: array->alloc = alloc; krh@6: } krh@6: krh@6: p = array->data + array->size; krh@6: array->size += size; krh@6: krh@6: return p; krh@6: } krh@6: krh@0: static int krh@0: write_to_fd(int fd, void *p, size_t size) krh@0: { krh@0: int rest, len; krh@0: krh@0: rest = size; krh@0: while (rest > 0) { krh@0: len = write(fd, p, rest); krh@0: if (len < 0) krh@0: return -1; krh@0: rest -= len; krh@0: } krh@0: krh@0: return 0; krh@0: } krh@0: krh@0: static int krh@0: write_to_file(const char *filename, void *p, size_t size) krh@0: { krh@0: int fd, err; krh@0: krh@0: fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666); krh@0: if (fd < 0) krh@0: return -1; krh@0: err = write_to_fd(fd, p, size); krh@0: close(fd); krh@0: krh@0: return err; krh@0: } krh@0: krh@0: static void * krh@0: zalloc(size_t size) krh@0: { krh@0: void *p; krh@0: krh@0: p = malloc(size); krh@0: memset(p, 0, size); krh@0: krh@0: return p; krh@0: } krh@0: krh@4: struct razor_set_header { krh@4: unsigned int magic; krh@4: unsigned int version; krh@4: struct { unsigned int type, offset; } sections[0]; krh@4: }; krh@4: krh@4: #define RAZOR_MAGIC 0x7a7a7a7a krh@4: #define RAZOR_VERSION 1 krh@4: #define RAZOR_BUCKETS 1 krh@4: #define RAZOR_STRINGS 2 krh@4: #define RAZOR_PACKAGES 3 krh@7: #define RAZOR_PROVIDES 4 krh@4: krh@4: struct razor_package { krh@4: unsigned long name; krh@4: unsigned long version; krh@4: }; krh@4: krh@7: struct razor_provides { krh@7: unsigned long name; krh@7: unsigned long version; krh@7: }; krh@7: krh@4: struct razor_set { krh@6: struct array buckets; krh@6: struct array string_pool; krh@7: struct array packages; krh@7: struct array provides; krh@4: struct razor_set_header *header; krh@4: }; krh@4: krh@4: struct razor_set * krh@4: razor_set_create(void) krh@0: { krh@4: struct razor_set *set; krh@6: char *p; krh@0: krh@6: set = zalloc(sizeof(struct razor_set)); krh@6: p = array_add(&set->string_pool, 1); krh@6: *p = '\0'; krh@3: krh@4: return set; krh@0: } krh@0: krh@4: struct razor_set * krh@4: razor_set_open(const char *filename) krh@0: { krh@4: struct razor_set *set; krh@0: struct stat stat; krh@0: unsigned int size, offset; krh@0: int fd, i; krh@0: krh@4: set = zalloc(sizeof *set); krh@0: fd = open(filename, O_RDONLY); krh@0: if (fstat(fd, &stat) < 0) krh@0: return NULL; krh@4: set->header = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); krh@4: if (set->header == MAP_FAILED) { krh@4: free(set); krh@0: return NULL; krh@0: } krh@0: krh@4: for (i = 0; i < set->header->sections[i].type; i++) { krh@4: offset = set->header->sections[i].offset; krh@4: size = set->header->sections[i + 1].offset - offset; krh@0: krh@4: switch (set->header->sections[i].type) { krh@4: case RAZOR_BUCKETS: krh@6: set->buckets.data = (void *) set->header + offset; krh@6: set->buckets.size = size; krh@6: set->buckets.alloc = size; krh@0: break; krh@4: case RAZOR_STRINGS: krh@6: set->string_pool.data = (void *) set->header + offset; krh@6: set->string_pool.size = size; krh@6: set->string_pool.alloc = size; krh@0: break; krh@4: case RAZOR_PACKAGES: krh@6: set->packages.data = (void *) set->header + offset; krh@6: set->packages.size = size; krh@6: set->packages.size = size; krh@3: break; krh@7: case RAZOR_PROVIDES: krh@7: set->provides.data = (void *) set->header + offset; krh@7: set->provides.size = size; krh@7: set->provides.size = size; krh@7: break; krh@0: } krh@0: } krh@0: close(fd); krh@0: krh@4: return set; krh@0: } krh@0: krh@0: void krh@4: razor_set_destroy(struct razor_set *set) krh@0: { krh@0: unsigned int size; krh@0: int i; krh@0: krh@4: if (set->header) { krh@4: for (i = 0; set->header->sections[i].type; i++) krh@0: ; krh@4: size = set->header->sections[i].type; krh@4: munmap(set->header, size); krh@0: } else { krh@6: free(set->buckets.data); krh@6: free(set->string_pool.data); krh@6: free(set->packages.data); krh@0: } krh@0: krh@4: free(set); krh@0: } krh@0: krh@0: static int krh@4: razor_set_write(struct razor_set *set, const char *filename) krh@0: { krh@0: char data[4096]; krh@4: struct razor_set_header *header = (struct razor_set_header *) data; krh@7: int fd, pool_size, packages_size, provides_size; krh@3: krh@3: /* Align these to pages sizes */ krh@6: pool_size = (set->string_pool.size + 4095) & ~4095; krh@6: packages_size = (set->packages.size + 4095) & ~4095; krh@7: provides_size = (set->provides.size + 4095) & ~4095; krh@0: krh@0: memset(data, 0, sizeof data); krh@4: header->magic = RAZOR_MAGIC; krh@4: header->version = RAZOR_VERSION; krh@0: krh@4: header->sections[0].type = RAZOR_BUCKETS; krh@0: header->sections[0].offset = sizeof data; krh@0: krh@4: header->sections[1].type = RAZOR_STRINGS; krh@6: header->sections[1].offset = krh@6: header->sections[0].offset + set->buckets.alloc; krh@0: krh@4: header->sections[2].type = RAZOR_PACKAGES; krh@6: header->sections[2].offset = krh@6: header->sections[1].offset + pool_size; krh@3: krh@7: header->sections[3].type = RAZOR_PROVIDES; krh@6: header->sections[3].offset = krh@6: header->sections[2].offset + packages_size; krh@0: krh@7: header->sections[4].type = 0; krh@7: header->sections[4].offset = krh@7: header->sections[3].offset + provides_size; krh@7: krh@0: fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666); krh@0: if (fd < 0) krh@0: return -1; krh@0: krh@0: write_to_fd(fd, data, sizeof data); krh@6: write_to_fd(fd, set->buckets.data, set->buckets.alloc); krh@6: write_to_fd(fd, set->string_pool.data, pool_size); krh@6: write_to_fd(fd, set->packages.data, packages_size); krh@7: write_to_fd(fd, set->provides.data, provides_size); krh@0: krh@0: return 0; krh@0: } krh@0: krh@0: static unsigned int krh@0: hash_string(const char *key) krh@0: { krh@0: const char *p; krh@0: unsigned int hash = 0; krh@0: krh@0: for (p = key; *p; p++) krh@0: hash = (hash << 2) ^ *p; krh@0: krh@0: return hash; krh@0: } krh@0: krh@0: unsigned long krh@4: razor_set_lookup(struct razor_set *set, const char *key) krh@0: { krh@6: unsigned int mask, start, i; krh@6: unsigned long *b; krh@6: char *pool; krh@0: krh@6: pool = set->string_pool.data; krh@6: mask = set->buckets.alloc - 1; krh@6: start = hash_string(key) * sizeof(unsigned long); krh@0: krh@6: for (i = 0; i < set->buckets.alloc; i += sizeof *b) { krh@6: b = set->buckets.data + ((start + i) & mask); krh@6: krh@6: if (*b == 0) krh@0: return 0; krh@0: krh@6: if (strcmp(key, &pool[*b]) == 0) krh@6: return *b; krh@6: } krh@0: krh@0: return 0; krh@0: } krh@0: krh@0: static unsigned long krh@4: add_to_string_pool(struct razor_set *set, const char *key) krh@0: { krh@6: int len; krh@6: char *p; krh@0: krh@0: len = strlen(key) + 1; krh@6: p = array_add(&set->string_pool, len); krh@6: memcpy(p, key, len); krh@0: krh@6: return p - (char *) set->string_pool.data; krh@0: } krh@0: krh@0: static void krh@4: do_insert(struct razor_set *set, unsigned long value) krh@0: { krh@6: unsigned int mask, start, i; krh@6: unsigned long *b; krh@0: const char *key; krh@0: krh@6: key = (char *) set->string_pool.data + value; krh@6: mask = set->buckets.alloc - 1; krh@6: start = hash_string(key) * sizeof(unsigned long); krh@6: krh@6: for (i = 0; i < set->buckets.alloc; i += sizeof *b) { krh@6: b = set->buckets.data + ((start + i) & mask); krh@6: if (*b == 0) { krh@6: *b = value; krh@0: break; krh@0: } krh@6: } krh@0: } krh@0: krh@0: unsigned long krh@4: razor_set_insert(struct razor_set *set, const char *key) krh@0: { krh@6: unsigned long value, *buckets, *b, *end; krh@6: int alloc; krh@0: krh@6: alloc = set->buckets.alloc; krh@6: array_add(&set->buckets, 4 * sizeof *buckets); krh@6: if (alloc != set->buckets.alloc) { krh@6: end = set->buckets.data + alloc; krh@6: memset(end, 0, set->buckets.alloc - alloc); krh@6: for (b = set->buckets.data; b < end; b++) { krh@6: value = *b; krh@6: if (value != 0) { krh@6: *b = 0; krh@4: do_insert(set, value); krh@6: } krh@0: } krh@0: } krh@0: krh@4: value = add_to_string_pool(set, key); krh@4: do_insert (set, value); krh@0: krh@0: return value; krh@0: } krh@0: krh@3: static unsigned long krh@4: razor_set_add_package(struct razor_set *set, krh@3: unsigned long name, unsigned long version) krh@3: { krh@6: struct razor_package *p; krh@3: krh@6: p = array_add(&set->packages, sizeof *p); krh@3: krh@6: p->name = name; krh@6: p->version = version; krh@3: krh@6: return p - (struct razor_package *) set->packages.data; krh@0: } krh@0: krh@7: static unsigned long krh@7: razor_set_add_provides(struct razor_set *set, krh@7: unsigned long name, unsigned long version) krh@7: { krh@7: struct razor_provides *p; krh@7: krh@7: p = array_add(&set->provides, sizeof *p); krh@7: krh@7: p->name = name; krh@7: p->version = version; krh@7: krh@7: return p - (struct razor_provides *) set->packages.data; krh@7: } krh@7: krh@0: unsigned long krh@4: razor_set_tokenize(struct razor_set *set, const char *string) krh@0: { krh@0: unsigned long token; krh@0: krh@4: token = razor_set_lookup(set, string); krh@0: if (token != 0) krh@0: return token; krh@0: krh@4: return razor_set_insert(set, string); krh@0: } krh@0: krh@4: static struct razor_set *qsort_set; krh@3: krh@3: static int krh@3: compare_packages(const void *p1, const void *p2) krh@3: { krh@4: const struct razor_package *pkg1 = p1, *pkg2 = p2; krh@6: char *pool = qsort_set->string_pool.data; krh@3: krh@6: return strcmp(&pool[pkg1->name], &pool[pkg2->name]); krh@3: } krh@3: krh@7: static int krh@7: compare_provides(const void *p1, const void *p2) krh@7: { krh@7: const struct razor_provides *prv1 = p1, *prv2 = p2; krh@7: char *pool = qsort_set->string_pool.data; krh@7: krh@7: return strcmp(&pool[prv1->name], &pool[prv2->name]); krh@7: } krh@7: krh@3: static void krh@4: razor_set_sort(struct razor_set *set) krh@3: { krh@4: qsort_set = set; krh@6: qsort(set->packages.data, krh@6: set->packages.size / sizeof(struct razor_package), krh@6: sizeof(struct razor_package), compare_packages); krh@7: qsort(set->provides.data, krh@7: set->provides.size / sizeof(struct razor_provides), krh@7: sizeof(struct razor_provides), compare_provides); krh@3: } krh@3: krh@0: struct parsing_context { krh@4: struct razor_set *set; krh@4: int pkg_id; krh@0: }; krh@0: krh@0: static void krh@3: parse_package(struct parsing_context *ctx, const char **atts) krh@3: { krh@7: unsigned long name = 0, version = 0; krh@3: int i; krh@3: krh@3: for (i = 0; atts[i]; i += 2) { krh@3: if (strcmp(atts[i], "name") == 0) krh@4: name = razor_set_tokenize(ctx->set, atts[i + 1]); krh@3: else if (strcmp(atts[i], "version") == 0) krh@4: version = razor_set_tokenize(ctx->set, atts[i + 1]); krh@3: } krh@3: krh@3: if (name == 0 || version == 0) { krh@3: fprintf(stderr, "invalid package tag, " krh@3: "missing name or version attributes\n"); krh@3: return; krh@3: } krh@3: krh@4: ctx->pkg_id = razor_set_add_package(ctx->set, name, version); krh@4: krh@4: return; krh@3: } krh@3: krh@3: static void krh@7: parse_provides(struct parsing_context *ctx, const char **atts) krh@7: { krh@7: unsigned long name = 0, version = 0; krh@7: int i; krh@7: krh@7: for (i = 0; atts[i]; i += 2) { krh@7: if (strcmp(atts[i], "name") == 0) krh@7: name = razor_set_tokenize(ctx->set, atts[i + 1]); krh@7: } krh@7: krh@7: if (name == 0) { krh@7: fprintf(stderr, "invalid provides tag, " krh@7: "missing name attribute\n"); krh@7: return; krh@7: } krh@7: krh@7: ctx->pkg_id = razor_set_add_provides(ctx->set, name, version); krh@7: } krh@7: krh@7: static void krh@0: start_element(void *data, const char *name, const char **atts) krh@0: { krh@0: struct parsing_context *ctx = data; krh@0: int i; krh@0: krh@3: if (strcmp(name, "package") == 0) krh@3: parse_package(ctx, atts); krh@7: else if (strcmp(name, "provides") == 0) krh@7: parse_provides(ctx, atts); krh@3: krh@0: for (i = 0; atts[i]; i += 2) krh@4: razor_set_tokenize(ctx->set, atts[i + 1]); krh@0: } krh@0: krh@0: static void krh@0: end_element (void *data, const char *name) krh@0: { krh@4: struct parsing_context *ctx = data; krh@4: krh@4: if (strcmp(name, "package") == 0) krh@4: ctx->pkg_id = 0; krh@0: } krh@0: krh@0: static char * krh@0: sha1_to_hex(const unsigned char *sha1) krh@0: { krh@0: static int bufno; krh@0: static char hexbuffer[4][50]; krh@0: static const char hex[] = "0123456789abcdef"; krh@0: char *buffer = hexbuffer[3 & ++bufno], *buf = buffer; krh@0: int i; krh@0: krh@0: for (i = 0; i < 20; i++) { krh@0: unsigned int val = *sha1++; krh@0: *buf++ = hex[val >> 4]; krh@0: *buf++ = hex[val & 0xf]; krh@0: } krh@0: *buf = '\0'; krh@0: krh@0: return buffer; krh@0: } krh@0: krh@0: static int krh@4: razor_set_import(struct razor_set *set, const char *filename) krh@0: { krh@0: SHA_CTX sha1; krh@0: XML_Parser parser; krh@4: struct parsing_context ctx; krh@0: int fd; krh@0: void *p; krh@0: struct stat stat; krh@0: char buf[128]; krh@0: unsigned char hash[20]; krh@0: krh@0: fd = open(filename, O_RDONLY); krh@0: if (fstat(fd, &stat) < 0) krh@0: return -1; krh@0: p = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); krh@0: if (p == MAP_FAILED) krh@0: return -1; krh@0: krh@0: parser = XML_ParserCreate(NULL); krh@4: ctx.set = set; krh@4: XML_SetUserData(parser, &ctx); krh@0: XML_SetElementHandler(parser, start_element, end_element); krh@0: if (XML_Parse(parser, p, stat.st_size, 1) == XML_STATUS_ERROR) { krh@0: fprintf(stderr, krh@0: "%s at line %d, %s\n", krh@0: XML_ErrorString(XML_GetErrorCode(parser)), krh@0: XML_GetCurrentLineNumber(parser), krh@0: filename); krh@0: return 1; krh@0: } krh@0: krh@0: XML_ParserFree(parser); krh@0: krh@0: SHA1_Init(&sha1); krh@0: SHA1_Update(&sha1, p, stat.st_size); krh@0: SHA1_Final(hash, &sha1); krh@0: krh@0: close(fd); krh@0: krh@0: snprintf(buf, sizeof buf, "set/%s", sha1_to_hex(hash)); krh@0: if (write_to_file(buf, p, stat.st_size) < 0) krh@0: return -1; krh@0: munmap(p, stat.st_size); krh@0: krh@0: return 0; krh@0: } krh@0: krh@0: void krh@4: razor_set_list(struct razor_set *set) krh@3: { krh@6: struct razor_package *p, *end; krh@6: char *pool; krh@3: krh@6: pool = set->string_pool.data; krh@6: end = set->packages.data + set->packages.size; krh@6: for (p = set->packages.data; p < end && p->name; p++) krh@6: printf("%s %s\n", &pool[p->name], &pool[p->version]); krh@3: } krh@3: krh@3: void krh@7: razor_set_list_provides(struct razor_set *set) krh@7: { krh@7: struct razor_provides *p, *end; krh@7: char *pool; krh@7: krh@7: pool = set->string_pool.data; krh@7: end = set->provides.data + set->provides.size; krh@7: for (p = set->provides.data; p < end && p->name; p++) krh@7: printf("%s %s\n", &pool[p->name], &pool[p->version]); krh@7: } krh@7: krh@7: void krh@4: razor_set_info(struct razor_set *set) krh@3: { krh@3: unsigned int offset, size; krh@3: int i; krh@3: krh@4: for (i = 0; i < set->header->sections[i].type; i++) { krh@4: offset = set->header->sections[i].offset; krh@4: size = set->header->sections[i + 1].offset - offset; krh@3: krh@4: switch (set->header->sections[i].type) { krh@4: case RAZOR_BUCKETS: krh@3: printf("bucket section:\t\t%dkb\n", size / 1024); krh@3: break; krh@4: case RAZOR_STRINGS: krh@3: printf("string pool:\t\t%dkb\n", size / 1024); krh@3: break; krh@4: case RAZOR_PACKAGES: krh@3: printf("package section:\t%dkb\n", size / 1024); krh@3: break; krh@7: case RAZOR_PROVIDES: krh@7: printf("provides section:\t%dkb\n", size / 1024); krh@7: break; krh@3: } krh@3: } krh@0: } krh@0: krh@0: static int krh@0: usage(void) krh@0: { krh@7: printf("usage: razor [ import FILES | lookup | " krh@7: "list | list-provides | info ]\n"); krh@0: exit(1); krh@0: } krh@0: krh@0: static const char repo_filename[] = "system.repo"; krh@0: krh@0: int krh@0: main(int argc, char *argv[]) krh@0: { krh@0: int i; krh@4: struct razor_set *set; krh@0: struct stat statbuf; krh@0: krh@3: if (argc < 2) { krh@0: usage(); krh@0: } else if (strcmp(argv[1], "import") == 0) { krh@0: if (stat("set", &statbuf) && mkdir("set", 0777)) { krh@0: fprintf(stderr, "could not create directory 'set'\n"); krh@0: exit(-1); krh@0: } krh@0: krh@4: set = razor_set_create(); krh@0: krh@0: for (i = 2; i < argc; i++) { krh@4: if (razor_set_import(set, argv[i]) < 0) { krh@0: fprintf(stderr, "failed to import %s\n", krh@0: argv[i]); krh@0: exit(-1); krh@0: } krh@0: } krh@0: krh@4: razor_set_sort(set); krh@3: krh@6: /* FIXME: We add a sentinel package here, but we krh@6: * should probably just have a size field in the krh@6: * header section. */ krh@6: razor_set_add_package(set, 0, 0); krh@7: razor_set_add_provides(set, 0, 0); krh@6: krh@6: printf("bucket allocation: %d\n", set->buckets.alloc); krh@6: printf("pool size: %d\n", set->string_pool.size); krh@6: printf("pool allocation: %d\n", set->string_pool.alloc); krh@7: printf("packages: %d\n", krh@7: set->packages.size / sizeof(struct razor_package)); krh@7: printf("provides: %d\n", krh@7: set->provides.size / sizeof(struct razor_provides)); krh@0: krh@4: razor_set_write(set, repo_filename); krh@0: krh@4: razor_set_destroy(set); krh@0: } else if (strcmp(argv[1], "lookup") == 0) { krh@4: set = razor_set_open(repo_filename); krh@0: printf("%s is %lu\n", argv[2], krh@4: razor_set_lookup(set, argv[2])); krh@4: razor_set_destroy(set); krh@3: } else if (strcmp(argv[1], "list") == 0) { krh@4: set = razor_set_open(repo_filename); krh@4: razor_set_list(set); krh@4: razor_set_destroy(set); krh@7: } else if (strcmp(argv[1], "list-provides") == 0) { krh@7: set = razor_set_open(repo_filename); krh@7: razor_set_list_provides(set); krh@7: razor_set_destroy(set); krh@3: } else if (strcmp(argv[1], "info") == 0) { krh@4: set = razor_set_open(repo_filename); krh@4: razor_set_info(set); krh@4: razor_set_destroy(set); krh@0: } else { krh@0: usage(); krh@0: } krh@0: krh@0: return 0; krh@0: }