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@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@4: krh@4: struct razor_package { krh@4: unsigned long name; krh@4: unsigned long version; krh@4: }; krh@4: krh@4: struct razor_set { krh@4: unsigned long *buckets; krh@4: int bucket_count, bucket_alloc; krh@4: char *string_pool; krh@4: int pool_size, pool_alloc; krh@4: struct razor_set_header *header; krh@4: krh@4: struct razor_package *packages; krh@4: int package_count, package_alloc; krh@4: }; krh@4: krh@4: struct razor_set * krh@4: razor_set_create(void) krh@0: { krh@4: struct razor_set *set; krh@0: krh@4: set = zalloc(sizeof *set); krh@4: set->buckets = zalloc(4096 * sizeof *set->buckets); krh@4: set->bucket_count = 0; krh@4: set->bucket_alloc = 4096; krh@0: krh@4: set->string_pool = zalloc(4096); krh@4: set->pool_size = 1; krh@4: set->pool_alloc = 4096; krh@0: krh@4: set->packages = zalloc(4096 * sizeof *set->packages); krh@4: set->package_count = 0; krh@4: set->package_alloc = 4096; 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@4: set->buckets = (void *) set->header + offset; krh@4: set->bucket_count = size / sizeof *set->buckets; krh@4: set->bucket_alloc = set->bucket_count; krh@0: break; krh@4: case RAZOR_STRINGS: krh@4: set->string_pool = (void *) set->header + offset; krh@4: set->pool_size = size; krh@4: set->pool_alloc = size; krh@0: break; krh@4: case RAZOR_PACKAGES: krh@4: set->packages = (void *) set->header + offset; krh@4: set->package_count = size / sizeof *set->packages; krh@4: set->package_alloc = size / sizeof *set->packages; krh@3: 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@4: free(set->buckets); krh@4: free(set->string_pool); 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@3: int fd, pool_size, package_size; krh@3: krh@3: /* Align these to pages sizes */ krh@4: pool_size = (set->pool_size + 4095) & ~4095; krh@3: package_size = krh@4: (set->package_alloc * sizeof *set->packages + 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@3: header->sections[1].offset = header->sections[0].offset + krh@4: set->bucket_alloc * sizeof *set->buckets; krh@0: krh@4: header->sections[2].type = RAZOR_PACKAGES; krh@3: header->sections[2].offset = header->sections[1].offset + pool_size; krh@3: krh@3: header->sections[3].type = 0; krh@3: header->sections[3].offset = header->sections[2].offset + package_size; 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: krh@0: write_to_fd(fd, data, sizeof data); krh@4: write_to_fd(fd, set->buckets, set->bucket_alloc * sizeof *set->buckets); krh@4: write_to_fd(fd, set->string_pool, pool_size); krh@4: write_to_fd(fd, set->packages, package_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@0: unsigned int start; krh@0: unsigned int mask; krh@0: unsigned long value; krh@0: int i; krh@0: krh@4: mask = set->bucket_alloc - 1; krh@0: start = hash_string(key) & mask; krh@0: i = start; krh@0: do { krh@4: value = set->buckets[i]; krh@0: krh@0: if (value == 0) krh@0: return 0; krh@0: krh@4: if (strcmp(key, &set->string_pool[value]) == 0) krh@0: return value; krh@0: krh@0: i = (i + 1) & mask; krh@0: } while (i != start); 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@0: int len, alloc; krh@0: char *pool; krh@0: unsigned long value; krh@0: krh@0: len = strlen(key) + 1; krh@4: alloc = set->pool_alloc; krh@4: while (alloc < set->pool_size + len) krh@0: alloc *= 2; krh@4: if (set->pool_alloc < alloc) { krh@4: pool = realloc(set->string_pool, alloc); krh@0: if (pool == NULL) krh@0: return 0; krh@4: set->string_pool = pool; krh@4: set->pool_alloc = alloc; krh@0: } krh@0: krh@4: memcpy(set->string_pool + set->pool_size, key, len); krh@4: value = set->pool_size; krh@4: set->pool_size += len; krh@0: krh@0: return value; krh@0: } krh@0: krh@0: static void krh@4: do_insert(struct razor_set *set, unsigned long value) krh@0: { krh@0: unsigned int mask; krh@0: const char *key; krh@0: int i, start; krh@0: krh@4: key = &set->string_pool[value]; krh@4: mask = set->bucket_alloc - 1; krh@0: start = hash_string(key) & mask; krh@0: i = start; krh@0: do { krh@4: if (set->buckets[i] == 0) { krh@4: set->buckets[i] = value; krh@0: break; krh@0: } krh@0: i = (i + 1) & mask; krh@0: } while (i != start); krh@0: } krh@0: krh@0: unsigned long krh@4: razor_set_insert(struct razor_set *set, const char *key) krh@0: { krh@0: unsigned long value, *buckets, *old_buckets; krh@0: int i, alloc, old_alloc; krh@0: krh@4: alloc = set->bucket_alloc; krh@4: while (alloc < 4 * set->bucket_count) krh@0: alloc *= 2; krh@0: krh@4: if (alloc != set->bucket_alloc) { krh@4: buckets = zalloc(alloc * sizeof *set->buckets); krh@0: if (buckets == NULL) krh@0: return 0; krh@4: old_buckets = set->buckets; krh@4: set->buckets = buckets; krh@4: old_alloc = set->bucket_alloc; krh@4: set->bucket_alloc = alloc; krh@0: krh@0: for (i = 0; i < old_alloc; i++) { krh@0: value = old_buckets[i]; krh@0: if (value != 0) krh@4: do_insert(set, value); krh@0: } krh@0: free(old_buckets); krh@0: } krh@0: krh@4: value = add_to_string_pool(set, key); krh@4: do_insert (set, value); krh@4: set->bucket_count++; 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@4: struct razor_package *packages; krh@3: int alloc; krh@3: krh@4: /* FIXME: make 0 an illegal pkgs number. */ krh@4: alloc = set->package_alloc; krh@4: while (alloc < set->package_count + 1) krh@3: alloc *= 2; krh@4: if (set->package_alloc < alloc) { krh@4: packages = realloc(set->packages, alloc * sizeof set->packages); krh@3: if (packages == NULL) krh@3: return 0; krh@4: set->packages = packages; krh@4: set->package_alloc = alloc; krh@3: } krh@3: krh@4: set->packages[set->package_count].name = name; krh@4: set->packages[set->package_count].version = version; krh@3: krh@4: return set->package_count++; krh@0: } krh@0: 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@3: krh@4: return strcmp(&qsort_set->string_pool[pkg1->name], krh@4: &qsort_set->string_pool[pkg2->name]); krh@3: } krh@3: krh@3: static void krh@4: razor_set_sort(struct razor_set *set) krh@3: { krh@4: qsort_set = set; krh@4: qsort(set->packages, set->package_count, sizeof *set->packages, krh@3: compare_packages); 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@3: unsigned long name, version; 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@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@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@3: int i; krh@4: struct razor_package *p; krh@3: krh@4: p = set->packages; krh@4: for (i = 0; i < set->package_count && p->name; i++, p++) { krh@3: printf("%s %s\n", krh@4: &set->string_pool[p->name], krh@4: &set->string_pool[p->version]); krh@3: } krh@3: } krh@3: krh@3: 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@3: } krh@3: } krh@0: } krh@0: krh@0: static int krh@0: usage(void) krh@0: { krh@3: printf("usage: razor [ import FILES | lookup | list | 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@0: printf("number of buckets: %d\n", krh@4: set->bucket_count); krh@0: printf("bucket allocation: %d\n", krh@4: set->bucket_alloc); krh@4: printf("pool size: %d\n", set->pool_size); krh@4: printf("pool allocation: %d\n", set->pool_alloc); 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@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: }