This is an attempt to add a one-file-per-database backend. The main reason I wrote this was because load times of the database on my laptop when it's not yet cached is around 40-50 seconds. When you just want to do a quick "pacman -Ss" that can be a little annoying. I know you have discussed the current database vs. one-file earlier, and I think someone said another backend could be supported too. This first patch contains the main database code and a program to convert .db.tar.gz and file databases into the new format. Signed-off-by: Sivert Berg <sivertb@stud.ntnu.no> --- lib/libalpm/be_packed.h | 757 +++++++++++++++++++++++++++++++++++++++++++++++ src/util/Makefile.am | 4 +- src/util/convdb.c | 236 +++++++++++++++ 3 files changed, 996 insertions(+), 1 deletions(-) create mode 100644 lib/libalpm/be_packed.h create mode 100644 src/util/convdb.c diff --git a/lib/libalpm/be_packed.h b/lib/libalpm/be_packed.h new file mode 100644 index 0000000..e118a3a --- /dev/null +++ b/lib/libalpm/be_packed.h @@ -0,0 +1,757 @@ +/* + * Copyright (c) 2008 Sivert Berg + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/* _HOW IT WORKS_ + * + * If you've ever looked at the ext2 filesystem you will find alot + * of similarities (among others the inode that I didn't have the + * imagination to find a new name for). + * + * _GROUP_ + * Basicly the database is divided into groups. Each group got the + * same number of blocks and inodes. + * + * _INODE_ + * Each file is assigned an unique inode. The inode got a list of + * blocks where the actual data for the file is stored. Worth noting + * is that the last entry in the block list is indirect. That means + * it points to a block that points to blocks. + */ + +#include <stdint.h> +#include <sys/mman.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + + +#ifndef BE_PACKED_H +#define BE_PACKED_H + +#define BLOCK_SIZE 256 +#define GROUP_SIZE (BLOCK_SIZE * 4096) +#define BLOCKS_PER_GROUP 3936 +#define INODES_PER_GROUP 1024 + +#define MIN(a, b) (((a) < (b))?(a):(b)) + +/* _pack_open_file flags */ +#define PO_READ 1 +#define PO_WRITE 2 + + +typedef struct __inode_t { + int32_t size; + int32_t block[7]; +} inode_t; + +typedef struct __group_desc_t { + uint32_t inodes_free; + uint32_t blocks_free; + uint32_t checksum; + inode_t i_table[INODES_PER_GROUP]; + uint32_t bitmap[BLOCKS_PER_GROUP / 32]; + char data[]; +} group_desc_t; + +typedef struct __package_t { + int32_t inode; + uint16_t rec_len; + uint16_t name_len; + char name[]; +} package_t; + +typedef struct __pack_db_t { + int fd; + int groups; + int read_only; + group_desc_t *map; +} pack_db_t; + +typedef struct __pack_file_t { + int inode; + size_t pos; +} pack_file_t; + +typedef struct __pack_dir_t { + size_t pos; +} pack_dir_t; + + +/** + ** GROUP FUNCTIONS + */ +/* Initilizes a new group */ +void _pack_init_group(group_desc_t *group) +{ + int i, j; + + for (i = 0; i < INODES_PER_GROUP; i++) { + group->i_table[i].size = -1; + for (j = 0; j < 7; j++) + group->i_table[i].block[j] = -1; + } + + for (i = 0; i < BLOCKS_PER_GROUP / 32; i++) { + group->bitmap[i] = 0; + } + + group->inodes_free = INODES_PER_GROUP; + group->blocks_free = BLOCKS_PER_GROUP; + group->checksum = 0; +} + + +/* Adds a new group to the file */ +void _pack_new_group(pack_db_t *db) +{ + db->groups++; + ftruncate(db->fd, db->groups * GROUP_SIZE); + db->map = mremap(db->map, (db->groups - 1) * GROUP_SIZE, + db->groups * GROUP_SIZE, MREMAP_MAYMOVE); + + _pack_init_group((group_desc_t*)((char*)db->map + + GROUP_SIZE * (db->groups - 1))); +} + + +/** + ** POINTER FUNCTIONS (return pointers to various structures) + */ +group_desc_t *_pack_get_group(pack_db_t *db, int group) +{ + return (group_desc_t*)((char*)db->map + group*GROUP_SIZE); +} + + +inode_t *_pack_get_inode(pack_db_t *db, int inode) +{ + int group = inode / INODES_PER_GROUP; + group_desc_t *groupd = _pack_get_group(db, group); + inode %= INODES_PER_GROUP; + + return groupd->i_table + inode; +} + +void *_pack_get_block(pack_db_t *db, int block) +{ + if (block == -1) + return NULL; + + group_desc_t *group = _pack_get_group(db, block / BLOCKS_PER_GROUP); + return group->data + (block % BLOCKS_PER_GROUP) * BLOCK_SIZE; +} + + +/* Searches for a free block in the bitmap and marks the bit + * it finds as used. + */ +int _pack_check_bitmap(group_desc_t *group) +{ + int i = 0, j = 0, t = 0; + + for (i = 0; i < BLOCKS_PER_GROUP / 32; i++) { + if (group->bitmap[i] != 0xffffffff) { + t = group->bitmap[i]; + for (j = 0; j < 32 && (t & 1); j++, t >>= 1); + break; + } + } + + group->bitmap[i] |= 1 << j; + return i * 32 + j; +} + + +/* Clears the n'th bit */ +int _pack_bitmap_free(group_desc_t *group, int n) +{ + int i = n / 32; + int j = n % 32; + + group->bitmap[i] &= ~(i << j); + return 0; +} + + +/* Returns the number of a new unused block + * CAUTION: THIS FUNCTION MAY RESULT IN A MMREMAP! + * that means ANY pointers into the database may be invalid + * after you've called this function. + */ +int _pack_get_free_block(pack_db_t *db) +{ + int i; + group_desc_t *group; + int ret; + + for (i = 0; i < db->groups; i++) { + group = _pack_get_group(db, i); + if (group->blocks_free) { + group->blocks_free--; + ret = _pack_check_bitmap(group); + ret += i * BLOCKS_PER_GROUP; + return ret;; + } + } + + _pack_new_group(db); + group = _pack_get_group(db, i); + group->blocks_free--; + ret = _pack_check_bitmap(group); + ret += i * BLOCKS_PER_GROUP; + + return ret; +} + + +/* Frees the block given */ +int _pack_block_free(pack_db_t *db, int block) +{ + group_desc_t *group; + int g, b; + + g = block / BLOCKS_PER_GROUP; + b = block & BLOCKS_PER_GROUP; + + group = _pack_get_group(db, g); + group->blocks_free++; + + return _pack_bitmap_free(group, b); +} + + +/* Returns the number of a new free inode. + * CAUTION: THIS FUNCTION MAY RESULT IN A MMREMAP! + * that means ANY pointers into the database may be invalid + * after you've called this function. + */ +int _pack_get_free_inode(pack_db_t *db) +{ + int i = 0, j = 0; + group_desc_t *group; + + for (i = 0; i < db->groups; i++) { + group = _pack_get_group(db, i); + if (group->inodes_free) { + group->inodes_free--; + for (j = 0; j < INODES_PER_GROUP; j++) { + if (group->i_table[j].size == -1) { + group->i_table[j].size = 0; + return i * INODES_PER_GROUP + j; + } + } + } + } + + _pack_new_group(db); + group = _pack_get_group(db, i); + group->inodes_free--; + + return i * INODES_PER_GROUP; +} + + +/* Frees an inode and all of the blocks it might be using */ +int _pack_inode_free(pack_db_t *db, int in) +{ + inode_t *inode = _pack_get_inode(db, in); + group_desc_t *group = _pack_get_group(db, in / INODES_PER_GROUP); + int i; + int block; + int32_t *blocks; + int size = BLOCK_SIZE / sizeof(uint32_t) - 1; + + inode->size = -1; + + for (i = 0; i < 6; i++) { + if (inode->block[i] != -1) { + _pack_block_free(db, inode->block[i]); + inode->block[i] = -1; + } + } + + block = inode->block[6]; + blocks = _pack_get_block(db, block); + + while(blocks != NULL) { + for (i = 0; i < size; i++) { + if (blocks[i] != -1) + _pack_block_free(db, blocks[i]); + } + + _pack_block_free(db, block); + block = blocks[size]; + blocks = _pack_get_block(db, block); + } + + group->inodes_free++; + + return 0; +} + + +/* Returns the number of the block associated with the inode */ +int _pack_inode_get_block(pack_db_t *db, int i, int block) +{ + inode_t *inode = _pack_get_inode(db, i); + if (block < 6) + return inode->block[block]; + + block -= 6; + int size = BLOCK_SIZE / sizeof(uint32_t) - 1; + uint32_t *blocks = _pack_get_block(db, inode->block[6]); + + for (;blocks != NULL && block >= size; block -= size) + blocks = _pack_get_block(db, blocks[size]); + + if (blocks == NULL) + return -1; + + return blocks[block]; +} + + +/* Returns the number of the block block associated with the inode. + * If there is no such block, it gets allocated. + * CAUTION: THIS FUNCTION MAY RESULT IN A MMREMAP! + * that means ANY pointers into the database may be invalid + * after you've called this function. + */ +int _pack_inode_add_block(pack_db_t *db, int in, int block) +{ + inode_t *inode = _pack_get_inode(db, in); + int tmp; + + if (block < 6) { + if (inode->block[block] != -1) + return inode->block[block]; + + tmp = _pack_get_free_block(db); + inode = _pack_get_inode(db, in); + return inode->block[block] = tmp; + } + + block -= 6; + int size = BLOCK_SIZE / sizeof(int32_t) - 1; + int i; + int old_block; + int32_t *blocks; + + if (inode->block[6] == -1) { + tmp = _pack_get_free_block(db); + inode = _pack_get_inode(db, in); + inode->block[6] = tmp; + blocks = _pack_get_block(db, inode->block[6]); + for (i = 0; i < size + 1; i++) + blocks[i] = -1; + } + else + blocks = _pack_get_block(db, inode->block[6]); + + old_block = inode->block[6]; + + for (;block >= size; block -= size) { + if (blocks[size] == -1) { + tmp = _pack_get_free_block(db); + blocks = _pack_get_block(db, old_block); + blocks[size] = tmp; + old_block = tmp; + blocks = _pack_get_block(db, tmp); + for (i = 0; i < size + 1; i++) + blocks[i] = -1; + } + else { + old_block = blocks[size]; + blocks = _pack_get_block(db, blocks[size]); + } + } + + if (blocks[block] == -1) { + tmp = _pack_get_free_block(db); + blocks = _pack_get_block(db, old_block); + blocks[block] = tmp; + } + + return blocks[block]; +} + +/* Returns a pointer to the package with the name pkgname. + * Returns NULL if it's not found + */ +package_t *_pack_find_package(pack_db_t *db, const char *pkgname) +{ + inode_t *inode = _pack_get_inode(db, 0); + int s_len = strlen(pkgname); + package_t *pkg; + int block = 0; + int b_pos = 0; + + pkg = _pack_get_block(db, _pack_inode_get_block(db, 0, block)); + + while (block < (inode->size / BLOCK_SIZE)) { + if (pkg->inode != 0 && + pkg->name_len == s_len && + strncmp(pkgname, pkg->name, s_len) == 0) + break; + + b_pos += pkg->rec_len; + pkg = (package_t*)((char*)pkg + pkg->rec_len); + if (b_pos >= BLOCK_SIZE) { + pkg = _pack_get_block(db, _pack_inode_get_block(db, 0, ++block)); + b_pos = 0; + } + } + + return pkg; +} + +/* Creates a new package with the name pkgname. + * Returns a pointer to the new package. + * CAUTION: THIS FUNCTION MAY RESULT IN A MMREMAP! + * that means ANY pointers into the database may be invalid + * after you've called this function. + */ +package_t *_pack_add_package(pack_db_t *db, const char *pkgname) +{ + inode_t *inode = _pack_get_inode(db, 0); + int s_len = strlen(pkgname); + int len = s_len + sizeof(package_t); + package_t *pkg; + int block = 0; + int b_pos = 0; + + pkg = _pack_get_block(db, _pack_inode_get_block(db, 0, block)); + + while (pkg && block < (inode->size / BLOCK_SIZE)) { + if (pkg->rec_len >= len && pkg->inode == 0) + break; + + b_pos += pkg->rec_len; + pkg = (package_t*)((char*)pkg + pkg->rec_len); + if (b_pos >= BLOCK_SIZE) { + pkg = _pack_get_block(db, _pack_inode_get_block(db, 0, ++block)); + b_pos = 0; + } + } + + if (pkg == NULL) { + pkg = _pack_get_block(db, _pack_inode_add_block(db, 0, block)); + pkg->rec_len = BLOCK_SIZE; + pkg->inode = 0; + inode = _pack_get_inode(db, 0); + inode->size += BLOCK_SIZE; + } + + strncpy(pkg->name, pkgname, s_len); + if (pkg->rec_len > (len + sizeof(package_t) + 10)) { + package_t *foo = (package_t*)((char*)pkg + len); + foo->rec_len = pkg->rec_len - len; + foo->inode = 0; + pkg->rec_len = len; + } + + pkg->inode = -1; + pkg->name_len = s_len; + + return pkg; +} + + +/* Reads data from the file until buf is full or we hit a newline */ +int _pack_file_gets(pack_db_t *db, pack_file_t *file, char *buf, size_t size) +{ + inode_t *inode = _pack_get_inode(db, file->inode); + size_t i; + size_t b_pos = file->pos % BLOCK_SIZE; + const char *src = _pack_get_block(db, + _pack_inode_get_block(db, file->inode, file->pos / BLOCK_SIZE)); + int newline = 0; + + for (i = 0 + ;i < size && file->pos < inode->size && !newline + ;i++, b_pos++, file->pos++) { + if (b_pos >= BLOCK_SIZE) { + src = _pack_get_block(db, + _pack_inode_get_block(db, file->inode, file->pos / BLOCK_SIZE)); + b_pos = 0; + } + + buf[i] = src[b_pos]; + + if (src[b_pos] == '\n') + newline = 1; + } + + if (i < size - 1) + buf[i] = '\0'; + + return i; +} + + +/* Write the data in buf into the file + * CAUTION: THIS FUNCTION MAY RESULT IN A MMREMAP! + * that means ANY pointers into the database may be invalid + * after you've called this function. + */ +int _pack_file_puts(pack_db_t *db, pack_file_t *file, + const char *buf, size_t size) +{ + size_t i; + size_t b_pos = file->pos % BLOCK_SIZE; + char *dest = _pack_get_block(db, + _pack_inode_add_block(db, file->inode, file->pos / BLOCK_SIZE)); + int block; + + for (i = 0; i < size; i++, file->pos++, b_pos++) + { + if (b_pos >= BLOCK_SIZE) { + block = _pack_inode_add_block(db, file->inode, file->pos / BLOCK_SIZE); + dest = _pack_get_block(db, block); + b_pos = 0; + } + dest[b_pos] = buf[i]; + } + + inode_t *inode = _pack_get_inode(db, file->inode); + inode->size = file->pos; + + return i; +} + + +int _pack_file_eof(pack_db_t *db, pack_file_t *file) +{ + inode_t *inode = _pack_get_inode(db, file->inode); + return file->pos >= inode->size; +} + + +/* Opens a file in the database + * flags: if you pass PO_READ, it will return -1 + * if the file does not exist, if you pass PO_WRITE + * the file will be created if it does not exist + * CAUTION: THIS FUNCTION MAY RESULT IN A MMREMAP! + * that means ANY pointers into the database may be invalid + * after you've called this function. + */ +pack_file_t *_pack_open_file(pack_db_t *db, const char *pkgname, + const char *filename, int flags) +{ + char name[512]; + + snprintf(name, 512, "%s/%s", pkgname, filename); + + package_t *pkg = _pack_find_package(db, name); + int w = flags & PO_WRITE; + int tmp; + + if (w && db->read_only) + return NULL; + + if (pkg == NULL && w) + pkg = _pack_add_package(db, name); + else if (pkg == NULL) + return NULL; + + pack_file_t *ret = malloc(sizeof(pack_file_t)); + ret->pos = 0; + + if (pkg->inode == -1 && w) { + tmp = _pack_get_free_inode(db); + pkg = _pack_find_package(db, name); + ret->inode = pkg->inode = tmp; + } + else + ret->inode = pkg->inode; + + return ret; +} + + +/* Closes the file given */ +void _pack_close_file(pack_file_t *file) +{ + free(file); +} + + +/* Removes a package along with all it's files */ +int _pack_package_remove(pack_db_t *db, const char *pkgname) +{ + inode_t *inode = _pack_get_inode(db, 0); + int s_len = strlen(pkgname); + package_t *prev, *pkg; + int block = 0; + int b_pos = 0; + + if (db->read_only) + return -1; + + prev = NULL; + pkg = _pack_get_block(db, _pack_inode_get_block(db, 0, block)); + + while (block < (inode->size / BLOCK_SIZE)) { + if (pkg->inode != 0 && + pkg->name_len > s_len && + strncmp(pkgname, pkg->name, s_len) == 0 && + pkg->name[s_len] == '/') { + _pack_inode_free(db, pkg->inode); + + if (prev != NULL && prev->inode == 0) { + prev->rec_len += pkg->rec_len; + pkg = prev; + } + else + pkg->inode = 0; + } + + prev = pkg; + b_pos += pkg->rec_len; + pkg = (package_t*)((char*)pkg + pkg->rec_len); + if (b_pos >= BLOCK_SIZE) { + pkg = _pack_get_block(db, _pack_inode_get_block(db, 0, ++block)); + b_pos = 0; + prev = NULL; + } + } + + return 0; +} + + +/* Gets the next entry in the directory */ +int _pack_dir_next(pack_db_t *db, pack_dir_t *dir, char *name, size_t len) +{ + inode_t *inode = _pack_get_inode(db, 0); + + if (dir->pos >= inode->size) + return -1; + + int block, b_pos; + package_t *pkg; + + do { + block = dir->pos / BLOCK_SIZE; + b_pos = dir->pos % BLOCK_SIZE; + pkg = _pack_get_block(db, _pack_inode_get_block(db, 0, block)); + if (!pkg) + return -1; + pkg = (package_t*)((char*)pkg + b_pos); + dir->pos += pkg->rec_len; + } while (pkg->inode == 0); + + strncpy(name, pkg->name, MIN(len, pkg->name_len)); + if (len > pkg->name_len) + name[pkg->name_len] = '\0'; + + return 0; +} + + +/* Opens the directory for reading */ +pack_dir_t *_pack_open_dir(pack_db_t *db) +{ + pack_dir_t *dir = malloc(sizeof(pack_dir_t)); + dir->pos = 0; + + return dir; +} + + +/* Closes the given directory */ +void _pack_close_dir(pack_dir_t *dir) +{ + free(dir); +} + + +/* Opens a packed database */ +pack_db_t *_pack_db_open(const char *filename) +{ + struct stat st; + int is_new = 0; + + pack_db_t *db = malloc(sizeof(pack_db_t)); + db->fd = open(filename, O_RDWR, 0); + db->read_only = 0; + + if (db->fd == -1) { + db->fd = open(filename, O_RDONLY, 0); + if (db->fd == -1) { + db->fd = open(filename, O_RDWR | O_CREAT, 0644); + + if (db->fd == -1) { + free(db); + printf("Could not open %s\n", filename); + return NULL; + } + + ftruncate(db->fd, GROUP_SIZE); + is_new = 1; + } + else + db->read_only = 1; + } + + fstat(db->fd, &st); + db->groups = st.st_size / GROUP_SIZE; + + int flags = PROT_READ; + if (!db->read_only) + flags |= PROT_WRITE; + + db->map = mmap(NULL, GROUP_SIZE * db->groups, flags, MAP_SHARED, + db->fd, 0); + + if (db->map == MAP_FAILED) { + perror("mmap"); + free(db); + exit(0); + } + + if (is_new) { + _pack_init_group(db->map); + db->map->i_table[0].size = 0; + } + + return db; +} + + +/* Closes a packed database */ +void _pack_db_close(pack_db_t *db) +{ + munmap(db->map, db->groups * GROUP_SIZE); + close(db->fd); + free(db); +} + +#endif /* BE_PACKED_H */ diff --git a/src/util/Makefile.am b/src/util/Makefile.am index 97a0ffa..64e18ad 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -3,7 +3,7 @@ conffile = ${sysconfdir}/pacman.conf dbpath = ${localstatedir}/lib/pacman/ cachedir = ${localstatedir}/cache/pacman/pkg/ -bin_PROGRAMS = vercmp testpkg testdb +bin_PROGRAMS = vercmp testpkg testdb convdb DEFS = -DLOCALEDIR=\"@localedir@\" \ -DCONFFILE=\"$(conffile)\" \ @@ -18,6 +18,8 @@ AM_CFLAGS = -pedantic -D_GNU_SOURCE vercmp_SOURCES = vercmp.c vercmp_LDADD = $(top_builddir)/lib/libalpm/.libs/libalpm.la +convdb_SOURCES = convdb.c + testpkg_SOURCES = testpkg.c testpkg_LDADD = $(top_builddir)/lib/libalpm/.libs/libalpm.la diff --git a/src/util/convdb.c b/src/util/convdb.c new file mode 100644 index 0000000..f1f39bd --- /dev/null +++ b/src/util/convdb.c @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2008 Sivert Berg + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/* WARNING: This file is pretty much just a quick hack thrown together + * in a hurry. It seems to work, but don't take my word for it. + */ + +#include <be_packed.h> +#include <dirent.h> +#include <archive.h> +#include <archive_entry.h> + + +/* Convert an archieved database into a packed database + * Expects a gziped database ending in .db.tar.gz + */ +void convert_archive(const char *filename) +{ + struct archive_entry *aent = NULL; + struct archive *arch = archive_read_new(); + archive_read_support_compression_gzip(arch); + archive_read_support_format_tar(arch); + if (archive_read_open_filename(arch, filename, 1) != ARCHIVE_OK) { + printf("Could not open archive %s\n", archive_error_string(arch)); + return; + } + + char *db_name = strndup(filename, strlen(filename) - strlen(".tar.gz")); + pack_db_t *db = _pack_db_open(db_name); + free(db_name); + char buffer[4096]; + size_t size; + pack_file_t *pf; + + while (!archive_read_next_header(arch, &aent)) { + const char *type = NULL; + if (strstr(archive_entry_pathname(aent), "/desc")) + type = "desc"; + else if (strstr(archive_entry_pathname(aent), "/depends")) + type = "depends"; + else if (strstr(archive_entry_pathname(aent), "/install")) + type = "install"; + else if (strstr(archive_entry_pathname(aent), "/files")) + type = "files"; + + if (type == NULL) + continue; + + char *name = strdup(archive_entry_pathname(aent)); + int i; + for (i = 0; name[i] != '/' && name[i]; i++); + name[i] = '\0'; + + pf = _pack_open_file(db, name, type, PO_WRITE); + + free(name); + + do { + size = archive_read_data(arch, buffer, 4096); + _pack_file_puts(db, pf, buffer, size); + } while (size == 4096); + + _pack_close_file(pf); + + } + + _pack_db_close(db); + archive_read_close(arch); + archive_read_finish(arch); +} + + + +const char *dir_name(const char *full_path) +{ + const char *slash = full_path; + while (*full_path) + if (*full_path++ == '/') + slash = full_path; + return slash; +} + + +/* Convert a directory database into a packed database */ +void convert_directory(const char *pathname) +{ + char db_name[1024]; + pack_db_t *db; + DIR *dir = opendir(pathname); + struct dirent *dire; + char path[1024]; + char buffer[8196]; + size_t size; + FILE *f; + pack_file_t *pf; + + strcpy(db_name, dir_name(pathname)); + strcat(db_name, ".db"); + db = _pack_db_open(db_name); + + if (dir == NULL) { + perror("opendir"); + return; + } + + dire = readdir(dir); + while (dire != NULL) { + if (strcmp(dire->d_name, ".") && strcmp(dire->d_name, "..")) { + + + size_t foo = 0; + sprintf(path, "%s/%s/desc", pathname, dire->d_name); + f = fopen(path, "r"); + if (f == NULL) + goto depends; + pf = _pack_open_file(db, dire->d_name, "desc", PO_WRITE); + do { + size = fread(buffer, 1, 8196, f); + _pack_file_puts(db, pf, buffer, size); + foo += size; + } while (size == 8196); + _pack_close_file(pf); + fclose(f); +depends: + sprintf(path, "%s/%s/depends", pathname, dire->d_name); + f = fopen(path, "r"); + if (f == NULL) + goto files; + pf = _pack_open_file(db, dire->d_name, "depends", PO_WRITE); + do { + size = fread(buffer, 1, 8196, f); + _pack_file_puts(db, pf, buffer, size); + foo += size; + } while (size == 8196); + _pack_close_file(pf); + fclose(f); +files: + sprintf(path, "%s/%s/files", pathname, dire->d_name); + f = fopen(path, "r"); + if (f == NULL) + goto install; + pf = _pack_open_file(db, dire->d_name, "files", PO_WRITE); + do { + size = fread(buffer, 1, 8196, f); + _pack_file_puts(db, pf, buffer, size); + foo += size; + } while (size == 8196); + _pack_close_file(pf); + fclose(f); +install: + sprintf(path, "%s/%s/install", pathname, dire->d_name); + f = fopen(path, "r"); + if (f == NULL) + goto changelog; + pf = _pack_open_file(db, dire->d_name, "install", PO_WRITE); + do { + size = fread(buffer, 1, 8196, f); + _pack_file_puts(db, pf, buffer, size); + foo += size; + } while (size == 8196); + _pack_close_file(pf); + fclose(f); +changelog: + sprintf(path, "%s/%s/changelog", pathname, dire->d_name); + f = fopen(path, "r"); + if (f == NULL) + goto deltas; + pf = _pack_open_file(db, dire->d_name, "changelog", PO_WRITE); + do { + size = fread(buffer, 1, 8196, f); + _pack_file_puts(db, pf, buffer, size); + foo += size; + } while (size == 8196); + _pack_close_file(pf); + fclose(f); +deltas: + sprintf(path, "%s/%s/deltas", pathname, dire->d_name); + f = fopen(path, "r"); + if (f == NULL) + goto done; + pf = _pack_open_file(db, dire->d_name, "deltas", PO_WRITE); + do { + size = fread(buffer, 1, 8196, f); + _pack_file_puts(db, pf, buffer, size); + foo += size; + } while (size == 8196); + _pack_close_file(pf); + fclose(f); +done: + pf = NULL; + } + + dire = readdir(dir); + } + + closedir(dir); + _pack_db_close(db); +} + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + printf("Usage: %s <dir/file>\n", argv[0]); + exit(0); + } + + if (strstr(argv[1], ".tar.gz") != NULL) + convert_archive(argv[1]); + else + convert_directory(argv[1]); + + return 0; +} -- 1.6.0.5