[pacman-dev] [PATCH 1/2] Added a new database backend

Sivert Berg sivertb at stud.ntnu.no
Mon Dec 15 04:20:54 EST 2008


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 at 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



More information about the pacman-dev mailing list