From d0edeec8fb8fa5abee8b3065cea5e4882d0c51c4 Mon Sep 17 00:00:00 2001
From: Timo Teräs <timo.teras@iki.fi>
Date: Tue, 19 May 2020 11:39:21 +0300
Subject: make the atom functions not use global state

This greatly helps with memory management on applications that
may want to daemonize and open/close database several times.

Also the lifetime and "owner" of memory for all data is now
explicitly bound to owning struct apk_database, which might
be helpful when writing language bindings. As side effect, the
interned "atoms" are unique only within what apk_database, so
comparing packages from different apk_database may not work
as expected.

Fixes #10697
---
 src/Makefile       |  2 +-
 src/apk.c          |  1 -
 src/apk_atom.h     | 33 ++++++++++++++++++++++++
 src/apk_blob.h     |  5 ----
 src/apk_database.h |  2 ++
 src/apk_io.h       |  3 ++-
 src/app_add.c      |  6 ++---
 src/app_audit.c    |  4 +--
 src/app_fetch.c    |  4 +--
 src/app_index.c    | 27 ++++++++++---------
 src/app_info.c     |  2 +-
 src/app_list.c     | 30 ++++++++-------------
 src/app_stats.c    |  4 +--
 src/app_upgrade.c  |  2 +-
 src/app_version.c  |  2 +-
 src/atom.c         | 64 +++++++++++++++++++++++++++++++++++++++++++++
 src/blob.c         | 76 ------------------------------------------------------
 src/commit.c       |  4 +--
 src/database.c     | 36 ++++++++++++++------------
 src/hash.c         |  3 ++-
 src/io.c           | 11 +++-----
 src/lua-apk.c      |  1 -
 src/package.c      | 18 ++++++-------
 src/solver.c       | 24 ++++++++---------
 24 files changed, 185 insertions(+), 179 deletions(-)
 create mode 100644 src/apk_atom.h
 create mode 100644 src/atom.c

(limited to 'src')

diff --git a/src/Makefile b/src/Makefile
index 4a055ce..3bbd51e 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -18,7 +18,7 @@ ZLIB_LIBS		:= $(shell $(PKG_CONFIG) --libs zlib)
 # Dynamic library
 libapk.so.$(VERSION)-objs := \
 	common.o database.o package.o commit.o solver.o \
-	version.o blob.o hash.o print.o \
+	version.o atom.o blob.o hash.o print.o \
 	io.o io_url.o io_gunzip.o io_archive.o
 
 libapk.so.$(VERSION)-libs := libfetch/libfetch.a
diff --git a/src/apk.c b/src/apk.c
index 8fbc3e8..79aef3e 100644
--- a/src/apk.c
+++ b/src/apk.c
@@ -518,7 +518,6 @@ int main(int argc, char **argv)
 
 	memset(&dbopts, 0, sizeof(dbopts));
 	list_init(&dbopts.repository_list);
-	apk_atom_init();
 	umask(0);
 	setup_terminal();
 
diff --git a/src/apk_atom.h b/src/apk_atom.h
new file mode 100644
index 0000000..1b6ad6c
--- /dev/null
+++ b/src/apk_atom.h
@@ -0,0 +1,33 @@
+/* apk_atom.h - Alpine Package Keeper (APK)
+ *
+ * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
+ * Copyright (C) 2008-2020 Timo Teräs <timo.teras@iki.fi>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#ifndef APK_ATOM_H
+#define APK_ATOM_H
+
+#include "apk_hash.h"
+#include "apk_blob.h"
+
+extern apk_blob_t apk_atom_null;
+
+struct apk_atom_pool {
+	struct apk_hash hash;
+};
+
+void apk_atom_init(struct apk_atom_pool *);
+void apk_atom_free(struct apk_atom_pool *);
+apk_blob_t *apk_atom_get(struct apk_atom_pool *atoms, apk_blob_t blob, int duplicate);
+
+static inline apk_blob_t *apk_atomize(struct apk_atom_pool *atoms, apk_blob_t blob) {
+	return apk_atom_get(atoms, blob, 0);
+}
+static inline apk_blob_t *apk_atomize_dup(struct apk_atom_pool *atoms, apk_blob_t blob) {
+	return apk_atom_get(atoms, blob, 1);
+}
+
+#endif
diff --git a/src/apk_blob.h b/src/apk_blob.h
index 84bd143..008666e 100644
--- a/src/apk_blob.h
+++ b/src/apk_blob.h
@@ -25,7 +25,6 @@ struct apk_blob {
 };
 typedef struct apk_blob apk_blob_t;
 typedef int (*apk_blob_cb)(void *ctx, apk_blob_t blob);
-extern apk_blob_t apk_null_blob;
 
 #define BLOB_FMT		"%.*s"
 #define BLOB_PRINTF(b)		(int)(b).len, (b).ptr
@@ -130,10 +129,6 @@ void apk_blob_pull_base64(apk_blob_t *b, apk_blob_t to);
 void apk_blob_pull_hexdump(apk_blob_t *b, apk_blob_t to);
 int apk_blob_pull_blob_match(apk_blob_t *b, apk_blob_t match);
 
-void apk_atom_init(void);
-apk_blob_t *apk_blob_atomize(apk_blob_t blob);
-apk_blob_t *apk_blob_atomize_dup(apk_blob_t blob);
-
 #if defined(__GLIBC__) && !defined(__UCLIBC__)
 extern size_t strlcpy(char *dest, const char *src, size_t size);
 #endif
diff --git a/src/apk_database.h b/src/apk_database.h
index dc6525f..3488031 100644
--- a/src/apk_database.h
+++ b/src/apk_database.h
@@ -12,6 +12,7 @@
 
 #include "apk_version.h"
 #include "apk_hash.h"
+#include "apk_atom.h"
 #include "apk_archive.h"
 #include "apk_package.h"
 #include "apk_io.h"
@@ -166,6 +167,7 @@ struct apk_database {
 	struct apk_repository repos[APK_MAX_REPOS];
 	struct apk_repository_tag repo_tags[APK_MAX_TAGS];
 	struct apk_id_cache id_cache;
+	struct apk_atom_pool atoms;
 
 	struct {
 		struct apk_hash names;
diff --git a/src/apk_io.h b/src/apk_io.h
index e4759b0..18b8ecc 100644
--- a/src/apk_io.h
+++ b/src/apk_io.h
@@ -16,6 +16,7 @@
 #include "apk_defines.h"
 #include "apk_blob.h"
 #include "apk_hash.h"
+#include "apk_atom.h"
 
 struct apk_id_cache {
 	int root_fd;
@@ -167,7 +168,7 @@ int apk_blob_to_file(int atfd, const char *file, apk_blob_t b, unsigned int flag
 #define APK_FI_XATTR_CSUM(x)	(((x) & 0xff) << 8)
 #define APK_FI_CSUM(x)		(((x) & 0xff))
 int apk_fileinfo_get(int atfd, const char *filename, unsigned int flags,
-		     struct apk_file_info *fi);
+		     struct apk_file_info *fi, struct apk_atom_pool *atoms);
 void apk_fileinfo_hash_xattr(struct apk_file_info *fi);
 void apk_fileinfo_free(struct apk_file_info *fi);
 
diff --git a/src/app_add.c b/src/app_add.c
index fb6e0e0..1f6c875 100644
--- a/src/app_add.c
+++ b/src/app_add.c
@@ -100,9 +100,9 @@ static struct apk_package *create_virtual_package(struct apk_database *db, struc
 	if (virtpkg == NULL) return 0;
 
 	virtpkg->name = name;
-	virtpkg->version = apk_blob_atomize_dup(APK_BLOB_STR(ver));
+	virtpkg->version = apk_atomize_dup(&db->atoms, APK_BLOB_STR(ver));
 	virtpkg->description = strdup("virtual meta package");
-	virtpkg->arch = apk_blob_atomize(APK_BLOB_STR("noarch"));
+	virtpkg->arch = apk_atomize(&db->atoms, APK_BLOB_STR("noarch"));
 
 	mdctx = EVP_MD_CTX_new();
 	EVP_DigestInit_ex(mdctx, apk_checksum_default(), NULL);
@@ -135,7 +135,7 @@ static int add_main(void *ctx, struct apk_database *db, struct apk_string_array
 		apk_blob_pull_dep(&b, db, &virtdep);
 		if (APK_BLOB_IS_NULL(b) || virtdep.conflict ||
 		    virtdep.result_mask != APK_DEPMASK_ANY ||
-		    virtdep.version != &apk_null_blob) {
+		    virtdep.version != &apk_atom_null) {
 			apk_error("%s: bad package specifier");
 			return -1;
 		}
diff --git a/src/app_audit.c b/src/app_audit.c
index fb2a0ee..011a26c 100644
--- a/src/app_audit.c
+++ b/src/app_audit.c
@@ -107,7 +107,7 @@ static int audit_file(struct audit_ctx *actx,
 				APK_FI_NOFOLLOW |
 				APK_FI_XATTR_CSUM(dbf->acl->xattr_csum.type ?: APK_CHECKSUM_DEFAULT) |
 				APK_FI_CSUM(dbf->csum.type),
-				&fi) != 0)
+				&fi, &db->atoms) != 0)
 		return -EPERM;
 
 	if (dbf->csum.type != APK_CHECKSUM_NONE &&
@@ -183,7 +183,7 @@ static int audit_directory_tree_item(void *ctx, int dirfd, const char *name)
 	int reason = 0;
 
 	if (bdir.len + bent.len + 1 >= sizeof(atctx->path)) return 0;
-	if (apk_fileinfo_get(dirfd, name, APK_FI_NOFOLLOW, &fi) < 0) return 0;
+	if (apk_fileinfo_get(dirfd, name, APK_FI_NOFOLLOW, &fi, &db->atoms) < 0) return 0;
 
 	memcpy(&atctx->path[atctx->pathlen], bent.ptr, bent.len);
 	atctx->pathlen += bent.len;
diff --git a/src/app_fetch.c b/src/app_fetch.c
index 8e73c0e..e6f4fbe 100644
--- a/src/app_fetch.c
+++ b/src/app_fetch.c
@@ -145,7 +145,7 @@ static int fetch_package(apk_hash_item item, void *pctx)
 	}
 
 	if (!(ctx->flags & FETCH_STDOUT)) {
-		if (apk_fileinfo_get(ctx->outdir_fd, filename, APK_CHECKSUM_NONE, &fi) == 0 &&
+		if (apk_fileinfo_get(ctx->outdir_fd, filename, APK_CHECKSUM_NONE, &fi, &db->atoms) == 0 &&
 		    fi.size == pkg->size)
 			return 0;
 	}
@@ -227,7 +227,7 @@ static void mark_name_flags(struct apk_database *db, const char *match, struct a
 	struct fetch_ctx *ctx = (struct fetch_ctx *) pctx;
 	struct apk_dependency dep = (struct apk_dependency) {
 		.name = name,
-		.version = &apk_null_blob,
+		.version = &apk_atom_null,
 		.result_mask = APK_DEPMASK_ANY,
 	};
 
diff --git a/src/app_index.c b/src/app_index.c
index 4c0b0d1..53e53b0 100644
--- a/src/app_index.c
+++ b/src/app_index.c
@@ -27,7 +27,7 @@ struct index_ctx {
 	const char *index;
 	const char *output;
 	const char *description;
-	apk_blob_t *rewrite_arch;
+	const char *rewrite_arch;
 	time_t index_mtime;
 	int method;
 	unsigned short index_flags;
@@ -64,7 +64,7 @@ static int option_parse_applet(void *ctx, struct apk_db_options *dbopts, int opt
 		ictx->output = optarg;
 		break;
 	case OPT_INDEX_rewrite_arch:
-		ictx->rewrite_arch = apk_blob_atomize(APK_BLOB_STR(optarg));
+		ictx->rewrite_arch = optarg;
 		break;
 	case OPT_INDEX_no_warnings:
 		ictx->index_flags |= APK_INDEXF_NO_WARNINGS;
@@ -86,7 +86,7 @@ static int index_read_file(struct apk_database *db, struct index_ctx *ictx)
 
 	if (ictx->index == NULL)
 		return 0;
-	if (apk_fileinfo_get(AT_FDCWD, ictx->index, APK_CHECKSUM_NONE, &fi) < 0)
+	if (apk_fileinfo_get(AT_FDCWD, ictx->index, APK_CHECKSUM_NONE, &fi, &db->atoms) < 0)
 		return 0;
 
 	ictx->index_mtime = fi.mtime;
@@ -121,6 +121,7 @@ static int index_main(void *ctx, struct apk_database *db, struct apk_string_arra
 	struct index_ctx *ictx = (struct index_ctx *) ctx;
 	struct apk_package *pkg;
 	char **parg;
+	apk_blob_t *rewrite_arch = NULL;
 
 	if (isatty(STDOUT_FILENO) && ictx->output == NULL &&
 	    !(apk_force & APK_FORCE_BINARY_STDOUT)) {
@@ -137,8 +138,11 @@ static int index_main(void *ctx, struct apk_database *db, struct apk_string_arra
 		return r;
 	}
 
+	if (ictx->rewrite_arch)
+		rewrite_arch = apk_atomize(&db->atoms, APK_BLOB_STR(ictx->rewrite_arch));
+
 	foreach_array_item(parg, args) {
-		if (apk_fileinfo_get(AT_FDCWD, *parg, APK_CHECKSUM_NONE, &fi) < 0) {
+		if (apk_fileinfo_get(AT_FDCWD, *parg, APK_CHECKSUM_NONE, &fi, &db->atoms) < 0) {
 			apk_warning("File '%s' is unaccessible", *parg);
 			continue;
 		}
@@ -174,15 +178,11 @@ static int index_main(void *ctx, struct apk_database *db, struct apk_string_arra
 
 			foreach_array_item(p, name->providers) {
 				pkg = p->pkg;
-				if (pkg->name != name)
-					continue;
-				if (apk_blob_compare(bver, *pkg->version) != 0)
-					continue;
-				if (pkg->size != fi.size)
-					continue;
+				if (pkg->name != name) continue;
+				if (apk_blob_compare(bver, *pkg->version) != 0) continue;
+				if (pkg->size != fi.size) continue;
 				pkg->filename = strdup(*parg);
-				if (ictx->rewrite_arch != NULL)
-					pkg->arch = ictx->rewrite_arch;
+				if (rewrite_arch) pkg->arch = rewrite_arch;
 				found = TRUE;
 				break;
 			}
@@ -197,8 +197,7 @@ static int index_main(void *ctx, struct apk_database *db, struct apk_string_arra
 				errors++;
 			} else {
 				newpkgs++;
-				if (ictx->rewrite_arch != NULL)
-					pkg->arch = ictx->rewrite_arch;
+				if (rewrite_arch) pkg->arch = rewrite_arch;
 			}
 			apk_sign_ctx_free(&sctx);
 		}
diff --git a/src/app_info.c b/src/app_info.c
index 41d6a99..b444b80 100644
--- a/src/app_info.c
+++ b/src/app_info.c
@@ -125,7 +125,7 @@ static void info_who_owns(struct info_ctx *ctx, struct apk_database *db,
 		if (apk_verbosity < 1) {
 			dep = (struct apk_dependency) {
 				.name = pkg->name,
-				.version = apk_blob_atomize(APK_BLOB_NULL),
+				.version = &apk_atom_null,
 				.result_mask = APK_DEPMASK_ANY,
 			};
 			apk_deps_add(&deps, &dep);
diff --git a/src/app_list.c b/src/app_list.c
index ab5c846..24d1a88 100644
--- a/src/app_list.c
+++ b/src/app_list.c
@@ -67,33 +67,25 @@ static const struct apk_package *is_upgradable(struct apk_name *name, const stru
 {
 	struct apk_provider *p;
 	struct apk_package *ipkg;
-	apk_blob_t *latest = apk_blob_atomize(APK_BLOB_STR(""));
+	apk_blob_t no_version = APK_BLOB_STR("");
+	apk_blob_t *latest = &no_version;
+	int r;
 
-	if (name == NULL)
-		return NULL;
+	if (!name) return NULL;
 
 	ipkg = apk_pkg_get_installed(name);
-	if (ipkg == NULL)
-		return NULL;
+	if (!ipkg) return NULL;
 
-	if (pkg0 == NULL)
-	{
-		foreach_array_item(p, name->providers)
-		{
+	if (!pkg0) {
+		foreach_array_item(p, name->providers) {
 			pkg0 = p->pkg;
-			int r;
-
-			if (pkg0 == ipkg)
-				continue;
-
+			if (pkg0 == ipkg) continue;
 			r = apk_version_compare_blob(*pkg0->version, *latest);
-			if (r == APK_VERSION_GREATER)
-				latest = pkg0->version;
+			if (r == APK_VERSION_GREATER) latest = pkg0->version;
 		}
-	}
-	else
+	} else {
 		latest = pkg0->version;
-
+	}
 	return apk_version_compare_blob(*ipkg->version, *latest) == APK_VERSION_LESS ? ipkg : NULL;
 }
 
diff --git a/src/app_stats.c b/src/app_stats.c
index 583e4c6..7e60494 100644
--- a/src/app_stats.c
+++ b/src/app_stats.c
@@ -24,8 +24,6 @@ static int list_count(struct list_head *h)
 
 static int stats_main(void *ctx, struct apk_database *db, struct apk_string_array *args)
 {
-	extern struct apk_hash atom_hash;
-
 	printf(
 		"installed:\n"
 		"  packages: %d\n"
@@ -46,7 +44,7 @@ static int stats_main(void *ctx, struct apk_database *db, struct apk_string_arra
 		list_count(&db->installed.triggers),
 		db->available.names.num_items,
 		db->available.packages.num_items,
-		atom_hash.num_items
+		db->atoms.hash.num_items
 		);
 	return 0;
 }
diff --git a/src/app_upgrade.c b/src/app_upgrade.c
index 0c8d4dd..7e5347f 100644
--- a/src/app_upgrade.c
+++ b/src/app_upgrade.c
@@ -174,7 +174,7 @@ static int upgrade_main(void *ctx, struct apk_database *db, struct apk_string_ar
 		foreach_array_item(dep, world) {
 			if (dep->result_mask == APK_DEPMASK_CHECKSUM) {
 				dep->result_mask = APK_DEPMASK_ANY;
-				dep->version = apk_blob_atomize(APK_BLOB_NULL);
+				dep->version = &apk_atom_null;
 			}
 		}
 	} else {
diff --git a/src/app_version.c b/src/app_version.c
index df2506c..2dc3bcd 100644
--- a/src/app_version.c
+++ b/src/app_version.c
@@ -121,7 +121,7 @@ static void ver_print_package_status(struct apk_database *db, const char *match,
 	struct apk_provider *p0;
 	char pkgname[41];
 	const char *opstr;
-	apk_blob_t *latest = apk_blob_atomize(APK_BLOB_STR(""));
+	apk_blob_t *latest = apk_atomize(&db->atoms, APK_BLOB_STR(""));
 	unsigned int latest_repos = 0;
 	int i, r = -1;
 	unsigned short tag, allowed_repos;
diff --git a/src/atom.c b/src/atom.c
new file mode 100644
index 0000000..e2ff83e
--- /dev/null
+++ b/src/atom.c
@@ -0,0 +1,64 @@
+/* apk_atom.c - Alpine Package Keeper (APK)
+ *
+ * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
+ * Copyright (C) 2008-2020 Timo Teräs <timo.teras@iki.fi>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#include "apk_atom.h"
+
+apk_blob_t apk_atom_null = APK_BLOB_NULL;
+
+struct apk_atom_hashnode {
+	struct hlist_node hash_node;
+	apk_blob_t blob;
+};
+
+static apk_blob_t atom_hash_get_key(apk_hash_item item)
+{
+	return ((struct apk_atom_hashnode *) item)->blob;
+}
+
+static struct apk_hash_ops atom_ops = {
+	.node_offset = offsetof(struct apk_atom_hashnode, hash_node),
+	.get_key = atom_hash_get_key,
+	.hash_key = apk_blob_hash,
+	.compare = apk_blob_compare,
+	.delete_item = (apk_hash_delete_f) free,
+};
+
+void apk_atom_init(struct apk_atom_pool *atoms)
+{
+	apk_hash_init(&atoms->hash, &atom_ops, 10000);
+}
+
+void apk_atom_free(struct apk_atom_pool *atoms)
+{
+	apk_hash_free(&atoms->hash);
+}
+
+apk_blob_t *apk_atom_get(struct apk_atom_pool *atoms, apk_blob_t blob, int duplicate)
+{
+	struct apk_atom_hashnode *atom;
+	unsigned long hash = apk_hash_from_key(&atoms->hash, blob);
+
+	if (blob.len < 0 || !blob.ptr) return &apk_atom_null;
+
+	atom = (struct apk_atom_hashnode *) apk_hash_get_hashed(&atoms->hash, blob, hash);
+	if (atom) return &atom->blob;
+
+	if (duplicate) {
+		char *ptr;
+		atom = malloc(sizeof(*atom) + blob.len);
+		ptr = (char*) (atom + 1);
+		memcpy(ptr, blob.ptr, blob.len);
+		atom->blob = APK_BLOB_PTR_LEN(ptr, blob.len);
+	} else {
+		atom = malloc(sizeof(*atom));
+		atom->blob = blob;
+	}
+	apk_hash_insert_hashed(&atoms->hash, atom, hash);
+	return &atom->blob;
+}
diff --git a/src/blob.c b/src/blob.c
index 72b9fd6..1a63686 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -15,11 +15,6 @@
 #include "apk_blob.h"
 #include "apk_hash.h"
 
-struct apk_blob_atom {
-	struct hlist_node hash_node;
-	apk_blob_t blob;
-};
-
 char *apk_blob_cstr(apk_blob_t blob)
 {
 	char *cstr;
@@ -640,77 +635,6 @@ err:
 	*b = APK_BLOB_NULL;
 }
 
-static apk_blob_t atom_hash_get_key(apk_hash_item item)
-{
-	return ((struct apk_blob_atom *) item)->blob;
-}
-
-struct apk_hash atom_hash;
-static struct apk_hash_ops atom_ops = {
-	.node_offset = offsetof(struct apk_blob_atom, hash_node),
-	.get_key = atom_hash_get_key,
-	.hash_key = apk_blob_hash,
-	.compare = apk_blob_compare,
-	.delete_item = (apk_hash_delete_f) free,
-};
-
-apk_blob_t apk_null_blob = {0,0};
-
-#ifdef VALGRIND
-static void apk_atom_fini(void)
-{
-	apk_hash_free(&atom_hash);
-}
-#endif
-
-void apk_atom_init(void)
-{
-#ifdef VALGRIND
-	atexit(apk_atom_fini);
-#endif
-	apk_hash_init(&atom_hash, &atom_ops, 10000);
-}
-
-apk_blob_t *apk_blob_atomize(apk_blob_t blob)
-{
-	struct apk_blob_atom *atom;
-	unsigned long hash = apk_hash_from_key(&atom_hash, blob);
-
-	if (blob.len < 0 || blob.ptr == NULL)
-		return &apk_null_blob;
-
-	atom = (struct apk_blob_atom *) apk_hash_get_hashed(&atom_hash, blob, hash);
-	if (atom != NULL)
-		return &atom->blob;
-
-	atom = malloc(sizeof(*atom));
-	atom->blob = blob;
-	apk_hash_insert_hashed(&atom_hash, atom, hash);
-
-	return &atom->blob;
-}
-
-apk_blob_t *apk_blob_atomize_dup(apk_blob_t blob)
-{
-	struct apk_blob_atom *atom;
-	unsigned long hash = apk_hash_from_key(&atom_hash, blob);
-	char *ptr;
-
-	if (blob.len < 0 || blob.ptr == NULL)
-		return &apk_null_blob;
-	atom = (struct apk_blob_atom *) apk_hash_get_hashed(&atom_hash, blob, hash);
-	if (atom != NULL)
-		return &atom->blob;
-
-	atom = malloc(sizeof(*atom) + blob.len);
-	ptr = (char*) (atom + 1);
-	memcpy(ptr, blob.ptr, blob.len);
-	atom->blob = APK_BLOB_PTR_LEN(ptr, blob.len);
-	apk_hash_insert_hashed(&atom_hash, atom, hash);
-
-	return &atom->blob;
-}
-
 #if defined(__GLIBC__) && !defined(__UCLIBC__)
 size_t strlcpy(char *dst, const char *src, size_t size)
 {
diff --git a/src/commit.c b/src/commit.c
index 8c337e3..b82607c 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -453,8 +453,8 @@ static void print_conflicts(struct print_state *ps, struct apk_package *pkg)
 		foreach_array_item(p, d->name->providers) {
 			if (!p->pkg->marked)
 				continue;
-			if (d->version == &apk_null_blob &&
-			    p->version == &apk_null_blob)
+			if (d->version == &apk_atom_null &&
+			    p->version == &apk_atom_null)
 				continue;
 			if (once && p->pkg == pkg &&
 			    p->version == d->version) {
diff --git a/src/database.c b/src/database.c
index 65d07e2..d113d6a 100644
--- a/src/database.c
+++ b/src/database.c
@@ -225,7 +225,7 @@ struct apk_name *apk_db_get_name(struct apk_database *db, apk_blob_t name)
 	return pn;
 }
 
-static struct apk_db_acl *apk_db_acl_atomize(mode_t mode, uid_t uid, gid_t gid, const struct apk_checksum *xattr_csum)
+static struct apk_db_acl *apk_db_acl_atomize(struct apk_database *db, mode_t mode, uid_t uid, gid_t gid, const struct apk_checksum *xattr_csum)
 {
 	struct apk_db_acl acl = { .mode = mode & 07777, .uid = uid, .gid = gid };
 	apk_blob_t *b;
@@ -233,7 +233,7 @@ static struct apk_db_acl *apk_db_acl_atomize(mode_t mode, uid_t uid, gid_t gid,
 	if (xattr_csum && xattr_csum->type != APK_CHECKSUM_NONE)
 		acl.xattr_csum = *xattr_csum;
 
-	b = apk_blob_atomize_dup(APK_BLOB_STRUCT(acl));
+	b = apk_atomize_dup(&db->atoms, APK_BLOB_STRUCT(acl));
 	return (struct apk_db_acl *) b->ptr;
 }
 
@@ -518,8 +518,7 @@ struct apk_package *apk_db_pkg_add(struct apk_database *db, struct apk_package *
 	struct apk_package *idb;
 	struct apk_dependency *dep;
 
-	if (pkg->license == NULL)
-		pkg->license = apk_blob_atomize(APK_BLOB_NULL);
+	if (!pkg->license) pkg->license = &apk_atom_null;
 
 	/* Set as "cached" if installing from specified file, and
 	 * for virtual packages */
@@ -848,7 +847,7 @@ int apk_db_index_read(struct apk_database *db, struct apk_istream *is, int repo)
 			else
 				xattr_csum.type = APK_CHECKSUM_NONE;
 
-			acl = apk_db_acl_atomize(mode, uid, gid, &xattr_csum);
+			acl = apk_db_acl_atomize(db, mode, uid, gid, &xattr_csum);
 			if (field == 'M')
 				diri->acl = acl;
 			else
@@ -1501,6 +1500,7 @@ void apk_db_init(struct apk_database *db)
 	apk_hash_init(&db->available.packages, &pkg_info_hash_ops, 10000);
 	apk_hash_init(&db->installed.dirs, &dir_hash_ops, 20000);
 	apk_hash_init(&db->installed.files, &file_hash_ops, 200000);
+	apk_atom_init(&db->atoms);
 	list_init(&db->installed.packages);
 	list_init(&db->installed.triggers);
 	apk_dependency_array_init(&db->world);
@@ -1517,8 +1517,8 @@ int apk_db_open(struct apk_database *db, struct apk_db_options *dbopts)
 	apk_blob_t blob;
 	int r, fd, write_arch = FALSE;
 
-	apk_default_acl_dir = apk_db_acl_atomize(0755, 0, 0, NULL);
-	apk_default_acl_file = apk_db_acl_atomize(0644, 0, 0, NULL);
+	apk_default_acl_dir = apk_db_acl_atomize(db, 0755, 0, 0, NULL);
+	apk_default_acl_file = apk_db_acl_atomize(db, 0644, 0, 0, NULL);
 
 	if (apk_flags & APK_SIMULATE) {
 		dbopts->open_flags &= ~(APK_OPENF_CREATE | APK_OPENF_WRITE);
@@ -1553,16 +1553,16 @@ int apk_db_open(struct apk_database *db, struct apk_db_options *dbopts)
 		db->permanent = 0;
 
 	if (dbopts->root && dbopts->arch) {
-		db->arch = apk_blob_atomize(APK_BLOB_STR(dbopts->arch));
+		db->arch = apk_atomize(&db->atoms, APK_BLOB_STR(dbopts->arch));
 		write_arch = TRUE;
 	} else {
 		apk_blob_t arch;
 		arch = apk_blob_from_file(db->root_fd, apk_arch_file);
 		if (!APK_BLOB_IS_NULL(arch)) {
-			db->arch = apk_blob_atomize_dup(apk_blob_trim(arch));
+			db->arch = apk_atomize_dup(&db->atoms, apk_blob_trim(arch));
 			free(arch.ptr);
 		} else {
-			db->arch = apk_blob_atomize(APK_BLOB_STR(APK_DEFAULT_ARCH));
+			db->arch = apk_atomize(&db->atoms, APK_BLOB_STR(APK_DEFAULT_ARCH));
 			write_arch = TRUE;
 		}
 	}
@@ -1820,6 +1820,7 @@ void apk_db_close(struct apk_database *db)
 	apk_hash_free(&db->available.names);
 	apk_hash_free(&db->installed.files);
 	apk_hash_free(&db->installed.dirs);
+	apk_atom_free(&db->atoms);
 
 	if (db->root_proc_dir) {
 		umount2(db->root_proc_dir, MNT_DETACH|UMOUNT_NOFOLLOW);
@@ -1867,12 +1868,12 @@ int apk_db_get_tag_id(struct apk_database *db, apk_blob_t tag)
 	db->num_repo_tags++;
 
 	if (tag.ptr[0] == '@') {
-		db->repo_tags[i].tag = *apk_blob_atomize_dup(tag);
+		db->repo_tags[i].tag = *apk_atomize_dup(&db->atoms, tag);
 	} else {
 		char *tmp = alloca(tag.len + 1);
 		tmp[0] = '@';
 		memcpy(&tmp[1], tag.ptr, tag.len);
-		db->repo_tags[i].tag = *apk_blob_atomize_dup(APK_BLOB_PTR_LEN(tmp, tag.len+1));
+		db->repo_tags[i].tag = *apk_atomize_dup(&db->atoms, APK_BLOB_PTR_LEN(tmp, tag.len+1));
 	}
 
 	db->repo_tags[i].plain_name = db->repo_tags[i].tag;
@@ -2551,7 +2552,7 @@ static int apk_db_install_archive_entry(void *_ctx,
 			apk_message("%s", ae->name);
 
 		/* Extract the file with temporary name */
-		file->acl = apk_db_acl_atomize(ae->mode, ae->uid, ae->gid, &ae->xattr_csum);
+		file->acl = apk_db_acl_atomize(db, ae->mode, ae->uid, ae->gid, &ae->xattr_csum);
 		r = apk_archive_entry_extract(
 				db->root_fd, ae,
 				format_tmpname(pkg, file, tmpname_file),
@@ -2592,7 +2593,7 @@ static int apk_db_install_archive_entry(void *_ctx,
 			diri = apk_db_install_directory_entry(ctx, name);
 			apk_db_dir_prepare(db, diri->dir, ae->mode);
 		}
-		apk_db_diri_set(diri, apk_db_acl_atomize(ae->mode, ae->uid, ae->gid, &ae->xattr_csum));
+		apk_db_diri_set(diri, apk_db_acl_atomize(db, ae->mode, ae->uid, ae->gid, &ae->xattr_csum));
 	}
 	ctx->installed_size += ctx->current_file_size;
 
@@ -2628,7 +2629,7 @@ static void apk_db_purge_pkg(struct apk_database *db,
 			if ((diri->dir->protect_mode == APK_PROTECT_NONE) ||
 			    (apk_flags & APK_PURGE) ||
 			    (file->csum.type != APK_CHECKSUM_NONE &&
-			     apk_fileinfo_get(db->root_fd, name, APK_FI_NOFOLLOW | file->csum.type, &fi) == 0 &&
+			     apk_fileinfo_get(db->root_fd, name, APK_FI_NOFOLLOW | file->csum.type, &fi, &db->atoms) == 0 &&
 			     apk_checksum_compare(&file->csum, &fi.csum) == 0))
 				unlinkat(db->root_fd, name, 0);
 			if (apk_verbosity >= 3)
@@ -2684,7 +2685,7 @@ static void apk_db_migrate_files(struct apk_database *db,
 				cstype = ofile->csum.type;
 			cstype |= APK_FI_NOFOLLOW;
 
-			r = apk_fileinfo_get(db->root_fd, name, cstype, &fi);
+			r = apk_fileinfo_get(db->root_fd, name, cstype, &fi, &db->atoms);
 			if (ofile && ofile->diri->pkg->name == NULL) {
 				/* File was from overlay, delete the
 				 * packages version */
@@ -2702,7 +2703,8 @@ static void apk_db_migrate_files(struct apk_database *db,
 				if (ofile == NULL ||
 				    ofile->csum.type != file->csum.type)
 					apk_fileinfo_get(db->root_fd, name,
-						APK_FI_NOFOLLOW | file->csum.type, &fi);
+						APK_FI_NOFOLLOW | file->csum.type,
+						&fi, &db->atoms);
 				if ((apk_flags & APK_CLEAN_PROTECTED) ||
 				    (file->csum.type != APK_CHECKSUM_NONE &&
 				     apk_checksum_compare(&file->csum, &fi.csum) == 0)) {
diff --git a/src/hash.c b/src/hash.c
index 28301d4..6835ddf 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -93,6 +93,7 @@ void apk_hash_delete_hashed(struct apk_hash *h, apk_blob_t key, unsigned long ha
 			if (h->ops->compare_item(item, key) == 0) {
 				hlist_del(pos, &h->buckets->item[hash]);
 				h->ops->delete_item(item);
+				h->num_items--;
 				break;
 			}
 		}
@@ -103,9 +104,9 @@ void apk_hash_delete_hashed(struct apk_hash *h, apk_blob_t key, unsigned long ha
 			if (h->ops->compare(key, itemkey) == 0) {
 				hlist_del(pos, &h->buckets->item[hash]);
 				h->ops->delete_item(item);
+				h->num_items--;
 				break;
 			}
 		}
 	}
 }
-
diff --git a/src/io.c b/src/io.c
index 92f1a32..9643218 100644
--- a/src/io.c
+++ b/src/io.c
@@ -692,7 +692,7 @@ void apk_fileinfo_hash_xattr(struct apk_file_info *fi)
 }
 
 int apk_fileinfo_get(int atfd, const char *filename, unsigned int flags,
-		     struct apk_file_info *fi)
+		     struct apk_file_info *fi, struct apk_atom_pool *atoms)
 {
 	struct stat64 st;
 	unsigned int checksum = flags & 0xff;
@@ -735,7 +735,7 @@ int apk_fileinfo_get(int atfd, const char *filename, unsigned int flags,
 					}
 					*apk_xattr_array_add(&xattrs) = (struct apk_xattr) {
 						.name = &buf[i],
-						.value = *apk_blob_atomize_dup(APK_BLOB_PTR_LEN(val, vlen)),
+						.value = *apk_atomize_dup(atoms, APK_BLOB_PTR_LEN(val, vlen)),
 					};
 				}
 				apk_fileinfo_hash_xattr_array(xattrs, apk_checksum_evp(xattr_checksum), &fi->xattr_csum);
@@ -747,11 +747,8 @@ int apk_fileinfo_get(int atfd, const char *filename, unsigned int flags,
 		if (r && r != ENOTSUP) return -r;
 	}
 
-	if (checksum == APK_CHECKSUM_NONE)
-		return 0;
-
-	if (S_ISDIR(st.st_mode))
-		return 0;
+	if (checksum == APK_CHECKSUM_NONE) return 0;
+	if (S_ISDIR(st.st_mode)) return 0;
 
 	/* Checksum file content */
 	if ((flags & APK_FI_NOFOLLOW) && S_ISLNK(st.st_mode)) {
diff --git a/src/lua-apk.c b/src/lua-apk.c
index fa153e1..f653163 100644
--- a/src/lua-apk.c
+++ b/src/lua-apk.c
@@ -171,7 +171,6 @@ static int Papk_db_open(lua_State *L)
 
 	memset(&opts, 0, sizeof(opts));
 	list_init(&opts.repository_list);
-	apk_atom_init();
 	if (lua_istable(L, 1))
 		get_dbopts(L, 1, &opts);
 	else
diff --git a/src/package.c b/src/package.c
index 4514ad7..5db77ec 100644
--- a/src/package.c
+++ b/src/package.c
@@ -259,7 +259,7 @@ void apk_blob_pull_dep(apk_blob_t *b, struct apk_database *db, struct apk_depend
 
 	*dep = (struct apk_dependency){
 		.name = name,
-		.version = apk_blob_atomize_dup(bver),
+		.version = apk_atomize_dup(&db->atoms, bver),
 		.repository_tag = tag,
 		.result_mask = mask,
 		.conflict = conflict,
@@ -295,7 +295,7 @@ void apk_dep_from_pkg(struct apk_dependency *dep, struct apk_database *db,
 
 	*dep = (struct apk_dependency) {
 		.name = pkg->name,
-		.version = apk_blob_atomize_dup(b),
+		.version = apk_atomize_dup(&db->atoms, b),
 		.result_mask = APK_DEPMASK_CHECKSUM,
 	};
 }
@@ -323,7 +323,7 @@ int apk_dep_is_provided(struct apk_dependency *dep, struct apk_provider *p)
 	case APK_DEPMASK_ANY:
 		return !dep->conflict;
 	default:
-		if (p->version == &apk_null_blob)
+		if (p->version == &apk_atom_null)
 			return dep->conflict;
 		if (apk_version_compare_blob_fuzzy(*p->version, *dep->version, dep->fuzzy)
 		    & dep->result_mask)
@@ -779,7 +779,7 @@ int apk_pkg_add_info(struct apk_database *db, struct apk_package *pkg,
 		pkg->name = apk_db_get_name(db, value);
 		break;
 	case 'V':
-		pkg->version = apk_blob_atomize_dup(value);
+		pkg->version = apk_atomize_dup(&db->atoms, value);
 		break;
 	case 'T':
 		pkg->description = apk_blob_cstr(value);
@@ -788,10 +788,10 @@ int apk_pkg_add_info(struct apk_database *db, struct apk_package *pkg,
 		pkg->url = apk_blob_cstr(value);
 		break;
 	case 'L':
-		pkg->license = apk_blob_atomize_dup(value);
+		pkg->license = apk_atomize_dup(&db->atoms, value);
 		break;
 	case 'A':
-		pkg->arch = apk_blob_atomize_dup(value);
+		pkg->arch = apk_atomize_dup(&db->atoms, value);
 		break;
 	case 'D':
 		apk_blob_pull_deps(&value, db, &pkg->depends);
@@ -812,10 +812,10 @@ int apk_pkg_add_info(struct apk_database *db, struct apk_package *pkg,
 		apk_blob_pull_deps(&value, db, &pkg->install_if);
 		break;
 	case 'o':
-		pkg->origin = apk_blob_atomize_dup(value);
+		pkg->origin = apk_atomize_dup(&db->atoms, value);
 		break;
 	case 'm':
-		pkg->maintainer = apk_blob_atomize_dup(value);
+		pkg->maintainer = apk_atomize_dup(&db->atoms, value);
 		break;
 	case 't':
 		pkg->build_time = apk_blob_pull_uint(&value, 10);
@@ -921,7 +921,7 @@ int apk_pkg_read(struct apk_database *db, const char *file,
 	struct apk_file_info fi;
 	int r;
 
-	r = apk_fileinfo_get(AT_FDCWD, file, APK_CHECKSUM_NONE, &fi);
+	r = apk_fileinfo_get(AT_FDCWD, file, APK_CHECKSUM_NONE, &fi, &db->atoms);
 	if (r != 0)
 		return r;
 
diff --git a/src/solver.c b/src/solver.c
index 0881196..febf52d 100644
--- a/src/solver.c
+++ b/src/solver.c
@@ -42,7 +42,7 @@ struct apk_solver_state {
 
 static struct apk_provider provider_none = {
 	.pkg = NULL,
-	.version = &apk_null_blob
+	.version = &apk_atom_null
 };
 
 void apk_solver_set_name_flags(struct apk_name *name,
@@ -321,10 +321,10 @@ static void exclude_non_providers(struct apk_solver_state *ss, struct apk_name *
 
 	foreach_array_item(p, name->providers) {
 		if (p->pkg->name == must_provide || !p->pkg->ss.pkg_selectable ||
-		    (skip_virtuals && p->version == &apk_null_blob))
+		    (skip_virtuals && p->version == &apk_atom_null))
 			goto next;
 		foreach_array_item(d, p->pkg->provides)
-			if (d->name == must_provide || (skip_virtuals && d->version == &apk_null_blob))
+			if (d->name == must_provide || (skip_virtuals && d->version == &apk_atom_null))
 				goto next;
 		disqualify_package(ss, p->pkg, "provides transitivity");
 	next: ;
@@ -425,10 +425,10 @@ static void reconsider_name(struct apk_solver_state *ss, struct apk_name *name)
 				merge_index(&dep->name->ss.merge_depends, num_options);
 
 		if (merge_index(&pkg->name->ss.merge_provides, num_options))
-			pkg->name->ss.has_virtual_provides |= (p->version == &apk_null_blob);
+			pkg->name->ss.has_virtual_provides |= (p->version == &apk_atom_null);
 		foreach_array_item(dep, pkg->provides)
 			if (merge_index(&dep->name->ss.merge_provides, num_options))
-				dep->name->ss.has_virtual_provides |= (dep->version == &apk_null_blob);
+				dep->name->ss.has_virtual_provides |= (dep->version == &apk_atom_null);
 
 		num_tag_not_ok += !pkg->ss.tag_ok;
 		num_options++;
@@ -630,8 +630,8 @@ static void assign_name(struct apk_solver_state *ss, struct apk_name *name, stru
 
 	if (name->ss.locked) {
 		/* If both are providing this name without version, it's ok */
-		if (p.version == &apk_null_blob &&
-		    name->ss.chosen.version == &apk_null_blob)
+		if (p.version == &apk_atom_null &&
+		    name->ss.chosen.version == &apk_atom_null)
 			return;
 		if (ss->ignore_conflict)
 			return;
@@ -656,8 +656,8 @@ static void assign_name(struct apk_solver_state *ss, struct apk_name *name, stru
 		foreach_array_item(p0, name->providers) {
 			if (p0->pkg == p.pkg)
 				continue;
-			if (p.version == &apk_null_blob &&
-			    p0->version == &apk_null_blob)
+			if (p.version == &apk_atom_null &&
+			    p0->version == &apk_atom_null)
 				continue;
 			disqualify_package(ss, p0->pkg, "conflicting provides");
 		}
@@ -668,7 +668,7 @@ static void assign_name(struct apk_solver_state *ss, struct apk_name *name, stru
 
 static void select_package(struct apk_solver_state *ss, struct apk_name *name)
 {
-	struct apk_provider chosen = { NULL, &apk_null_blob }, *p;
+	struct apk_provider chosen = { NULL, &apk_atom_null }, *p;
 	struct apk_package *pkg = NULL;
 	struct apk_dependency *d;
 
@@ -689,7 +689,7 @@ static void select_package(struct apk_solver_state *ss, struct apk_name *name)
 				continue;
 			/* Virtual packages without provider_priority cannot be autoselected,
 			 * unless there is only one provider */
-			if (p->version == &apk_null_blob &&
+			if (p->version == &apk_atom_null &&
 			    p->pkg->name->auto_select_virtual == 0 &&
 			    p->pkg->name->ss.requirers == 0 &&
 			    (p->pkg->provider_priority == 0 && name->providers->num > 1))
@@ -934,7 +934,7 @@ static void generate_changeset(struct apk_solver_state *ss, struct apk_dependenc
 		pkg->name->ss.installed_pkg = pkg;
 		pkg->name->ss.installed_name = pkg->name;
 		foreach_array_item(d, pkg->provides)
-			if (d->version != &apk_null_blob)
+			if (d->version != &apk_atom_null)
 				d->name->ss.installed_name = pkg->name;
 	}
 	list_for_each_entry(ipkg, &ss->db->installed.packages, installed_pkgs_list)
-- 
cgit v1.2.3-70-g09d2