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(a)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