From 17404bdb42c983589c6e807b0a3991f42da724b2 Mon Sep 17 00:00:00 2001
From: Juan RP <xtraeme@gmail.com>
Date: Sat, 8 Aug 2009 22:29:48 +0200
Subject: [PATCH] Mega-commit with improvements and changes done in the past
 days.

- Introduce package states: unpacked, broken, installed, etc.
  Not yet finished, only unpacked and installed are used for now.

- Move package metadata files in binary packages directly to
  the top directory, this speeds up some ops and makes easier to
  continue working in future changes.

- xbps-bin: -C flag to check the hash of package files has been
  superseded by the 'check' target, which verifies the integrity
  of an installed package.

- Use the 'essential' object when upgrading packages, overwritting
  current files. This is needed for critical packages like sh, libc
  and others.

- Miscellaneous tweaks and improvements thorough the code.

--HG--
extra : convert_revision : 2073fcc123efc24b3e9327b5e22aa91752f20df6
---
 bin/xbps-bin/Makefile       |   2 +-
 bin/xbps-bin/check.c        | 289 ++++++++++++++++++++++
 bin/xbps-bin/defs.h         |   3 +-
 bin/xbps-bin/install.c      | 475 +++++++++++++++++++++---------------
 bin/xbps-bin/main.c         |  25 +-
 bin/xbps-bin/remove.c       |   6 +-
 bin/xbps-pkgdb/main.c       |   7 +-
 bin/xbps-repo/index.c       |  74 ++----
 bin/xbps-repo/util.c        |  40 +--
 bin/xbps-repo/util.h        |   2 +-
 doc/BINPKG_INFO             |  25 +-
 doc/TODO                    |   4 -
 lib/Makefile                |   7 +-
 lib/configure.c             |  92 +++++++
 lib/depends.c               |  76 ++++--
 lib/findpkg.c               |  28 ++-
 lib/register.c              | 111 ++-------
 lib/remove.c                |  37 +--
 lib/state.c                 | 235 ++++++++++++++++++
 lib/unpack.c                | 128 ++++++----
 lib/util.c                  |  17 +-
 shutils/make-binpkg.sh      |  49 ++--
 shutils/metadata.sh         |  10 +-
 shutils/metadata_scripts.sh |   3 +-
 24 files changed, 1216 insertions(+), 529 deletions(-)
 create mode 100644 bin/xbps-bin/check.c
 create mode 100644 lib/configure.c
 create mode 100644 lib/state.c

diff --git a/bin/xbps-bin/Makefile b/bin/xbps-bin/Makefile
index b1cf168624b..546853bbe6a 100644
--- a/bin/xbps-bin/Makefile
+++ b/bin/xbps-bin/Makefile
@@ -1,5 +1,5 @@
 BIN	= xbps-bin
-OBJS	= install.o main.o remove.o ../xbps-repo/util.o
+OBJS	= check.o install.o main.o remove.o ../xbps-repo/util.o
 
 TOPDIR = ../..
 include $(TOPDIR)/prog.mk
diff --git a/bin/xbps-bin/check.c b/bin/xbps-bin/check.c
new file mode 100644
index 00000000000..95e5d520160
--- /dev/null
+++ b/bin/xbps-bin/check.c
@@ -0,0 +1,289 @@
+/*-
+ * Copyright (c) 2009 Juan Romero Pardines.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <limits.h>
+#include <libgen.h>
+#include <unistd.h>
+
+#include <xbps_api.h>
+#include "defs.h"
+
+/*
+ * Checks package integrity of an installed package. This
+ * consists in four tasks:
+ *
+ * 	o Check for metadata files (files.plist and props.plist),
+ * 	  we only check if the file exists and its dictionary can
+ * 	  be externalized and is not empty.
+ * 	o Check for missing installed files.
+ * 	o Check the hash for all installed files, except
+ * 	  configuration files (which is expected if they are modified).
+ * 	o Check for missing run time dependencies.
+ *
+ * If any of these checks fail, the package will change its
+ * state to 'broken'.
+ */
+
+int
+xbps_check_pkg_integrity(const char *pkgname)
+{
+	prop_dictionary_t pkgd, propsd, filesd;
+	prop_array_t array;
+	prop_object_t obj;
+	prop_object_iterator_t iter;
+	const char *rootdir, *file, *sha256, *reqpkg;
+	char *path;
+	int rv = 0;
+	bool files_broken = false, broken = false;
+
+	assert(pkgname != NULL);
+
+	rootdir = xbps_get_rootdir();
+	pkgd = xbps_find_pkg_installed_from_plist(pkgname);
+	if (pkgd == NULL) {
+		printf("Package %s is not installed.\n", pkgname);
+		return 0;
+	}
+
+	/*
+	 * Check for props.plist metadata file.
+	 */
+	path = xbps_xasprintf("%s/%s/metadata/%s/%s", rootdir,
+	    XBPS_META_PATH, pkgname, XBPS_PKGPROPS);
+	if (path == NULL) {
+		rv = errno;
+		goto out;
+	}
+
+	propsd = prop_dictionary_internalize_from_file(path);
+	free(path);
+	if (propsd == NULL) {
+		printf("%s: unexistent %s metadata file.\n", pkgname,
+		    XBPS_PKGPROPS);
+		rv = errno;
+		broken = true;
+		goto out;
+	} else if (prop_object_type(propsd) != PROP_TYPE_DICTIONARY) {
+		printf("%s: invalid %s metadata file.\n", pkgname,
+		    XBPS_PKGPROPS);
+		rv = EINVAL;
+		broken = true;
+		goto out1;
+	} else if (prop_dictionary_count(propsd) == 0) {
+		printf("%s: incomplete %s metadata file.\n", pkgname,
+		    XBPS_PKGPROPS);
+		rv = EINVAL;
+		broken = true;
+		goto out1;
+	}
+
+	/*
+	 * Check for files.plist metadata file.
+	 */
+	path = xbps_xasprintf("%s/%s/metadata/%s/%s", rootdir,
+	    XBPS_META_PATH, pkgname, XBPS_PKGFILES);
+	if (path == NULL) {
+		rv = errno;
+		goto out1;
+	}
+
+	filesd = prop_dictionary_internalize_from_file(path);
+	free(path);
+	if (filesd == NULL) {
+		printf("%s: unexistent %s metadata file.\n", pkgname,
+		    XBPS_PKGPROPS);
+		rv = ENOENT;
+		broken = true;
+		goto out1;
+	} else if (prop_object_type(filesd) != PROP_TYPE_DICTIONARY) {
+		printf("%s: invalid %s metadata file.\n", pkgname,
+		    XBPS_PKGFILES);
+		rv = EINVAL;
+		broken = true;
+		goto out2;
+	} else if (prop_dictionary_count(filesd) == 0) {
+		printf("%s: incomplete %s metadata file.\n", pkgname,
+		    XBPS_PKGFILES);
+		rv = EINVAL;
+		broken = true;
+		goto out2;
+	} else if (((array = prop_dictionary_get(filesd, "files")) == NULL) ||
+		   ((array = prop_dictionary_get(filesd, "links")) == NULL) ||
+		   ((array = prop_dictionary_get(filesd, "dirs")) == NULL)) {
+			printf("%s: incomplete %s metadata file.\n", pkgname,
+			    XBPS_PKGFILES);
+			rv = EINVAL;
+			broken = true;
+			goto out2;
+	}
+
+	printf("%s: metadata check PASSED.\n", pkgname);
+
+	/*
+	 * Check for missing files and its hash.
+	 */
+	array = prop_dictionary_get(filesd, "files");
+	if ((prop_object_type(array) == PROP_TYPE_ARRAY) &&
+	     prop_array_count(array) > 0) {
+		iter = xbps_get_array_iter_from_dict(filesd, "files");
+		if (iter == NULL) {
+			rv = ENOMEM;
+			goto out2;
+		}
+		while ((obj = prop_object_iterator_next(iter))) {
+			prop_dictionary_get_cstring_nocopy(obj, "file", &file);
+			path = xbps_xasprintf("%s/%s", rootdir, file);
+			if (path == NULL) {
+				prop_object_iterator_release(iter);
+				rv = errno;
+				goto out2;
+			}
+                        prop_dictionary_get_cstring_nocopy(obj,
+                            "sha256", &sha256);
+			rv = xbps_check_file_hash(path, sha256);
+			switch (rv) {
+			case 0:
+				break;
+			case ENOENT:
+				printf("%s: unexistent file %s.\n",
+				    pkgname, file);
+				files_broken = true;
+				broken = true;
+				break;
+			case ERANGE:
+                                printf("%s: hash mismatch for %s.\n",
+				    pkgname, file);
+				files_broken = true;
+				broken = true;
+				break;
+			default:
+				printf("%s: unexpected error for %s (%s)\n",
+				    pkgname, file, strerror(rv));
+				break;
+			}
+			free(path);
+                }
+                prop_object_iterator_release(iter);
+		if (files_broken)
+			printf("%s: files check FAILED.\n", pkgname);
+		else
+			printf("%s: files check PASSED.\n", pkgname);
+	}
+
+	/*
+	 * Check for missing configuration files.
+	 */
+	array = prop_dictionary_get(filesd, "conf_files");
+	if (array && prop_object_type(array) == PROP_TYPE_ARRAY &&
+	    prop_array_count(array) > 0) {
+		iter = xbps_get_array_iter_from_dict(filesd, "conf_files");
+		if (iter == NULL) {
+			rv = ENOMEM;
+			goto out2;
+		}
+		while ((obj = prop_object_iterator_next(iter))) {
+			prop_dictionary_get_cstring_nocopy(obj, "file", &file);
+			path = xbps_xasprintf("%s/%s", rootdir, file);
+			if (path == NULL) {
+				prop_object_iterator_release(iter);
+				rv = ENOMEM;
+				goto out2;
+			}
+			if ((rv = access(path, R_OK)) == -1) {
+				if (errno == ENOENT) {
+					printf("%s: unexistent file %s\n",
+					    pkgname, file);
+					broken = true;
+				} else
+					printf("%s: unexpected error for "
+					    "%s (%s)\n", pkgname, file,
+					    strerror(errno));
+			}
+			free(path);
+		}
+		prop_object_iterator_release(iter);
+		if (rv == 0)
+			printf("%s: configuration files check PASSED.\n",
+			    pkgname);
+		else
+			printf("%s: configuration files check FAILED.\n",
+			    pkgname);
+	}
+
+	/*
+	 * Check for missing run time dependencies.
+	 */
+	if (xbps_pkg_has_rundeps(propsd)) {
+		iter = xbps_get_array_iter_from_dict(propsd, "run_depends");
+		if (iter == NULL) {
+			rv = ENOMEM;
+			goto out2;
+		}
+		while ((obj = prop_object_iterator_next(iter))) {
+			reqpkg = prop_string_cstring_nocopy(obj);
+			if (xbps_check_is_installed_pkg(reqpkg) < 0) {
+				rv = ENOENT;
+				printf("%s: dependency not satisfied: %s\n",
+				    pkgname, reqpkg);
+				broken = true;
+			}
+		}
+		prop_object_iterator_release(iter);
+		if (rv == ENOENT)
+			printf("%s: run-time dependency check FAILED.\n",
+			    pkgname);
+		else
+			printf("%s: run-time dependency check PASSED.\n",
+			    pkgname);
+	}
+
+out2:
+	prop_object_release(filesd);
+out1:
+	prop_object_release(propsd);
+out:
+	prop_object_release(pkgd);
+
+	if (broken) {
+		rv = xbps_set_pkg_state_installed(pkgname,
+		    XBPS_PKG_STATE_BROKEN);
+		if (rv == 0)
+			printf("%s: changed package state to broken.\n",
+			    pkgname);
+		else
+			printf("%s: can't change package state (%s).\n",
+			    pkgname, strerror(rv));
+	}
+
+	xbps_release_regpkgdb_dict();
+
+	return rv;
+}
diff --git a/bin/xbps-bin/defs.h b/bin/xbps-bin/defs.h
index 578ab688ffe..e5664073500 100644
--- a/bin/xbps-bin/defs.h
+++ b/bin/xbps-bin/defs.h
@@ -28,7 +28,8 @@
 
 void	xbps_install_pkg(const char *, bool, bool);
 void	xbps_autoremove_pkgs(void);
-void	xbps_remove_pkg(const char *, bool);
+void	xbps_remove_installed_pkg(const char *, bool);
 void	xbps_autoupdate_pkgs(bool);
+int	xbps_check_pkg_integrity(const char *);
 
 #endif /* !_XBPS_BIN_DEFS_H_ */
diff --git a/bin/xbps-bin/install.c b/bin/xbps-bin/install.c
index 55a79a3bc5b..db417880aba 100644
--- a/bin/xbps-bin/install.c
+++ b/bin/xbps-bin/install.c
@@ -27,6 +27,7 @@
 #include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
+#include <err.h>
 #include <errno.h>
 #include <limits.h>
 #include <prop/proplib.h>
@@ -34,8 +35,25 @@
 #include <xbps_api.h>
 #include "defs.h"
 
+enum {
+	TRANS_ONE,
+	TRANS_ALL
+};
+
+struct transaction {
+	prop_dictionary_t dict;
+	prop_object_iterator_t iter;
+	const char *originpkgname;
+	const char *curpkgname;
+	int type;
+	bool force;
+	bool update;
+};
+
 static void	show_missing_deps(prop_dictionary_t, const char *);
 static int	show_missing_dep_cb(prop_object_t, void *, bool *);
+static int	exec_transaction(struct transaction *);
+static void	cleanup(int);
 
 static void
 show_missing_deps(prop_dictionary_t d, const char *pkgname)
@@ -65,36 +83,44 @@ show_missing_dep_cb(prop_object_t obj, void *arg, bool *loop_done)
 	return EINVAL;
 }
 
-static void
-check_pkg_hashes(prop_dictionary_t props, prop_object_iterator_t iter)
+static int
+check_pkg_hashes(prop_object_iterator_t iter)
 {
 	prop_object_t obj;
-	const char *repoloc, *filename;
+	const char *pkgname, *repoloc, *filename;
 	int rv = 0;
+	pkg_state_t state = 0;
 
 	printf("Checking binary package file(s) integrity...\n");
 	while ((obj = prop_object_iterator_next(iter)) != NULL) {
+		prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname);
+		state = 0;
+		if (xbps_get_pkg_state_dictionary(obj, &state) != 0)
+			return EINVAL;
+
+		if (state == XBPS_PKG_STATE_UNPACKED)
+			continue;
+
 		prop_dictionary_get_cstring_nocopy(obj, "repository", &repoloc);
 		prop_dictionary_get_cstring_nocopy(obj, "filename", &filename);
 		rv = xbps_check_pkg_file_hash(obj, repoloc);
 		if (rv != 0 && rv != ERANGE) {
-			printf("error: checking hash for %s (%s)\n",
-			    filename, strerror(rv));
-			prop_object_release(props);
-			exit(EXIT_FAILURE);
+			printf("Unexpected error while checking hash for "
+			    "%s (%s)\n", filename, strerror(rv));
+			return -1;
 		} else if (rv != 0 && rv == ERANGE) {
-			printf("Hash doesn't match for %s!\n", filename);
-			prop_object_release(props);
-			exit(EXIT_FAILURE);
+			printf("Hash mismatch for %s, exiting.\n",
+			    filename);
+			return -1;
 		}
 	}
 	prop_object_iterator_reset(iter);
-	printf("\n");
+
+	return 0;
 }
 
-static void
-show_transaction_sizes(prop_dictionary_t props, prop_object_iterator_t iter,
-		       const char *descr)
+static int
+show_transaction_sizes(prop_object_iterator_t iter, const char *descr)
 {
 	prop_object_t obj;
 	uint64_t tsize = 0, dlsize = 0, instsize = 0;
@@ -145,301 +171,344 @@ show_transaction_sizes(prop_dictionary_t props, prop_object_iterator_t iter,
 	 */
 	if (xbps_humanize_number(size, 5, (int64_t)dlsize,
 	    "", HN_AUTOSCALE, HN_NOSPACE) == -1) {
-		printf("error: humanize_number %s\n", strerror(errno));
-		prop_object_release(props);
-		exit(EXIT_FAILURE);
+		printf("error: humanize_number returns %s\n",
+		    strerror(errno));
+		return -1;
 	}
 	printf("Total download size: %s\n", size);
 	if (xbps_humanize_number(size, 5, (int64_t)instsize,
 	    "", HN_AUTOSCALE, HN_NOSPACE) == -1) {
-		printf("error: humanize_number2 %s\n", strerror(errno));
-		prop_object_release(props);
-		exit(EXIT_FAILURE);
+		printf("error: humanize_number2 returns %s\n",
+		    strerror(errno));
+		return -1;
 	}
 	printf("Total installed size: %s\n\n", size);
+
+	return 0;
 }
 
 void
 xbps_install_pkg(const char *pkg, bool force, bool update)
 {
-	prop_dictionary_t props, instpkg;
+	struct transaction *trans;
+	prop_dictionary_t pkgd;
 	prop_array_t array;
-	prop_object_t obj;
-	prop_object_iterator_t iter;
-	const char *instver, *origin, *pkgname, *version;
 	int rv = 0;
-	bool pkg_is_dep = false, doup = false;
 
 	/*
-	 * Find and sort all required package dictionaries.
+	 * Find all required pkgs and sort the package transaction.
 	 */
-	printf("Finding/sorting required binary packages...\n");
-
-	rv = xbps_prepare_pkg(pkg);
-	if (rv != 0 && rv == EAGAIN) {
-		printf("Unable to locate %s in repository pool.\n", pkg);
-		exit(EXIT_FAILURE);
-	} else if (rv != 0 && rv != ENOENT) {
-		printf("Unexpected error: %s\n", strerror(rv));
-		exit(EXIT_FAILURE);
+	pkgd = xbps_find_pkg_installed_from_plist(pkg);
+	if (update) {
+		if (pkgd) {
+			if ((rv = xbps_find_new_pkg(pkg, pkgd)) == 0) {
+				printf("Package '%s' is up to date.\n", pkg);
+				prop_object_release(pkgd);
+				cleanup(rv);
+			}
+			prop_object_release(pkgd);
+		} else {
+			printf("Package '%s' not installed.\n", pkg);
+			cleanup(rv);
+		}
+	} else {
+		if (pkgd) {
+			printf("Package '%s' is already installed.\n", pkg);
+			prop_object_release(pkgd);
+			cleanup(rv);
+		}
+		rv = xbps_prepare_pkg(pkg);
+		if (rv != 0 && rv == EAGAIN) {
+			printf("unable to locate %s in repository pool.", pkg);
+			cleanup(rv);
+		} else if (rv != 0 && rv != ENOENT) {
+			printf("unexpected error: %s", strerror(rv));
+			cleanup(rv);
+		}
 	}
 
-	props = xbps_get_pkg_props();
-	if (props == NULL) {
+	trans = calloc(1, sizeof(struct transaction));
+	if (trans == NULL)
+		goto out;
+
+	trans->dict = xbps_get_pkg_props();
+	if (trans->dict == NULL) {
 		printf("error: unexistent props dictionary!\n");
-		exit(EXIT_FAILURE);
+		goto out1;
 	}
 
 	/*
 	 * Bail out if there are unresolved deps.
 	 */
-	array = prop_dictionary_get(props, "missing_deps");
+	array = prop_dictionary_get(trans->dict, "missing_deps");
 	if (prop_array_count(array) > 0) {
-		show_missing_deps(props, pkg);
-		goto out;
+		show_missing_deps(trans->dict, pkg);
+		goto out2;
 	}
 
-	prop_dictionary_get_cstring_nocopy(props, "origin", &origin);
+	prop_dictionary_get_cstring_nocopy(trans->dict,
+	     "origin", &trans->originpkgname);
 
-	array = prop_dictionary_get(props, "packages");
-	if (array == NULL || prop_array_count(array) == 0) {
-		printf("error: empty packages array!\n");
-		goto out;
-	}
-	iter = prop_array_iterator(array);
-	if (iter == NULL) {
-		printf("error: allocating array mem! (%s)\n", strerror(errno));
-		goto out;
+	/*
+	 * It's time to run the transaction!
+	 */
+	trans->iter = xbps_get_array_iter_from_dict(trans->dict, "packages");
+	if (trans->iter == NULL) {
+		printf("error: allocating array mem! (%s)",
+		    strerror(errno));
+		goto out2;
 	}
+
+	trans->force = force;
+	trans->update = update;
+	trans->type = TRANS_ONE;
+	rv = exec_transaction(trans);
+
+	prop_object_iterator_release(trans->iter);
+out2:
+	prop_object_release(trans->dict);
+out1:
+	free(trans);
+out:
+	cleanup(rv);
+}
+
+static int
+exec_transaction(struct transaction *trans)
+{
+	prop_dictionary_t instpkgd;
+	prop_object_t obj;
+	const char *pkgname, *version, *instver, *filename;
+	int rv = 0;
+	bool essential, isdep;
+	pkg_state_t state = 0;
+
+	assert(trans != NULL);
+	assert(trans->dict != NULL);
+	assert(trans->iter != NULL);
+
+	essential = isdep = false;
 	/*
 	 * Show download/installed size for the transaction.
 	 */
-	show_transaction_sizes(props, iter, "installed");
+	rv = show_transaction_sizes(trans->iter,
+	    trans->type == TRANS_ALL ? "updated" : "installed");
+	if (rv != 0)
+		return rv;
 
 	/*
 	 * Ask interactively (if -f not set).
 	 */
-	if (force == false) {
+	if (trans->force == false) {
 		if (xbps_noyes("Do you want to continue?") == false) {
 			printf("Aborting!\n");
-			goto out2;
+			return 0;
 		}
 	}
 
 	/*
 	 * Check the SHA256 hash for all required packages.
 	 */
-	check_pkg_hashes(props, iter);
+	if ((rv = check_pkg_hashes(trans->iter)) != 0)
+		return rv;
 
 	/*
-	 * Install all packages, the list is already sorted.
+	 * Iterate over the transaction dictionary.
 	 */
-	while ((obj = prop_object_iterator_next(iter)) != NULL) {
+	while ((obj = prop_object_iterator_next(trans->iter)) != NULL) {
 		prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname);
 		prop_dictionary_get_cstring_nocopy(obj, "version", &version);
-		if (strcmp(origin, pkgname))
-			pkg_is_dep = true;
+		prop_dictionary_get_bool(obj, "essential", &essential);
+		prop_dictionary_get_cstring_nocopy(obj, "filename", &filename);
 
-		if (update && strcmp(pkg, pkgname) == 0) {
-			/*
-			* Update a package, firstly removing current package.
-			 */
-			instpkg = xbps_find_pkg_installed_from_plist(pkgname);
-			if (instpkg == NULL) {
+		if ((trans->type == TRANS_ONE) &&
+		    strcmp(trans->originpkgname, pkgname))
+			isdep = true;
+
+		/*
+		 * If dependency is already unpacked skip this phase.
+		 */
+		state = 0;
+		if (xbps_get_pkg_state_dictionary(obj, &state) != 0)
+			return EINVAL;
+
+		if (state == XBPS_PKG_STATE_UNPACKED)
+			continue;
+
+		if ((trans->type == TRANS_ALL) ||
+		    (trans->update &&
+		     strcmp(trans->curpkgname, pkgname) == 0)) {
+			instpkgd = xbps_find_pkg_installed_from_plist(pkgname);
+			if (instpkgd == NULL) {
 				printf("error: unable to find %s installed "
 				    "dict!\n", pkgname);
-				goto out2;
+				return EINVAL;
 			}
 
-			prop_dictionary_get_cstring_nocopy(instpkg,
+			prop_dictionary_get_cstring_nocopy(instpkgd,
 			    "version", &instver);
-			printf("Updating package %s-%s to %s...\n", pkgname,
-			    instver, version);
-			prop_object_release(instpkg);
-			rv = xbps_remove_binary_pkg(pkgname, update);
-			if (rv != 0) {
-				printf("error: removing %s-%s (%s)\n",
-				    pkgname, instver, strerror(rv));
-				goto out2;
-			}
+			prop_object_release(instpkgd);
 
-		} else {
-			printf("Installing %s%s-%s ...\n",
-			    pkg_is_dep ? "dependency " : "", pkgname, version);
+			/*
+			 * If this package is not 'essential', just remove
+			 * the old package and install the new one. Otherwise
+			 * we just overwrite the files.
+			 */
+			if (essential == false) {
+				rv = xbps_remove_pkg(pkgname, version, true);
+				if (rv != 0) {
+					printf("error: removing %s-%s (%s)\n",
+					    pkgname, instver, strerror(rv));
+					return rv;
+				}
+			}
 		}
 		/*
 		 * Unpack binary package.
 		 */
-		if ((rv = xbps_unpack_binary_pkg(obj)) != 0) {
+		printf("Unpacking %s-%s (from .../%s) ...\n", pkgname, version,
+		    filename);
+		if ((rv = xbps_unpack_binary_pkg(obj, essential)) != 0) {
 			printf("error: unpacking %s-%s (%s)\n", pkgname,
 			    version, strerror(rv));
-			goto out2;
+			return rv;
 		}
 		/*
 		 * Register binary package.
 		 */
-		if (update && !pkg_is_dep)
-			doup = true;
-
-		if ((rv = xbps_register_pkg(obj, doup, pkg_is_dep)) != 0) {
+		if ((rv = xbps_register_pkg(obj, isdep)) != 0) {
 			printf("error: registering %s-%s! (%s)\n",
 			    pkgname, version, strerror(rv));
-			goto out2;
+			return rv;
 		}
-		pkg_is_dep = false;
+		isdep = false;
+		/*
+		 * Set package state to unpacked in the transaction
+		 * dictionary.
+		 */
+		if ((rv = xbps_set_pkg_state_dictionary(obj,
+		    XBPS_PKG_STATE_UNPACKED)) != 0)
+			return rv;
 	}
-out2:
-	prop_object_iterator_release(iter);
-out:
-	prop_object_release(props);
-	xbps_release_repolist_data();
-	xbps_release_regpkgdb_dict();
-	if (rv != 0)
-		exit(EXIT_FAILURE);
+	prop_object_iterator_reset(trans->iter);
+	/*
+	 * Configure all unpacked packages.
+	 */
+	while ((obj = prop_object_iterator_next(trans->iter)) != NULL) {
+		prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname);
+		prop_dictionary_get_cstring_nocopy(obj, "version", &version);
+		printf("Configuring package %s-%s ...\n", pkgname, version);
 
-	exit(EXIT_SUCCESS);
+		if ((rv = xbps_configure_pkg(pkgname, version)) != 0) {
+			printf("Error configuring package %s-%s\n",
+			    pkgname, version);
+			return rv;
+		}
+	}
+
+	return 0;
 }
 
 void
 xbps_autoupdate_pkgs(bool force)
 {
-	prop_dictionary_t dict, props, instpkg;
-	prop_array_t array;
+	struct transaction *trans;
+	prop_dictionary_t dict;
 	prop_object_t obj;
-	prop_object_iterator_t iter;
-	const char *pkgname, *version, *instver;
+	const char *pkgname;
 	int rv = 0;
 
+	/*
+	 * Prepare dictionary with all registered packages.
+	 */
 	dict = xbps_prepare_regpkgdb_dict();
 	if (dict == NULL) {
 		printf("No packages currently installed (%s).\n",
 		    strerror(errno));
-		exit(EXIT_SUCCESS);
+		cleanup(rv);
 	}
-
-	iter = xbps_get_array_iter_from_dict(dict, "packages");
-	if (iter == NULL) {
-		rv = EINVAL;
-		goto out;
-	}
-
+	/*
+	 * Prepare dictionary with all registered repositories.
+	 */
 	if ((rv = xbps_prepare_repolist_data()) != 0)
 		goto out;
 
-	while ((obj = prop_object_iterator_next(iter)) != NULL) {
+	/*
+	 * Prepare transaction data.
+	 */
+	trans = calloc(1, sizeof(struct transaction));
+	if (trans == NULL)
+		goto out;
+
+	trans->iter = xbps_get_array_iter_from_dict(dict, "packages");
+	if (trans->iter == NULL) {
+		rv = EINVAL;
+		goto out1;
+	}
+
+	/*
+	 * Find out if there is a newer version for all currently
+	 * installed packages.
+	 */
+	while ((obj = prop_object_iterator_next(trans->iter)) != NULL) {
 		prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname);
 		rv = xbps_find_new_pkg(pkgname, obj);
-		if (rv != 0)
-			break;
+		if (rv != 0) {
+			prop_object_iterator_release(trans->iter);
+			goto out1;
+		}
 	}
-	prop_object_iterator_release(iter);
+	prop_object_iterator_release(trans->iter);
 
-	/* Sort the list of packages */
-	props = xbps_get_pkg_props();
-	if (props == NULL) {
+	/*
+	 * Get package transaction dictionary.
+	 */
+	trans->dict = xbps_get_pkg_props();
+	if (trans->dict == NULL) {
 		if (errno == 0) {
 			printf("All packages are up-to-date.\n");
-			exit(EXIT_SUCCESS);
+			goto out;
 		}
 		printf("Error while checking for new pkgs: %s\n",
 		    strerror(errno));
-		goto out;
+		goto out1;
 	}
-	if ((rv = xbps_sort_pkg_deps(props)) != 0) {
+	/*
+	 * Sort the package transaction dictionary.
+	 */
+	if ((rv = xbps_sort_pkg_deps(trans->dict)) != 0) {
 		printf("Error while sorting packages: %s\n",
 		    strerror(rv));
-		goto out;
+		goto out2;
 	}
-	/* Update all packages now */
-	array = prop_dictionary_get(props, "packages");
-	if (array == NULL || prop_array_count(array) == 0) {
-		printf("error: empty packages array!\n");
-		prop_object_release(props);
-		goto out;
-	}
-	iter = prop_array_iterator(array);
-	if (iter == NULL) {
+
+	/*
+	 * It's time to run the transaction!
+	 */
+	trans->iter = xbps_get_array_iter_from_dict(trans->dict, "packages");
+	if (trans->iter == NULL) {
 		printf("error: allocating array mem! (%s)\n", strerror(errno));
-		prop_object_release(props);
-		goto out;
+		goto out2;
 	}
 
-	/*
-	 * Show download/installed size for the transaction.
-	 */
-	show_transaction_sizes(props, iter, "upgraded");
-
-	/*
-	 * Ask interactively (if -f not set).
-	 */
-	if (force == false) {
-		if (xbps_noyes("Do you want to continue?") == false) {
-			printf("Aborting!\n");
-			prop_object_release(props);
-			goto out2;
-		}
-	}
-
-	/*
-	 * Check the SHA256 hash for all required packages.
-	 */
-	check_pkg_hashes(props, iter);
-
-	while ((obj = prop_object_iterator_next(iter)) != NULL) {
-		prop_dictionary_get_cstring_nocopy(obj, "pkgname", &pkgname);
-		prop_dictionary_get_cstring_nocopy(obj, "version", &version);
-
-		/*
-		 * Update a package, firstly removing current package.
-		 */
-		instpkg = xbps_find_pkg_installed_from_plist(pkgname);
-		if (instpkg == NULL) {
-			printf("error: unable to find %s installed "
-			    "dict!\n", pkgname);
-			prop_object_release(props);
-			goto out2;
-		}
-
-		prop_dictionary_get_cstring_nocopy(instpkg,
-		    "version", &instver);
-		printf("Updating package %s-%s to %s...\n", pkgname,
-		    instver, version);
-		prop_object_release(instpkg);
-		rv = xbps_remove_binary_pkg(pkgname, true);
-		if (rv != 0) {
-			printf("error: removing %s-%s (%s)\n",
-			    pkgname, instver, strerror(rv));
-			prop_object_release(props);
-			goto out2;
-		}
-
-		/*
-		 * Unpack binary package.
-		 */
-		if ((rv = xbps_unpack_binary_pkg(obj)) != 0) {
-			printf("error: unpacking %s-%s (%s)\n", pkgname,
-			    version, strerror(rv));
-			prop_object_release(props);
-			goto out2;
-		}
-		/*
-		 * Register binary package.
-		 */
-		if ((rv = xbps_register_pkg(obj, true, false)) != 0) {
-			printf("error: registering %s-%s! (%s)\n",
-			    pkgname, version, strerror(rv));
-			prop_object_release(props);
-			goto out2;
-		}
-	}
+	trans->force = force;
+	trans->update = true;
+	trans->type = TRANS_ALL;
+	rv = exec_transaction(trans);
 
+	prop_object_iterator_release(trans->iter);
 out2:
-	prop_object_iterator_release(iter);
+	prop_object_release(trans->dict);
+out1:
+	free(trans);
 out:
+	cleanup(rv);
+}
+
+static void
+cleanup(int rv)
+{
 	xbps_release_repolist_data();
 	xbps_release_regpkgdb_dict();
-	if (rv != 0)
-		exit(EXIT_FAILURE);
-
-	exit(EXIT_SUCCESS);
+	exit(rv == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
 }
diff --git a/bin/xbps-bin/main.c b/bin/xbps-bin/main.c
index 0db1329dc20..0feee5e6c0f 100644
--- a/bin/xbps-bin/main.c
+++ b/bin/xbps-bin/main.c
@@ -45,9 +45,10 @@ usage(void)
 {
 	printf("Usage: xbps-bin [options] [target] [arguments]\n\n"
 	" Available targets:\n"
-        "    autoremove, autoupdate, files, install, list, remove\n"
-	"    show, update\n"
+        "    autoremove, autoupdate, check, files, install, list\n"
+	"    remove, show, update\n"
 	" Targets with arguments:\n"
+	"    check\t<pkgname>\n"
 	"    files\t<pkgname>\n"
 	"    install\t<pkgname>\n"
 	"    remove\t<pkgname>\n"
@@ -56,8 +57,6 @@ usage(void)
 	" Options shared by all targets:\n"
 	"    -r\t\t<rootdir>\n"
 	"    -v\t\t<verbose>\n"
-	" Options used by the files target:\n"
-	"    -C\t\tTo check the SHA256 hash for any listed file.\n"
 	" Options used by the (auto)remove target:\n"
 	"    -f\t\tForce installation or removal of packages.\n"
 	"      \t\tBeware with this option if you use autoremove!\n"
@@ -65,7 +64,7 @@ usage(void)
 	" Examples:\n"
 	"    $ xbps-bin autoremove\n"
 	"    $ xbps-bin autoupdate\n"
-	"    $ xbps-bin -C files klibc\n"
+	"    $ xbps-bin files klibc\n"
 	"    $ xbps-bin install klibc\n"
 	"    $ xbps-bin -r /path/to/root install klibc\n"
 	"    $ xbps-bin list\n"
@@ -101,13 +100,10 @@ main(int argc, char **argv)
 {
 	prop_dictionary_t dict;
 	int c, flags = 0, rv = 0;
-	bool chkhash = false, force = false, verbose = false;
+	bool force = false, verbose = false;
 
 	while ((c = getopt(argc, argv, "Cfr:v")) != -1) {
 		switch (c) {
-		case 'C':
-			chkhash = true;
-			break;
 		case 'f':
 			force = true;
 			break;
@@ -178,7 +174,7 @@ main(int argc, char **argv)
 		if (argc != 2)
 			usage();
 
-		xbps_remove_pkg(argv[1], force);
+		xbps_remove_installed_pkg(argv[1], force);
 
 	} else if (strcasecmp(argv[0], "show") == 0) {
 		/* Shows info about an installed binary package. */
@@ -196,12 +192,19 @@ main(int argc, char **argv)
 		if (argc != 2)
 			usage();
 
-		rv = show_pkg_files_from_metadir(argv[1], chkhash);
+		rv = show_pkg_files_from_metadir(argv[1]);
 		if (rv != 0) {
 			printf("Package %s not installed.\n", argv[1]);
 			exit(EXIT_FAILURE);
 		}
 
+	} else if (strcasecmp(argv[0], "check") == 0) {
+		/* Checks the integrity of an installed package. */
+		if (argc != 2)
+			usage();
+
+		rv = xbps_check_pkg_integrity(argv[1]);
+
 	} else if (strcasecmp(argv[0], "autoupdate") == 0) {
 		/*
 		 * To update all packages currently installed.
diff --git a/bin/xbps-bin/remove.c b/bin/xbps-bin/remove.c
index 5237b661bda..2a990e7eef4 100644
--- a/bin/xbps-bin/remove.c
+++ b/bin/xbps-bin/remove.c
@@ -96,7 +96,7 @@ xbps_autoremove_pkgs(void)
 		prop_dictionary_get_cstring_nocopy(obj, "version", &version);
 
 		printf("Removing package %s-%s ...\n", pkgname, version);
-		if ((rv = xbps_remove_binary_pkg(pkgname, false)) != 0)
+		if ((rv = xbps_remove_pkg(pkgname, version, false)) != 0)
 			goto out2;
 	}
 out2:
@@ -110,7 +110,7 @@ out:
 }
 
 void
-xbps_remove_pkg(const char *pkgname, bool force)
+xbps_remove_installed_pkg(const char *pkgname, bool force)
 {
 	prop_array_t reqby;
 	prop_dictionary_t dict;
@@ -151,7 +151,7 @@ xbps_remove_pkg(const char *pkgname, bool force)
 	}
 
 	printf("Removing package %s-%s ...\n", pkgname, version);
-	if ((rv = xbps_remove_binary_pkg(pkgname, false)) != 0) {
+	if ((rv = xbps_remove_pkg(pkgname, version, false)) != 0) {
 		printf("Unable to remove %s-%s (%s).\n",
 		    pkgname, version, strerror(errno));
 		goto out;
diff --git a/bin/xbps-pkgdb/main.c b/bin/xbps-pkgdb/main.c
index dbae941cf88..012dd33a41a 100644
--- a/bin/xbps-pkgdb/main.c
+++ b/bin/xbps-pkgdb/main.c
@@ -139,7 +139,12 @@ main(int argc, char **argv)
 		prop_dictionary_set_cstring_nocopy(dict, "version", argv[2]);
 		prop_dictionary_set_cstring_nocopy(dict, "short_desc", argv[3]);
 
-		rv = xbps_register_pkg(dict, false, automatic);
+		rv = xbps_set_pkg_state_installed(argv[1],
+		    XBPS_PKG_STATE_INSTALLED);
+		if (rv != 0)
+			exit(EXIT_FAILURE);
+
+		rv = xbps_register_pkg(dict, automatic);
 		if (rv == EEXIST) {
 			printf("%s=> %s-%s already registered.\n",
 			    in_chroot ? "[chroot] " : "", argv[1], argv[2]);
diff --git a/bin/xbps-repo/index.c b/bin/xbps-repo/index.c
index 1f93749fbdc..6bbe929789a 100644
--- a/bin/xbps-repo/index.c
+++ b/bin/xbps-repo/index.c
@@ -36,7 +36,7 @@
 #include "index.h"
 
 /* Array of valid architectures */
-const char *archdirs[] = { "i686", "x86_64", "noarch", NULL };
+static const char *archdirs[] = { "i686", "x86_64", "noarch", NULL };
 
 static prop_dictionary_t
 repoidx_getdict(const char *pkgdir)
@@ -52,16 +52,13 @@ repoidx_getdict(const char *pkgdir)
 	dict = prop_dictionary_internalize_from_file(plist);
 	if (dict == NULL) {
 		dict = prop_dictionary_create();
-		if (dict == NULL) {
-			free(plist);
-			return NULL;
-		}
+		if (dict == NULL)
+			goto out;
 
 		array = prop_array_create();
 		if (array == NULL) {
-			free(plist);
 			prop_object_release(dict);
-			return NULL;
+			goto out;
 		}
 
 		prop_dictionary_set(dict, "packages", array);
@@ -71,6 +68,7 @@ repoidx_getdict(const char *pkgdir)
 		prop_dictionary_set_cstring_nocopy(dict,
 		    "pkgindex-version", XBPS_PKGINDEX_VERSION);
 	}
+out:
 	free(plist);
 
 	return dict;
@@ -84,38 +82,36 @@ repoidx_addpkg(const char *file, const char *filename, const char *pkgdir)
 	struct archive *ar;
 	struct archive_entry *entry;
 	struct stat st;
-	ssize_t nbytes = -1;
 	const char *pkgname, *version, *regver;
-	char *props, *sha256, *plist;
-	size_t propslen = 0;
+	char *sha256, *plist;
 	int rv = 0;
 
 	ar = archive_read_new();
-	if (ar == NULL)
-		return errno;
-
+	if (ar == NULL) {
+		rv = errno;
+		goto out;
+	}
 	/* Enable support for tar format and all compression methods */
 	archive_read_support_compression_all(ar);
 	archive_read_support_format_tar(ar);
 
 	if ((rv = archive_read_open_filename(ar, file,
 	     ARCHIVE_READ_BLOCKSIZE)) == -1) {
-		archive_read_finish(ar);
-		return errno;
+		rv = errno;
+		goto out1;
 	}
 
 	/* Get existing or create repo index dictionary */
 	idxdict = repoidx_getdict(pkgdir);
 	if (idxdict == NULL) {
-		archive_read_finish(ar);
-		return errno;
+		rv = errno;
+		goto out1;
 	}
 	plist = xbps_get_pkg_index_plist(pkgdir);
 	if (plist == NULL) {
 		prop_dictionary_remove(idxdict, "packages");
-		prop_object_release(idxdict);
-		archive_read_finish(ar);
-		return ENOMEM;
+		rv = ENOMEM;
+		goto out2;
 	}
 
 	/*
@@ -123,32 +119,15 @@ repoidx_addpkg(const char *file, const char *filename, const char *pkgdir)
 	 * into a buffer.
 	 */
 	while (archive_read_next_header(ar, &entry) == ARCHIVE_OK) {
-		if (strstr(archive_entry_pathname(entry), "props.plist") == 0) {
+		if (strstr(archive_entry_pathname(entry), XBPS_PKGPROPS) == 0) {
 			archive_read_data_skip(ar);
 			continue;
 		}
-
-		propslen = (size_t)archive_entry_size(entry);
-		props = malloc(propslen);
-		if (props == NULL) {
-			rv = errno;
-			break;
-		}
-		nbytes = archive_read_data(ar, props, propslen);
-		if ((size_t)nbytes != propslen) {
-			rv = EINVAL;
-			break;
-		}
-		newpkgd = prop_dictionary_internalize(props);
-		free(props);
-		propslen = 0;
+		newpkgd = xbps_read_dict_from_archive_entry(ar, entry);
 		if (newpkgd == NULL) {
-			archive_read_data_skip(ar);
-			continue;
-		} else if (prop_object_type(newpkgd) != PROP_TYPE_DICTIONARY) {
-			prop_object_release(newpkgd);
-			archive_read_data_skip(ar);
-			continue;
+			printf("%s: can't read %s metadata file, skipping!\n",
+			    file, XBPS_PKGPROPS);
+			break;
 		}
 
 		prop_dictionary_get_cstring_nocopy(newpkgd, "pkgname",
@@ -233,10 +212,13 @@ repoidx_addpkg(const char *file, const char *filename, const char *pkgdir)
 		    pkgname, version);
 		break;
 	}
-	archive_read_finish(ar);
-	free(plist);
-	prop_object_release(idxdict);
 
+	free(plist);
+out2:
+	prop_object_release(idxdict);
+out1:
+	archive_read_finish(ar);
+out:
 	return rv;
 }
 
@@ -253,7 +235,6 @@ xbps_repo_genindex(const char *pkgdir)
 
 	if (uname(&un) == -1)
 		return errno;
-
 	/*
 	 * Iterate over the known architecture directories to find
 	 * binary packages.
@@ -296,7 +277,6 @@ xbps_repo_genindex(const char *pkgdir)
 				free(path);
 				return rv;
 			}
-
 		}
 		(void)closedir(dirp);
 		free(path);
diff --git a/bin/xbps-repo/util.c b/bin/xbps-repo/util.c
index 1a720d39e5c..50ddd2cf8b6 100644
--- a/bin/xbps-repo/util.c
+++ b/bin/xbps-repo/util.c
@@ -197,14 +197,14 @@ show_pkg_info_from_metadir(const char *pkgname)
 }
 
 int
-show_pkg_files_from_metadir(const char *pkgname, bool hash)
+show_pkg_files_from_metadir(const char *pkgname)
 {
 	prop_dictionary_t pkgd;
 	prop_array_t array;
 	prop_object_iterator_t iter = NULL;
 	prop_object_t obj;
-	const char *destdir, *file, *sha256;
-	char *plist, *path = NULL, *array_str = "files";
+	const char *destdir, *file;
+	char *plist, *array_str = "files";
 	int i, rv = 0;
 
 	destdir = xbps_get_rootdir();
@@ -233,7 +233,6 @@ show_pkg_files_from_metadir(const char *pkgname, bool hash)
 			printf("%s\n", file);
 		}
 		prop_object_iterator_release(iter);
-		iter = NULL;
 	}
 
 	/* Files and configuration files. */
@@ -254,41 +253,10 @@ show_pkg_files_from_metadir(const char *pkgname, bool hash)
 		}
 		while ((obj = prop_object_iterator_next(iter))) {
 			prop_dictionary_get_cstring_nocopy(obj, "file", &file);
-			if (hash == false) {
-				printf("%s\n", file);
-				continue;
-			}
-
-			printf("%s", file);
-			if (destdir) {
-				path = xbps_xasprintf("%s/%s", destdir, file);
-				if (path == NULL) {
-					rv = EINVAL;
-					goto out2;
-				}
-			}
-			prop_dictionary_get_cstring_nocopy(obj,
-			    "sha256", &sha256);
-			if (destdir)
-				rv = xbps_check_file_hash(path, sha256);
-			else
-				rv = xbps_check_file_hash(file, sha256);
-
-			if (rv != 0 && rv != ERANGE)
-				printf(" (can't check: %s)", strerror(rv));
-			else if (rv == ERANGE)
-				printf("  WARNING! SHA256 HASH MISMATCH!");
-
-			printf("\n");
-			if (destdir)
-				free(path);
+			printf("%s\n", file);
 		}
 		prop_object_iterator_release(iter);
-		iter = NULL;
 	}
-out2:
-	if (iter != NULL)
-		prop_object_iterator_release(iter);
 out:
 	prop_object_release(pkgd);
 
diff --git a/bin/xbps-repo/util.h b/bin/xbps-repo/util.h
index a4d4ce61533..92f5db2c110 100644
--- a/bin/xbps-repo/util.h
+++ b/bin/xbps-repo/util.h
@@ -28,7 +28,7 @@
 
 int	search_string_in_pkgs(prop_object_t, void *, bool *);
 int	show_pkg_info_from_metadir(const char *);
-int	show_pkg_files_from_metadir(const char *, bool);
+int	show_pkg_files_from_metadir(const char *);
 int	show_pkg_info_from_repolist(prop_object_t, void *, bool *);
 int	list_strings_in_array(prop_object_t, void *, bool *);
 int	list_strings_sep_in_array(prop_object_t, void *, bool *);
diff --git a/doc/BINPKG_INFO b/doc/BINPKG_INFO
index 22932598329..0c23d55f02c 100644
--- a/doc/BINPKG_INFO
+++ b/doc/BINPKG_INFO
@@ -5,22 +5,27 @@
 A binary package built with xbps is a normal tar(1) archive, compressed
 with bzip2 and has the following structure:
 
-	/
-	/usr ------|
-	/var ------| => Package structure that will be installed.
-	/etc ------|
+	Package metadata
+	-----------------
+	/INSTALL
+	/REMOVE
+	/files.plist
+	/props.plist
+
+	Package data
+	-----------------
+	/usr
+	/var
+	/etc
 	...
-	/var/db/xbps/metadata/$pkgname
-	/var/db/xbps/metadata/$pkgname/files.plist
-	/var/db/xbps/metadata/$pkgname/props.plist
-	/var/db/xbps/metadata/$pkgname/INSTALL
-	/var/db/xbps/metadata/$pkgname/REMOVE
 
 Metadata info is stored in the "/var/db/xbps/metadata/$pkgname"
-directory and two files will be always be present: flist and props.plist.
+directory and two files will be always be present: files.plist
+and props.plist.
 
 The files.plist file contains the list of files/links/dirs that package
 will install, as well as SHA256 hashes for files.
+
 The props.plist file contains package metadata properties and has the
 following structure:
 
diff --git a/doc/TODO b/doc/TODO
index 557e6d8ba16..86b00837cec 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -29,13 +29,9 @@ xbps-bin:
  * Implement shell style match patterns with fnmatch().
  * Make -f flag to overwrite files when installing, and to ignore
    files with wrong checksum or unexistent when removing.
- * Add a target to check pkg integrity: normal files, metadata
-   and its dependencies.
 
 libxbps:
  * Fix glibc updates: removing libc is bad.
- * Create xbps_upgrade_pkg() to replace or remove/unpack and register.
-   Remove duplicate code from xbps-bin/install.c. [IN PROGRESS]
  * Add support to upgrade packages but overwritting current files;
    this will fix libc, sh and others. An "essential" boolean obj
    seems to be a good way to find such packages, like dpkg. [IN PROGRESS]
diff --git a/lib/Makefile b/lib/Makefile
index 24fc4b73167..787f482555d 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -9,9 +9,10 @@ LIBXBPS		= libxbps.so
 LIBXBPS_STATIC 	= libxbps.a
 LIBXBPS_LDFLAGS	= -larchive -lprop -shared -Wl,-soname,$(LIBXBPS).$(MAJOR)
 
-OBJECTS	=	cmpver.o depends.o fexec.o findpkg.o humanize_number.o
-OBJECTS	+=	orphans.o plist.o register.o remove.o repository.o requiredby.o
-OBJECTS	+=	sha256.o sortdeps.o unpack.o util.o
+OBJECTS	=	configure.o cmpver.o depends.o fexec.o findpkg.o
+OBJECTS	+=	humanize_number.o orphans.o plist.o register.o remove.o
+OBJECTS +=	repository.o requiredby.o sha256.o sortdeps.o state.o
+OBJECTS	+=	unpack.o util.o
 
 all: $(LIBXBPS) $(LIBXBPS_STATIC)
 .PHONY: all
diff --git a/lib/configure.c b/lib/configure.c
new file mode 100644
index 00000000000..7b301aaba96
--- /dev/null
+++ b/lib/configure.c
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 2009 Juan Romero Pardines.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include <xbps_api.h>
+
+/*
+ * Configure a package that is currently unpacked. This
+ * runs the post INSTALL action if required and updates the
+ * package state to installed.
+ */
+int
+xbps_configure_pkg(const char *pkgname, const char *version)
+{
+	const char *rootdir;
+	char *buf;
+	int rv = 0;
+	pkg_state_t state = 0;
+
+	assert(pkgname != NULL);
+	assert(version != NULL);
+	rootdir = xbps_get_rootdir();
+
+	if ((rv = xbps_get_pkg_state_installed(pkgname, &state)) != 0)
+		return rv;
+
+	/*
+	 * If package is already installed do nothing, and only
+	 * continue if it's unpacked.
+	 */
+	if (state == XBPS_PKG_STATE_INSTALLED)
+		return 0;
+	else if (state != XBPS_PKG_STATE_UNPACKED)
+		return EINVAL;
+
+	buf = xbps_xasprintf(".%s/metadata/%s/INSTALL",
+	    XBPS_META_PATH, pkgname);
+	if (buf == NULL)
+		return errno;
+
+	if (access(buf, R_OK) == 0) {
+		if (chdir(rootdir) == -1)
+			return errno;
+
+		if ((rv = xbps_file_chdir_exec(rootdir, buf, "post",
+		     pkgname, version, NULL)) != 0) {
+			free(buf);
+			printf("%s: post INSTALL action returned: %s\n",
+			    pkgname, strerror(errno));
+			return rv;
+		}
+	} else {
+		if (errno != ENOENT) {
+			free(buf);
+			return errno;
+		}
+	}
+	free(buf);
+
+	return xbps_set_pkg_state_installed(pkgname, XBPS_PKG_STATE_INSTALLED);
+}
diff --git a/lib/depends.c b/lib/depends.c
index 7fb4d04c4b7..023711e5d39 100644
--- a/lib/depends.c
+++ b/lib/depends.c
@@ -32,29 +32,27 @@
 
 #include <xbps_api.h>
 
-static int	add_missing_reqdep(prop_dictionary_t, const char *,
-				   const char *);
-static int	find_repo_deps(prop_dictionary_t, prop_dictionary_t,
-			       prop_dictionary_t, prop_array_t);
-static int 	find_repo_missing_deps(prop_dictionary_t, prop_dictionary_t,
-				       prop_dictionary_t);
+static int add_missing_reqdep(prop_dictionary_t, const char *, const char *);
+static int find_repo_deps(prop_dictionary_t, prop_dictionary_t, prop_array_t);
+static int find_repo_missing_deps(prop_dictionary_t, prop_dictionary_t);
 
 static int
-store_dependency(prop_dictionary_t master, prop_dictionary_t origind,
-		 prop_dictionary_t depd, prop_dictionary_t repod)
+store_dependency(prop_dictionary_t master, prop_dictionary_t depd,
+		 prop_dictionary_t repod)
 {
 	prop_dictionary_t dict;
 	prop_array_t array;
-	const char *reqbyname, *repoloc;
+	const char *repoloc, *pkgname;
+	int rv = 0;
+	pkg_state_t state = 0;
 
-	assert(origind != NULL);
+	assert(master != NULL);
 	assert(depd != NULL);
 	assert(repod != NULL);
-
 	/*
 	 * Get some info about dependencies and current repository.
 	 */
-	prop_dictionary_get_cstring_nocopy(origind, "pkgname", &reqbyname);
+	prop_dictionary_get_cstring_nocopy(depd, "pkgname", &pkgname);
 	prop_dictionary_get_cstring_nocopy(repod, "location-local", &repoloc);
 
 	dict = prop_dictionary_copy(depd);
@@ -66,6 +64,26 @@ store_dependency(prop_dictionary_t master, prop_dictionary_t origind,
 		prop_object_release(dict);
 		return errno;
 	}
+	/*
+	 * Always set "not-installed" package state. Will be overwritten
+	 * to its correct state later.
+	 */
+	rv = xbps_set_pkg_state_dictionary(dict, XBPS_PKG_STATE_NOT_INSTALLED);
+	if (rv != 0) {
+		prop_object_release(dict);
+		return rv;
+	}
+	/*
+	 * Overwrite package state in dictionary if it was unpacked
+	 * previously.
+	 */
+	rv = xbps_get_pkg_state_installed(pkgname, &state);
+	if (rv == 0) {
+		if ((rv = xbps_set_pkg_state_dictionary(dict, state)) != 0) {
+			prop_object_release(dict);
+			return rv;
+		}
+	}
 	/*
 	 * Add required objects into package dep's dictionary.
 	 */
@@ -132,8 +150,7 @@ xbps_find_deps_in_pkg(prop_dictionary_t master, prop_dictionary_t pkg)
 	assert(iter != NULL);
 
 	pkg_rdeps = prop_dictionary_get(pkg, "run_depends");
-	if (pkg_rdeps == NULL)
-		return 0;
+	assert(pkg_rdeps != NULL);
 
 	/*
 	 * Iterate over the repository pool and find out if we have
@@ -145,7 +162,7 @@ xbps_find_deps_in_pkg(prop_dictionary_t master, prop_dictionary_t pkg)
 		 * if any of them is not there it will be added
 		 * into the missing_deps array.
 		 */
-		rv = find_repo_deps(master, rdata->rd_repod, pkg, pkg_rdeps);
+		rv = find_repo_deps(master, rdata->rd_repod, pkg_rdeps);
 		if (rv != 0) {
 			if (rv == ENOENT) {
 				rv = 0;
@@ -164,7 +181,7 @@ xbps_find_deps_in_pkg(prop_dictionary_t master, prop_dictionary_t pkg)
 	 * just in case that indirect deps weren't found.
 	 */
 	SIMPLEQ_FOREACH(rdata, &repodata_queue, chain) {
-		rv = find_repo_missing_deps(master, rdata->rd_repod, pkg);
+		rv = find_repo_missing_deps(master, rdata->rd_repod);
 		if (rv != 0 && rv != ENOENT)
 			return rv;
 	}
@@ -173,8 +190,7 @@ xbps_find_deps_in_pkg(prop_dictionary_t master, prop_dictionary_t pkg)
 }
 
 static int
-find_repo_missing_deps(prop_dictionary_t master, prop_dictionary_t repo,
-		       prop_dictionary_t pkg)
+find_repo_missing_deps(prop_dictionary_t master, prop_dictionary_t repo)
 {
 	prop_array_t array;
 	prop_dictionary_t curpkgd;
@@ -214,7 +230,7 @@ find_repo_missing_deps(prop_dictionary_t master, prop_dictionary_t repo,
 		/*
 		 * Package is on repo, add it into the dictionary.
 		 */
-		if ((rv = store_dependency(master, pkg, curpkgd, repo)) != 0)
+		if ((rv = store_dependency(master, curpkgd, repo)) != 0)
 			break;
 		/*
 		 * Remove package from missing_deps array now.
@@ -233,9 +249,9 @@ find_repo_missing_deps(prop_dictionary_t master, prop_dictionary_t repo,
 
 static int
 find_repo_deps(prop_dictionary_t master, prop_dictionary_t repo,
-	       prop_dictionary_t pkg, prop_array_t pkg_rdeps)
+	       prop_array_t pkg_rdeps)
 {
-	prop_dictionary_t curpkgd;
+	prop_dictionary_t curpkgd, tmpd = NULL;
 	prop_array_t curpkg_rdeps;
 	prop_object_t obj;
 	prop_object_iterator_t iter;
@@ -285,10 +301,24 @@ find_repo_deps(prop_dictionary_t master, prop_dictionary_t repo,
 				continue;
 			}
 		}
+		/*
+		 * If package is installed but version doesn't satisfy
+		 * the dependency mark it as an update, otherwise as
+		 * an install.
+		 */
+		tmpd = xbps_find_pkg_installed_from_plist(pkgname);
+		if (tmpd != NULL) {
+			prop_dictionary_set_cstring_nocopy(curpkgd,
+			    "trans-action", "update");
+			prop_object_release(tmpd);
+		} else {
+			prop_dictionary_set_cstring_nocopy(curpkgd,
+			    "trans-action", "install");
+		}
 		/*
 		 * Package is on repo, add it into the dictionary.
 		 */
-		if ((rv = store_dependency(master, pkg, curpkgd, repo)) != 0) {
+		if ((rv = store_dependency(master, curpkgd, repo)) != 0) {
 			free(pkgname);
 			break;
 		}
@@ -311,7 +341,7 @@ find_repo_deps(prop_dictionary_t master, prop_dictionary_t repo,
 		/*
 		 * Iterate on required pkg to find more deps.
 		 */
-		if (!find_repo_deps(master, repo, curpkgd, curpkg_rdeps))
+		if (!find_repo_deps(master, repo, curpkg_rdeps))
 			continue;
 	}
 	prop_object_iterator_release(iter);
diff --git a/lib/findpkg.c b/lib/findpkg.c
index d7015027c99..f9598233e29 100644
--- a/lib/findpkg.c
+++ b/lib/findpkg.c
@@ -91,7 +91,7 @@ xbps_get_pkg_props(void)
 	if (pkg_props_initialized == false)
 		return NULL;
 
-	return prop_dictionary_copy(pkg_props);
+	return pkg_props;
 }
 
 int
@@ -134,13 +134,13 @@ xbps_prepare_repolist_data(void)
 	array = prop_dictionary_get(dict, "repository-list");
 	if (array == NULL) {
 		rv = EINVAL;
-		goto out;
+		goto out1;
 	}
 
 	iter = prop_array_iterator(array);
 	if (iter == NULL) {
 		rv = ENOMEM;
-		goto out;
+		goto out1;
 	}
 
 	while ((obj = prop_object_iterator_next(iter)) != NULL) {
@@ -175,8 +175,9 @@ xbps_prepare_repolist_data(void)
 
 out2:
 	prop_object_iterator_release(iter);
-out:
+out1:
 	prop_object_release(dict);
+out:
 	if (rv != 0)
 		xbps_release_repolist_data();
 
@@ -283,6 +284,7 @@ xbps_prepare_pkg(const char *pkgname)
 	struct repository_data *rdata;
 	const char *repoloc;
 	int rv = 0;
+	pkg_state_t state = 0;
 
 	assert(pkgname != NULL);
 
@@ -364,6 +366,24 @@ xbps_prepare_pkg(const char *pkgname)
 		goto out;
 	}
 
+	/*
+	 * Always set "not-installed" package state. Will be overwritten
+	 * to its correct state later.
+	 */
+	rv = xbps_set_pkg_state_dictionary(pkgrd, XBPS_PKG_STATE_NOT_INSTALLED);
+	if (rv != 0)
+		goto out;
+
+	/*
+	 * Overwrite package state in dictionary if it was unpacked
+	 * previously.
+	 */
+	rv = xbps_get_pkg_state_installed(pkgname, &state);
+	if (rv == 0) {
+		if ((rv = xbps_set_pkg_state_dictionary(pkgrd, state)) != 0)
+			goto out;
+        }
+
 	if (!prop_array_add(pkgs_array, pkgrd))
 		rv = errno;
 
diff --git a/lib/register.c b/lib/register.c
index 534ee99e192..8b778ecc866 100644
--- a/lib/register.c
+++ b/lib/register.c
@@ -34,25 +34,10 @@
 
 #include <xbps_api.h>
 
-static prop_dictionary_t
-make_dict_from_pkg(const char *name, const char *ver, const char *desc)
-{
-	prop_dictionary_t dict;
-
-	dict = prop_dictionary_create();
-	assert(dict != NULL);
-
-	prop_dictionary_set_cstring_nocopy(dict, "pkgname", name);
-	prop_dictionary_set_cstring_nocopy(dict, "version", ver);
-	prop_dictionary_set_cstring_nocopy(dict, "short_desc", desc);
-
-	return dict;
-}
-
 int
-xbps_register_pkg(prop_dictionary_t pkgrd, bool update, bool automatic)
+xbps_register_pkg(prop_dictionary_t pkgrd, bool automatic)
 {
-	prop_dictionary_t dict, pkgd, newpkgd;
+	prop_dictionary_t dict, pkgd;
 	prop_array_t array;
 	const char *pkgname, *version, *desc, *rootdir;
 	char *plist;
@@ -69,93 +54,41 @@ xbps_register_pkg(prop_dictionary_t pkgrd, bool update, bool automatic)
 	prop_dictionary_get_cstring_nocopy(pkgrd, "short_desc", &desc);
 
 	dict = prop_dictionary_internalize_from_file(plist);
-	if (dict == NULL) {
-		/*
-		 * No packages registered yet. Register package into
-		 * the dictionary.
-		 */
-		dict = prop_dictionary_create();
-		if (dict == NULL) {
-			free(plist);
-			return ENOMEM;
-		}
-
-		array = prop_array_create();
-		if (array == NULL) {
-			rv = ENOMEM;
-			goto out;
-		}
-
-		pkgd = make_dict_from_pkg(pkgname, version, desc);
-		if (!xbps_add_obj_to_array(array, pkgd)) {
-			prop_object_release(array);
-			rv = EINVAL;
-			goto out;
-		}
-
-		prop_dictionary_set_bool(pkgd, "automatic-install",
-			automatic);
-
-		if (!xbps_add_obj_to_dict(dict, array, "packages")) {
-			prop_object_release(array);
-			rv = EINVAL;
-			goto out;
-		}
-
-	} else {
-		/*
-		 * Check if package is already registered and return
-		 * an error if not updating.
-		 */
+	if (dict != NULL) {
 		pkgd = xbps_find_pkg_in_dict(dict, "packages", pkgname);
-		if (pkgd != NULL && update == false) {
-			rv = EEXIST;
-			goto out;
-		}
-		array = prop_dictionary_get(dict, "packages");
-		if (array == NULL) {
+		if (pkgd == NULL) {
 			rv = ENOENT;
 			goto out;
 		}
-
-		newpkgd = make_dict_from_pkg(pkgname, version, desc);
-		prop_dictionary_set_bool(newpkgd, "automatic-install",
-		    automatic);
+		prop_dictionary_set_cstring_nocopy(pkgd, "version", version);
+		prop_dictionary_set_cstring_nocopy(pkgd, "short_desc", desc);
+		prop_dictionary_set_bool(pkgd, "automatic-install", automatic);
 
 		/*
 		 * Add the requiredby objects for dependent packages.
 		 */
 		if (pkgrd && xbps_pkg_has_rundeps(pkgrd)) {
+			array = prop_dictionary_get(dict, "packages");
+			if (array == NULL) {
+				prop_object_release(pkgd);
+				rv = ENOENT;
+				goto out;
+			}
 			rv = xbps_requiredby_pkg_add(array, pkgrd);
 			if (rv != 0) {
-				prop_object_release(newpkgd);
-				goto out;
-			}
-		}
-
-		if (update) {
-			/*
-			 * If updating a package, set new version in
-			 * pkg dictionary.
-			 */
-			prop_dictionary_set_cstring_nocopy(pkgd,
-			    "version", version);
-		} else {
-			/*
-			 * If installing a package, add new pkg
-			 * dictionary into the packages array.
-			 */
-			if (!xbps_add_obj_to_array(array, newpkgd)) {
-				prop_object_release(newpkgd);
-				rv = EINVAL;
+				prop_object_release(pkgd);
 				goto out;
 			}
 		}
+		/*
+		 * Write plist file to storage.
+		 */
+		if (!prop_dictionary_externalize_to_file(dict, plist))
+			rv = errno;
+	} else {
+		free(plist);
+		return ENOENT;
 	}
-
-	if (!prop_dictionary_externalize_to_file(dict, plist))
-		rv = errno;
-
 out:
 	prop_object_release(dict);
 	free(plist);
diff --git a/lib/remove.c b/lib/remove.c
index efa1d7b1ba6..25413662403 100644
--- a/lib/remove.c
+++ b/lib/remove.c
@@ -60,7 +60,7 @@ xbps_unregister_pkg(const char *pkgname)
 }
 
 static int
-xbps_remove_binary_pkg_meta(const char *pkgname)
+remove_pkg_metadir(const char *pkgname)
 {
 	struct dirent *dp;
 	DIR *dirp;
@@ -271,7 +271,7 @@ dirs:
 }
 
 int
-xbps_remove_binary_pkg(const char *pkgname, bool update)
+xbps_remove_pkg(const char *pkgname, const char *version, bool update)
 {
 	prop_dictionary_t dict;
 	const char *rootdir = xbps_get_rootdir();
@@ -280,6 +280,7 @@ xbps_remove_binary_pkg(const char *pkgname, bool update)
 	bool prepostf = false;
 
 	assert(pkgname != NULL);
+	assert(version != NULL);
 
 	/*
 	 * Check if pkg is installed before anything else.
@@ -287,9 +288,6 @@ xbps_remove_binary_pkg(const char *pkgname, bool update)
 	if (xbps_check_is_installed_pkgname(pkgname) == false)
 		return ENOENT;
 
-	if (strcmp(rootdir, "") == 0)
-		rootdir = "/";
-
 	if (chdir(rootdir) == -1)
 		return errno;
 
@@ -306,7 +304,8 @@ xbps_remove_binary_pkg(const char *pkgname, bool update)
 		 * Run the pre remove action.
 		 */
 		prepostf = true;
-		rv = xbps_file_chdir_exec(rootdir, buf, "pre", pkgname, NULL);
+		rv = xbps_file_chdir_exec(rootdir, buf, "pre", pkgname,
+		    version, NULL);
 		if (rv != 0) {
 			printf("%s: prerm action target error (%s)\n", pkgname,
 			    strerror(errno));
@@ -317,10 +316,10 @@ xbps_remove_binary_pkg(const char *pkgname, bool update)
 
 	/*
 	 * Iterate over the pkg file list dictionary and remove all
-	 * files/dirs associated.
+	 * files, configuration files, links and dirs.
 	 */
-	path = xbps_xasprintf("%s/%s/metadata/%s/files.plist",
-	    rootdir, XBPS_META_PATH, pkgname);
+	path = xbps_xasprintf("%s/%s/metadata/%s/%s",
+	    rootdir, XBPS_META_PATH, pkgname, XBPS_PKGFILES);
 	if (path == NULL) {
 		free(buf);
 		return errno;
@@ -343,11 +342,12 @@ xbps_remove_binary_pkg(const char *pkgname, bool update)
 	prop_object_release(dict);
 
 	/*
-	 * Run the post remove action if REMOVE file is there.
+	 * Run the post remove action if REMOVE file is there
+	 * and we aren't updating a package.
 	 */
-	if (prepostf) {
+	if (update == false && prepostf) {
 		if ((rv = xbps_file_chdir_exec(rootdir, buf, "post",
-		     pkgname, NULL)) != 0) {
+		     pkgname, version, NULL)) != 0) {
 			printf("%s: postrm action target error (%s)\n",
 			    pkgname, strerror(errno));
 			free(buf);
@@ -365,16 +365,17 @@ xbps_remove_binary_pkg(const char *pkgname, bool update)
 
 	if (update == false) {
 		/*
-		 * Unregister pkg from database only if it's a removal
-		 * and not an update.
+		 * Unregister pkg from database.
 		 */
 		rv = xbps_unregister_pkg(pkgname);
 		if (rv != 0)
 			return rv;
+
+		/*
+		 * Remove pkg metadata directory.
+		 */
+		return remove_pkg_metadir(pkgname);
 	}
 
-	/*
-	 * Remove pkg metadata directory.
-	 */
-	return xbps_remove_binary_pkg_meta(pkgname);
+	return 0;
 }
diff --git a/lib/state.c b/lib/state.c
new file mode 100644
index 00000000000..18b544be942
--- /dev/null
+++ b/lib/state.c
@@ -0,0 +1,235 @@
+/*-
+ * Copyright (c) 2009 Juan Romero Pardines.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <xbps_api.h>
+
+static int
+set_new_state(prop_dictionary_t dict, pkg_state_t state)
+{
+	const char *state_str;
+
+	assert(dict != NULL);
+
+	switch (state) {
+	case XBPS_PKG_STATE_UNPACKED:
+		state_str = "unpacked";
+		break;
+	case XBPS_PKG_STATE_INSTALLED:
+		state_str = "installed";
+		break;
+	case XBPS_PKG_STATE_BROKEN:
+		state_str = "broken";
+		break;
+	case XBPS_PKG_STATE_CONFIG_FILES:
+		state_str = "config-files";
+		break;
+	case XBPS_PKG_STATE_NOT_INSTALLED:
+		state_str = "not-installed";
+		break;
+	default:
+		return -1;
+	}
+
+	if (!prop_dictionary_set_cstring_nocopy(dict, "state", state_str))
+		return -1;
+
+	return 0;
+}
+
+static pkg_state_t
+get_state(prop_dictionary_t dict)
+{
+	const char *state_str;
+	pkg_state_t state = 0;
+
+	assert(dict != NULL);
+
+	prop_dictionary_get_cstring_nocopy(dict, "state", &state_str);
+	if (state_str == NULL)
+		return 0;
+
+	if (strcmp(state_str, "unpacked") == 0)
+		state = XBPS_PKG_STATE_UNPACKED;
+	else if (strcmp(state_str, "installed") == 0)
+		state = XBPS_PKG_STATE_INSTALLED;
+	else if (strcmp(state_str, "broken") == 0)
+		state = XBPS_PKG_STATE_BROKEN;
+	else if (strcmp(state_str, "config-files") == 0)
+		state = XBPS_PKG_STATE_CONFIG_FILES;
+	else if (strcmp(state_str, "not-installed") == 0)
+		state = XBPS_PKG_STATE_NOT_INSTALLED;
+	else
+		return 0;
+
+	return state;
+}
+
+int
+xbps_get_pkg_state_installed(const char *pkgname, pkg_state_t *state)
+{
+	prop_dictionary_t dict, pkgd;
+	const char *rootdir;
+	char *plist;
+
+	assert(pkgname != NULL);
+	rootdir = xbps_get_rootdir();
+	plist = xbps_xasprintf("%s/%s/%s", rootdir,
+	    XBPS_META_PATH, XBPS_REGPKGDB);
+	if (plist == NULL)
+		return errno;
+
+	dict = prop_dictionary_internalize_from_file(plist);
+	if (dict == NULL) {
+		free(plist);
+		return errno;
+	}
+	free(plist);
+
+	pkgd = xbps_find_pkg_in_dict(dict, "packages", pkgname);
+	if (pkgd == NULL) {
+		prop_object_release(dict);
+		return ENOENT;
+	}
+	*state = get_state(pkgd);
+	if (*state == 0) {
+		prop_object_release(dict);
+		return EINVAL;
+	}
+	prop_object_release(dict);
+
+	return 0;
+}
+
+int
+xbps_get_pkg_state_dictionary(prop_dictionary_t dict, pkg_state_t *state)
+{
+	assert(dict != NULL);
+
+	if ((*state = get_state(dict)) == 0)
+		return EINVAL;
+
+	return 0;
+}
+
+int
+xbps_set_pkg_state_dictionary(prop_dictionary_t dict, pkg_state_t state)
+{
+	assert(dict != NULL);
+
+	return set_new_state(dict, state);
+}
+
+int
+xbps_set_pkg_state_installed(const char *pkgname, pkg_state_t state)
+{
+	prop_dictionary_t dict, pkgd;
+	prop_array_t array;
+	const char *rootdir;
+	char *plist;
+	int rv = 0;
+	bool newpkg = false;
+
+	rootdir = xbps_get_rootdir();
+	plist = xbps_xasprintf("%s/%s/%s", rootdir,
+	    XBPS_META_PATH, XBPS_REGPKGDB);
+	if (plist == NULL)
+		return EINVAL;
+
+	dict = prop_dictionary_internalize_from_file(plist);
+	if (dict == NULL) {
+		dict = prop_dictionary_create();
+		if (dict == NULL) {
+			free(plist);
+			return ENOMEM;
+		}
+		array = prop_array_create();
+		if (array == NULL) {
+			rv = ENOMEM;
+			goto out;
+		}
+		pkgd = prop_dictionary_create();
+		if (pkgd == NULL) {
+			prop_object_release(array);
+			rv = errno;
+			goto out;
+		}
+		prop_dictionary_set_cstring_nocopy(pkgd, "pkgname", pkgname);
+		if ((rv = set_new_state(pkgd, state)) != 0) {
+			prop_object_release(array);
+			goto out;
+		}
+		if (!xbps_add_obj_to_array(array, pkgd)) {
+			prop_object_release(array);
+			rv = EINVAL;
+			goto out;
+		}
+		if (!xbps_add_obj_to_dict(dict, array, "packages")) {
+			prop_object_release(array);
+			rv = EINVAL;
+			goto out;
+		}
+
+	} else {
+		pkgd = xbps_find_pkg_in_dict(dict, "packages", pkgname);
+		if (pkgd == NULL) {
+			newpkg = true;
+			pkgd = prop_dictionary_create();
+			prop_dictionary_set_cstring_nocopy(pkgd, "pkgname",
+			    pkgname);
+		}
+		array = prop_dictionary_get(dict, "packages");
+		if (array == NULL) {
+			rv = ENOENT;
+			goto out;
+		}
+		if ((rv = set_new_state(pkgd, state)) != 0) {
+			prop_object_release(pkgd);
+			goto out;
+		}
+		if (newpkg && !xbps_add_obj_to_array(array, pkgd)) {
+			prop_object_release(pkgd);
+			rv = EINVAL;
+			goto out;
+		}
+	}
+
+	if (!prop_dictionary_externalize_to_file(dict, plist))
+		rv = errno;
+
+out:
+	prop_object_release(dict);
+	free(plist);
+
+	return rv;
+}
diff --git a/lib/unpack.c b/lib/unpack.c
index d6ead40558d..b33d7e08eb5 100644
--- a/lib/unpack.c
+++ b/lib/unpack.c
@@ -35,13 +35,14 @@
 
 #include <xbps_api.h>
 
-static int unpack_archive_fini(struct archive *, prop_dictionary_t);
+static int unpack_archive_fini(struct archive *, prop_dictionary_t, bool);
 
 int
-xbps_unpack_binary_pkg(prop_dictionary_t pkg)
+xbps_unpack_binary_pkg(prop_dictionary_t pkg, bool essential)
 {
 	prop_string_t filename, repoloc, arch;
 	struct archive *ar;
+	const char *pkgname;
 	char *binfile;
 	int pkg_fd, rv = 0;
 
@@ -50,6 +51,7 @@ xbps_unpack_binary_pkg(prop_dictionary_t pkg)
 	/*
 	 * Append filename to the full path for binary pkg.
 	 */
+	prop_dictionary_get_cstring_nocopy(pkg, "pkgname", &pkgname);
 	filename = prop_dictionary_get(pkg, "filename");
 	arch = prop_dictionary_get(pkg, "architecture");
 	repoloc = prop_dictionary_get(pkg, "repository");
@@ -74,7 +76,9 @@ xbps_unpack_binary_pkg(prop_dictionary_t pkg)
 		goto out2;
 	}
 
-	/* Enable support for tar format and all compression methods */
+	/*
+	 * Enable support for tar format and all compression methods.
+	 */
 	archive_read_support_compression_all(ar);
 	archive_read_support_format_tar(ar);
 
@@ -82,15 +86,14 @@ xbps_unpack_binary_pkg(prop_dictionary_t pkg)
 	     ARCHIVE_READ_BLOCKSIZE)) != 0)
 		goto out3;
 
-	rv = unpack_archive_fini(ar, pkg);
+	rv = unpack_archive_fini(ar, pkg, essential);
 	/*
 	 * If installation of package was successful, make sure the package
 	 * is really on storage (if possible).
 	 */
 	if (rv == 0)
-		if (fsync(pkg_fd) == -1)
+		if (fdatasync(pkg_fd) == -1)
 			rv = errno;
-
 out3:
 	archive_read_finish(ar);
 out2:
@@ -98,31 +101,39 @@ out2:
 out:
 	free(binfile);
 
+	if (rv == 0) {
+		/*
+		 * Set package state to unpacked.
+		 */
+		rv = xbps_set_pkg_state_installed(pkgname,
+		    XBPS_PKG_STATE_UNPACKED);
+	}
+
 	return rv;
 }
 
 /*
- * Flags for extracting files in binary packages.
+ * Flags for extracting files in binary packages. If a package
+ * is marked as "essential", its files will be overwritten and then
+ * the old and new dictionaries are compared to find out if there
+ * are some files that were in the old package that should be removed.
  */
 #define EXTRACT_FLAGS	ARCHIVE_EXTRACT_SECURE_NODOTDOT | \
-			ARCHIVE_EXTRACT_SECURE_SYMLINKS | \
-			ARCHIVE_EXTRACT_NO_OVERWRITE | \
-			ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER
+			ARCHIVE_EXTRACT_SECURE_SYMLINKS
 #define FEXTRACT_FLAGS	ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_PERM | \
 			ARCHIVE_EXTRACT_TIME | EXTRACT_FLAGS
-
 /*
  * TODO: remove printfs and return appropiate errors to be interpreted by
  * the consumer.
  */
 static int
-unpack_archive_fini(struct archive *ar, prop_dictionary_t pkg)
+unpack_archive_fini(struct archive *ar, prop_dictionary_t pkg,
+		    bool essential)
 {
 	struct archive_entry *entry;
-	const char *prepost = "./INSTALL";
 	const char *pkgname, *version, *rootdir;
-	char *buf;
-	int rv = 0, flags = 0, lflags = 0;
+	char *buf, *buf2;
+	int rv = 0, flags, lflags;
 	bool actgt = false;
 
 	assert(ar != NULL);
@@ -130,6 +141,10 @@ unpack_archive_fini(struct archive *ar, prop_dictionary_t pkg)
 	rootdir = xbps_get_rootdir();
 	flags = xbps_get_flags();
 
+	/*
+	 * First we change to the destination directory or / if
+	 * not specified.
+	 */
 	if (strcmp(rootdir, "") == 0)
 		rootdir = "/";
 
@@ -144,38 +159,72 @@ unpack_archive_fini(struct archive *ar, prop_dictionary_t pkg)
 	else
 		lflags = EXTRACT_FLAGS;
 
-	buf = xbps_xasprintf(".%s/metadata/%s/INSTALL",
-	    XBPS_META_PATH, pkgname);
-	if (buf == NULL)
-		return errno;
+	if (essential == false) {
+		lflags |= ARCHIVE_EXTRACT_NO_OVERWRITE;
+		lflags |= ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER;
+	}
 
 	while (archive_read_next_header(ar, &entry) == ARCHIVE_OK) {
 		/*
-		 * Run the pre installation action target if there's a script
-		 * before writing data to disk.
+		 * Run the pre INSTALL action if the file is there.
 		 */
-		if (strcmp(prepost, archive_entry_pathname(entry)) == 0) {
-			actgt = true;
-			printf("\n");
-			(void)fflush(stdout);
+		if (strcmp("./INSTALL", archive_entry_pathname(entry)) == 0) {
+			buf = xbps_xasprintf(".%s/metadata/%s/INSTALL",
+			    XBPS_META_PATH, pkgname);
+			if (buf == NULL)
+				return errno;
 
+			actgt = true;
 			archive_entry_set_pathname(entry, buf);
 
 			if (archive_read_extract(ar, entry, lflags) != 0) {
-				if ((rv = archive_errno(ar)) != EEXIST)
-					break;
+				if ((rv = archive_errno(ar)) != EEXIST) {
+					free(buf);
+					return rv;
+				}
 			}
 
 			if ((rv = xbps_file_chdir_exec(rootdir, buf, "pre",
 			     pkgname, version, NULL)) != 0) {
+				free(buf);
 				printf("%s: preinst action target error %s\n",
 				    pkgname, strerror(errno));
-				(void)fflush(stdout);
-				break;
+				return rv;
 			}
-
 			/* pass to the next entry if successful */
+			free(buf);
 			continue;
+
+		/*
+		 * Unpack metadata files (REMOVE, files.plist and props.plist)
+		 * into the proper path.
+		 */
+		} else if (strcmp("./REMOVE",
+		    archive_entry_pathname(entry)) == 0) {
+			buf2 = xbps_xasprintf(".%s/metadata/%s/REMOVE",
+			    XBPS_META_PATH, pkgname);
+			if (buf2 == NULL)
+				return errno;
+			archive_entry_set_pathname(entry, buf2);
+			free(buf2);
+			buf2 = NULL;
+		} else if (strcmp("./files.plist",
+		    archive_entry_pathname(entry)) == 0) {
+			buf2 = xbps_xasprintf(".%s/metadata/%s/files.plist",
+			    XBPS_META_PATH, pkgname);
+			if (buf2 == NULL)
+				return errno;
+			archive_entry_set_pathname(entry, buf2);
+			free(buf2);
+			buf2 = NULL;
+		} else if (strcmp("./props.plist",
+		    archive_entry_pathname(entry)) == 0) {
+			buf2 = xbps_xasprintf(".%s/metadata/%s/props.plist",
+			    XBPS_META_PATH, pkgname);
+			if (buf2 == NULL)
+				return errno;
+			archive_entry_set_pathname(entry, buf2);
+			free(buf2);
 		}
 		/*
 		 * Extract all data from the archive now.
@@ -185,14 +234,12 @@ unpack_archive_fini(struct archive *ar, prop_dictionary_t pkg)
 			if (rv != EEXIST) {
 				printf("ERROR: %s...exiting!\n",
 				    archive_error_string(ar));
-				(void)fflush(stdout);
-				break;
+				return rv;;
 			} else if (rv == EEXIST) {
 				if (flags & XBPS_VERBOSE) {
 					printf("WARNING: ignoring existent "
 					    "path: %s\n",
 					    archive_entry_pathname(entry));
-					(void)fflush(stdout);
 				}
 				rv = 0;
 				continue;
@@ -204,20 +251,5 @@ unpack_archive_fini(struct archive *ar, prop_dictionary_t pkg)
 		}
 	}
 
-	if (rv == 0 && actgt) {
-		/*
-		 * Run the post installaction action target, if package
-		 * contains the script.
-		 */
-		if ((rv = xbps_file_chdir_exec(rootdir, buf, "post",
-		     pkgname, version, NULL)) != 0) {
-			printf("%s: postinst action target error %s\n",
-			    pkgname, strerror(errno));
-			(void)fflush(stdout);
-		}
-	}
-
-	free(buf);
-
 	return rv;
 }
diff --git a/lib/util.c b/lib/util.c
index 03f64859af3..c85e4706da2 100644
--- a/lib/util.c
+++ b/lib/util.c
@@ -116,6 +116,7 @@ xbps_check_is_installed_pkg(const char *pkg)
 	const char *reqver, *instver;
 	char *pkgname;
 	int rv = 0;
+	pkg_state_t state = 0;
 
 	assert(pkg != NULL);
 
@@ -125,16 +126,28 @@ xbps_check_is_installed_pkg(const char *pkg)
 	dict = xbps_find_pkg_installed_from_plist(pkgname);
 	if (dict == NULL) {
 		free(pkgname);
-		return -2; /* not installed */
+		return -1; /* not installed */
 	}
 
+	/*
+	 * Check that package state is fully installed, not
+	 * unpacked or something else.
+	 */
+	if (xbps_get_pkg_state_installed(pkgname, &state) != 0) {
+		free(pkgname);
+		return -1;
+	}
+	free(pkgname);
+
+	if (state != XBPS_PKG_STATE_INSTALLED)
+		return -1;
+
 	/* Get version from installed package */
 	prop_dictionary_get_cstring_nocopy(dict, "version", &instver);
 
 	/* Compare installed and required version. */
 	rv = xbps_cmpver(instver, reqver);
 
-	free(pkgname);
 	prop_object_release(dict);
 
 	return rv;
diff --git a/shutils/make-binpkg.sh b/shutils/make-binpkg.sh
index 1cf1bbf8192..bcd983e5d99 100644
--- a/shutils/make-binpkg.sh
+++ b/shutils/make-binpkg.sh
@@ -51,6 +51,7 @@ xbps_make_binpkg()
 xbps_make_binpkg_real()
 {
 	local binpkg pkgdir arch use_sudo lver
+	local tar_flags="cfp"
 
 	if [ ! -d ${DESTDIR} ]; then
 		echo "$pkgname: unexistent destdir... skipping!"
@@ -79,25 +80,39 @@ xbps_make_binpkg_real()
 	binpkg=$pkgname-$lver.$arch.xbps
 	pkgdir=$XBPS_PACKAGESDIR/$arch
 
+	#
+	# Make sure that INSTALL is the first file on the archive,
+	# this is to ensure that it's run before any other file is
+	# unpacked.
+	#
 	if [ -x ./INSTALL ]; then
-		#
-		# Make sure that INSTALL is the first file on the archive,
-		# this is to ensure that it's run before any other file is
-		# unpacked.
-		#
-		run_rootcmd $use_sudo tar cfp \
-			$XBPS_BUILDDIR/$binpkg ./INSTALL && \
-		run_rootcmd $use_sudo tar rfp $XBPS_BUILDDIR/$binpkg . \
-			--exclude "./INSTALL" \
-			--exclude "./var/db/xbps/metadata/*/flist" && \
-			bzip2 -9 $XBPS_BUILDDIR/$binpkg && \
-			mv $XBPS_BUILDDIR/$binpkg.bz2 $XBPS_BUILDDIR/$binpkg
-	else
-		run_rootcmd $use_sudo tar cfp $XBPS_BUILDDIR/$binpkg . \
-			--exclude "./var/db/xbps/metadata/*/flist" && \
-			bzip2 -9 $XBPS_BUILDDIR/$binpkg && \
-			mv $XBPS_BUILDDIR/$binpkg.bz2 $XBPS_BUILDDIR/$binpkg
+		run_rootcmd $use_sudo tar $tar_flags \
+			$XBPS_BUILDDIR/$binpkg ./INSTALL
+		[ $? -ne 0 ] && msg_error "Failed to add INSTALL script."
 	fi
+	if [ -x ./REMOVE ]; then
+		if [ -x ./INSTALL ]; then
+			tar_flags="rfp"
+		fi
+		run_rootcmd $use_sudo tar $tar_flags \
+			$XBPS_BUILDDIR/$binpkg ./REMOVE
+		[ $? -ne 0 ] && msg_error "Failed to add REMOVE script."
+	fi
+	if [ -x ./INSTALL -o -x ./REMOVE ]; then
+		tar_flags="rfp"
+	elif [ ! -x ./INSTALL -o ! -x ./REMOVE ]; then
+		tar_flags="cfp"
+	fi
+	run_rootcmd $use_sudo tar $tar_flags $XBPS_BUILDDIR/$binpkg \
+		./files.plist ./props.plist
+	[ $? -ne 0 ] && msg_error "Failed to add metadata files."
+
+	run_rootcmd $use_sudo tar rfp $XBPS_BUILDDIR/$binpkg . \
+		--exclude "./INSTALL" --exclude "./REMOVE" \
+		--exclude "./files.plist" --exclude "./props.plist" \
+		--exclude "./var/db/xbps/metadata/*/flist" && \
+		bzip2 -9 $XBPS_BUILDDIR/$binpkg && \
+		mv $XBPS_BUILDDIR/$binpkg.bz2 $XBPS_BUILDDIR/$binpkg
 	if [ $? -eq 0 ]; then
 		[ ! -d $pkgdir ] && mkdir -p $pkgdir
 		mv -f $XBPS_BUILDDIR/$binpkg $pkgdir
diff --git a/shutils/metadata.sh b/shutils/metadata.sh
index 7158074fb1e..e778e63bf94 100644
--- a/shutils/metadata.sh
+++ b/shutils/metadata.sh
@@ -346,12 +346,12 @@ _EOF
 	else
 		rm -f $TMPFLIST
 	fi
-	mv -f $TMPFPLIST $metadir/files.plist
-	mv -f $TMPFPROPS $metadir/props.plist
+	mv -f $TMPFPLIST ${DESTDIR}/files.plist
+	mv -f $TMPFPROPS ${DESTDIR}/props.plist
 
-	$XBPS_REGPKGDB_CMD sanitize-plist $metadir/files.plist
-	$XBPS_REGPKGDB_CMD sanitize-plist $metadir/props.plist
-	chmod 644 $metadir/*
+	$XBPS_REGPKGDB_CMD sanitize-plist ${DESTDIR}/files.plist
+	$XBPS_REGPKGDB_CMD sanitize-plist ${DESTDIR}/props.plist
+	chmod 644 ${DESTDIR}/files.plist ${DESTDIR}/props.plist
 
 	#
 	# Update desktop-file-utils database if package contains
diff --git a/shutils/metadata_scripts.sh b/shutils/metadata_scripts.sh
index 85d7a8c1e10..126c4d65b2c 100644
--- a/shutils/metadata_scripts.sh
+++ b/shutils/metadata_scripts.sh
@@ -26,7 +26,6 @@
 xbps_write_metadata_scripts_pkg()
 {
 	local action="$1"
-	local metadir="${DESTDIR}/var/db/xbps/metadata/$pkgname"
 	local tmpf=$(mktemp -t xbps-install.XXXXXXXXXX) || exit 1
 	local fpattern="s|${DESTDIR}||g;s|^\./$||g;/^$/d"
 	local targets found info_files
@@ -217,7 +216,7 @@ _EOF
 			rm -f $tmpf
 			return 0
 		fi
-		mv $tmpf ${metadir}/REMOVE && chmod 755 ${metadir}/REMOVE
+		mv $tmpf ${DESTDIR}/REMOVE && chmod 755 ${DESTDIR}/REMOVE
 		;;
 	esac
 }