Index: uspace/lib/bithenge/Makefile.linux
===================================================================
--- uspace/lib/bithenge/Makefile.linux	(revision 1c79996e4cd922c6012ceb0b80e17b06cc0ed5e5)
+++ uspace/lib/bithenge/Makefile.linux	(revision a42d7d8a6f7fcbe06ac84827876f2ba001e325a4)
@@ -46,4 +46,13 @@
 	tree.c
 
+ifdef COVERAGE
+	CFLAGS += -fprofile-arcs -ftest-coverage
+endif
+
+ifdef FAILURE
+	CFLAGS += -DBITHENGE_FAILURE_ENABLE=1
+	SOURCES += failure.c
+endif
+
 OBJECTS := $(addsuffix .o,$(basename $(SOURCES)))
 
Index: uspace/lib/bithenge/blob.c
===================================================================
--- uspace/lib/bithenge/blob.c	(revision 1c79996e4cd922c6012ceb0b80e17b06cc0ed5e5)
+++ uspace/lib/bithenge/blob.c	(revision a42d7d8a6f7fcbe06ac84827876f2ba001e325a4)
@@ -461,8 +461,9 @@
 /** Check whether the contents of two blobs are equal.
  * @memberof bithenge_blob_t
+ * @param[out] out Holds whether the blobs are equal.
  * @param a, b Blobs to compare.
- * @return Whether the blobs are equal. If an error occurs, returns false.
- */
-bool bithenge_blob_equal(bithenge_blob_t *a, bithenge_blob_t *b)
+ * @return EOK on success, or an error code from errno.h.
+ */
+int bithenge_blob_equal(bool *out, bithenge_blob_t *a, bithenge_blob_t *b)
 {
 	assert(a);
@@ -476,13 +477,16 @@
 		rc = bithenge_blob_read(a, offset, buffer_a, &size_a);
 		if (rc != EOK)
-			return false;
+			return rc;
 		rc = bithenge_blob_read(b, offset, buffer_b, &size_b);
 		if (rc != EOK)
-			return false;
-		if (size_a != size_b || bcmp(buffer_a, buffer_b, size_a))
-			return false;
+			return rc;
+		if (size_a != size_b || bcmp(buffer_a, buffer_b, size_a)) {
+			*out = false;
+			return EOK;
+		}
 		offset += size_a;
 	} while (size_a == sizeof(buffer_a));
-	return true;
+	*out = true;
+	return EOK;
 }
 
Index: uspace/lib/bithenge/blob.h
===================================================================
--- uspace/lib/bithenge/blob.h	(revision 1c79996e4cd922c6012ceb0b80e17b06cc0ed5e5)
+++ uspace/lib/bithenge/blob.h	(revision a42d7d8a6f7fcbe06ac84827876f2ba001e325a4)
@@ -243,5 +243,5 @@
 int bithenge_new_subblob(bithenge_node_t **, bithenge_blob_t *, aoff64_t,
     aoff64_t);
-bool bithenge_blob_equal(bithenge_blob_t *, bithenge_blob_t *);
+int bithenge_blob_equal(bool *, bithenge_blob_t *, bithenge_blob_t *);
 
 #endif
Index: uspace/lib/bithenge/expression.c
===================================================================
--- uspace/lib/bithenge/expression.c	(revision 1c79996e4cd922c6012ceb0b80e17b06cc0ed5e5)
+++ uspace/lib/bithenge/expression.c	(revision a42d7d8a6f7fcbe06ac84827876f2ba001e325a4)
@@ -104,5 +104,5 @@
 	/* Check types and get values. */
 	bithenge_int_t a_int = 0, b_int = 0;
-	bool a_bool = false, b_bool = false;
+	bool a_bool = false, b_bool = false, out_bool = false;
 	switch (self->op) {
 	case BITHENGE_EXPRESSION_ADD: /* fallthrough */
@@ -187,9 +187,14 @@
 		break;
 	case BITHENGE_EXPRESSION_EQUALS:
-		rc = bithenge_new_boolean_node(out, bithenge_node_equal(a, b));
+		rc = bithenge_node_equal(&out_bool, a, b);
+		if (rc != EOK)
+			break;
+		rc = bithenge_new_boolean_node(out, out_bool);
 		break;
 	case BITHENGE_EXPRESSION_NOT_EQUALS:
-		rc = bithenge_new_boolean_node(out,
-		    !bithenge_node_equal(a, b));
+		rc = bithenge_node_equal(&out_bool, a, b);
+		if (rc != EOK)
+			break;
+		rc = bithenge_new_boolean_node(out, !out_bool);
 		break;
 	case BITHENGE_EXPRESSION_AND:
Index: uspace/lib/bithenge/failure.c
===================================================================
--- uspace/lib/bithenge/failure.c	(revision a42d7d8a6f7fcbe06ac84827876f2ba001e325a4)
+++ uspace/lib/bithenge/failure.c	(revision a42d7d8a6f7fcbe06ac84827876f2ba001e325a4)
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2012 Sean Bartell
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - 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.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * 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.
+ */
+
+/** @addtogroup bithenge
+ * @{
+ */
+/**
+ * @file
+ * Fake system call errors for testing.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#define BITHENGE_FAILURE_DECLS_ONLY 1
+#include "failure.h"
+
+/* This file raises fake errors from system calls, to test that Bithenge
+ * handles the errors correctly. It has two primary modes of operation,
+ * depending on an environment variable:
+ *
+ * BITHENGE_FAILURE_INDEX not set: when a system call is made, a child process
+ * returns a fake error from that call. If the child process handles the error
+ * correctly (exit code is 1), the main process continues without errors. If
+ * the child process has a problem, the main process raises the fake error
+ * again and shows all stdout and stderr output. For speed, failures only occur
+ * for some system calls after the first 128.
+ *
+ * BITHENGE_FAILURE_INDEX set: the program runs normally until system call
+ * number BITHENGE_FAILURE_INDEX is made; a fake error is returned from this
+ * call. */
+
+static int g_failure_index = -1;
+static int g_failure_index_selected = -2;
+
+static int should_fail(void)
+{
+	g_failure_index++;
+
+	if (g_failure_index_selected == -2) {
+		char *sel_str = getenv("BITHENGE_FAILURE_INDEX");
+		if (sel_str) {
+			g_failure_index_selected = strtol(sel_str, NULL, 10);
+		} else {
+			g_failure_index_selected = -1;
+		}
+	} else if (g_failure_index_selected != -1) {
+		if (g_failure_index == g_failure_index_selected)
+			return 1; /* breakpoint here */
+		return 0;
+	}
+
+	/* Only fail half the time after 128 failures, 1/4 the time after 256
+	 * failures, 1/8 the time after 512 failures... */
+	int index = g_failure_index;
+	while (index >= 128) {
+		int test = (index & (64 | 1));
+		if (test == (64 | 1) || test == 0)
+			return 0;
+		index >>= 1;
+	}
+
+	if (!fork()) {
+		/* Child */
+		int null = open("/dev/null", O_WRONLY);
+		if (null == -1)
+			exit(127);
+		dup2(null, STDOUT_FILENO);
+		dup2(null, STDERR_FILENO);
+		close(null);
+		return 1;
+	}
+
+	/* Parent */
+	int status;
+	wait(&status);
+	if (WIFEXITED(status) && WEXITSTATUS(status) == 1)
+		return 0;
+
+	/* The child had an error! We couldn't see it because stdout and stderr
+	 * were redirected, and we couldn't debug it easily because it was a
+	 * separate process. Do it again without redirecting or forking. */
+	fprintf(stderr, "** Fake error raised here (BITHENGE_FAILURE_INDEX=%d)\n",
+	    g_failure_index);
+	return 1;
+}
+
+void *bithenge_failure_malloc(size_t size)
+{
+	if (should_fail())
+		return NULL;
+	return malloc(size);
+}
+
+void *bithenge_failure_realloc(void *ptr, size_t size)
+{
+	if (should_fail())
+		return NULL;
+	return realloc(ptr, size);
+}
+
+ssize_t bithenge_failure_read(int fd, void *buf, size_t count)
+{
+	if (should_fail()) {
+		errno = EIO;
+		return -1;
+	}
+	return read(fd, buf, count);
+}
+
+off_t bithenge_failure_lseek(int fd, off_t offset, int whither)
+{
+	if (should_fail()) {
+		errno = EINVAL;
+		return (off_t) -1;
+	}
+	return lseek(fd, offset, whither);
+}
+
+/** @}
+ */
Index: uspace/lib/bithenge/failure.h
===================================================================
--- uspace/lib/bithenge/failure.h	(revision a42d7d8a6f7fcbe06ac84827876f2ba001e325a4)
+++ uspace/lib/bithenge/failure.h	(revision a42d7d8a6f7fcbe06ac84827876f2ba001e325a4)
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2012 Sean Bartell
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - 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.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * 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.
+ */
+
+/** @addtogroup bithenge
+ * @{
+ */
+/**
+ * @file
+ * Fake system call errors for testing.
+ */
+
+#ifndef BITHENGE_FAILURE_H_
+#define BITHENGE_FAILURE_H_
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+void *bithenge_failure_malloc(size_t);
+void *bithenge_failure_realloc(void *, size_t);
+ssize_t bithenge_failure_read(int, void *, size_t);
+off_t bithenge_failure_lseek(int, off_t, int);
+
+#ifndef BITHENGE_FAILURE_DECLS_ONLY
+#define malloc bithenge_failure_malloc
+#define realloc bithenge_failure_realloc
+#define read bithenge_failure_read
+#define lseek bithenge_failure_lseek
+#endif
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/bithenge/file.c
===================================================================
--- uspace/lib/bithenge/file.c	(revision 1c79996e4cd922c6012ceb0b80e17b06cc0ed5e5)
+++ uspace/lib/bithenge/file.c	(revision a42d7d8a6f7fcbe06ac84827876f2ba001e325a4)
@@ -79,5 +79,5 @@
 		return ELIMIT;
 	if (lseek(blob->fd, offset, SEEK_SET) < 0)
-		return errno;
+		return errno == EINVAL ? EIO : EINVAL;
 
 	ssize_t amount_read;
Index: uspace/lib/bithenge/os.h
===================================================================
--- uspace/lib/bithenge/os.h	(revision 1c79996e4cd922c6012ceb0b80e17b06cc0ed5e5)
+++ uspace/lib/bithenge/os.h	(revision a42d7d8a6f7fcbe06ac84827876f2ba001e325a4)
@@ -32,2 +32,6 @@
 #include "linux/os.h"
 #endif
+
+#ifdef BITHENGE_FAILURE_ENABLE
+#include "failure.h"
+#endif
Index: uspace/lib/bithenge/script.c
===================================================================
--- uspace/lib/bithenge/script.c	(revision 1c79996e4cd922c6012ceb0b80e17b06cc0ed5e5)
+++ uspace/lib/bithenge/script.c	(revision a42d7d8a6f7fcbe06ac84827876f2ba001e325a4)
@@ -731,6 +731,8 @@
 		}
 		int rc = bithenge_binary_expression(&expr, op, expr, expr2);
-		if (rc != EOK)
-			error_errno(state, rc);
+		if (rc != EOK) {
+			expr = NULL;
+			error_errno(state, rc);
+		}
 	}
 	if (state->error != EOK) {
@@ -940,6 +942,8 @@
 		int rc = bithenge_if_transform(&switch_xform, exprs[num],
 		    xforms[num], switch_xform);
-		if (rc != EOK)
-			error_errno(state, rc);
+		if (rc != EOK) {
+			switch_xform = NULL;
+			error_errno(state, rc);
+		}
 	}
 
@@ -1189,10 +1193,9 @@
 		if (state->error != EOK)
 			break;
-		xforms[num] = parse_transform_no_compose(state);
-		num++;
+		xforms[num++] = parse_transform_no_compose(state);
 	}
 	if (state->error != EOK) {
-		while (xforms && num--)
-			bithenge_transform_dec_ref(xforms[num]);
+		while (xforms && num > 1)
+			bithenge_transform_dec_ref(xforms[--num]);
 		free(xforms);
 		bithenge_transform_dec_ref(result);
Index: uspace/lib/bithenge/sequence.c
===================================================================
--- uspace/lib/bithenge/sequence.c	(revision 1c79996e4cd922c6012ceb0b80e17b06cc0ed5e5)
+++ uspace/lib/bithenge/sequence.c	(revision a42d7d8a6f7fcbe06ac84827876f2ba001e325a4)
@@ -488,4 +488,17 @@
 		return rc;
 	}
+
+	rc = seq_node_init(struct_as_seq(node), &struct_node_seq_ops, inner,
+	    blob, self->num_subtransforms, false);
+	if (rc != EOK) {
+		bithenge_scope_dec_ref(inner);
+		free(node);
+		return rc;
+	}
+
+	bithenge_transform_inc_ref(struct_as_transform(self));
+	node->transform = self;
+	node->prefix = prefix;
+
 	/* We should inc_ref(node) here, but that would make a cycle. Instead,
 	 * we leave it 1 too low, so that when the only remaining use of node
@@ -493,16 +506,6 @@
 	 * struct_node_destroy. */
 	bithenge_scope_set_current_node(inner, struct_as_node(node));
-
-	rc = seq_node_init(struct_as_seq(node), &struct_node_seq_ops, inner,
-	    blob, self->num_subtransforms, false);
 	bithenge_scope_dec_ref(inner);
-	if (rc != EOK) {
-		free(node);
-		return rc;
-	}
-
-	bithenge_transform_inc_ref(struct_as_transform(self));
-	node->transform = self;
-	node->prefix = prefix;
+
 	*out = struct_as_node(node);
 
@@ -834,6 +837,10 @@
 				rc = seq_node_field_offset(
 				    node_as_seq(*out_node), &size, count);
-				if (rc != EOK)
+				if (rc == EINVAL || rc == ENOENT)
 					break;
+				if (rc != EOK) {
+					bithenge_node_dec_ref(*out_node);
+					return rc;
+				}
 				*out_size = size;
 			}
Index: uspace/lib/bithenge/tree.c
===================================================================
--- uspace/lib/bithenge/tree.c	(revision 1c79996e4cd922c6012ceb0b80e17b06cc0ed5e5)
+++ uspace/lib/bithenge/tree.c	(revision a42d7d8a6f7fcbe06ac84827876f2ba001e325a4)
@@ -90,6 +90,9 @@
 {
 	get_for_each_data_t *data = (get_for_each_data_t *)raw_data;
-	bool equal = bithenge_node_equal(key, data->key);
+	bool equal;
+	int rc = bithenge_node_equal(&equal, key, data->key);
 	bithenge_node_dec_ref(key);
+	if (rc != EOK)
+		return rc;
 	if (equal) {
 		*data->out = value;
@@ -348,26 +351,33 @@
  * internal nodes. Takes ownership of nothing.
  * @memberof bithenge_node_t
+ * @param[out] out Holds whether the nodes are equal.
  * @param a, b Nodes to compare.
- * @return Whether the nodes are equal. If an error occurs, returns false.
+ * @return EOK on success or an error code from errno.h.
  * @todo Add support for internal nodes.
  */
-bool bithenge_node_equal(bithenge_node_t *a, bithenge_node_t *b)
-{
-	if (a->type != b->type)
-		return false;
+int bithenge_node_equal(bool *out, bithenge_node_t *a, bithenge_node_t *b)
+{
+	if (a->type != b->type) {
+		*out = false;
+		return EOK;
+	}
 	switch (a->type) {
 	case BITHENGE_NODE_INTERNAL:
-		return false;
+		*out = false;
+		return EOK;
 	case BITHENGE_NODE_BOOLEAN:
-		return a->boolean_value == b->boolean_value;
+		*out = a->boolean_value == b->boolean_value;
+		return EOK;
 	case BITHENGE_NODE_INTEGER:
-		return a->integer_value == b->integer_value;
+		*out = a->integer_value == b->integer_value;
+		return EOK;
 	case BITHENGE_NODE_STRING:
-		return !str_cmp(a->string_value.ptr, b->string_value.ptr);
+		*out = !str_cmp(a->string_value.ptr, b->string_value.ptr);
+		return EOK;
 	case BITHENGE_NODE_BLOB:
-		return bithenge_blob_equal(bithenge_node_as_blob(a),
+		return bithenge_blob_equal(out, bithenge_node_as_blob(a),
 		    bithenge_node_as_blob(b));
 	}
-	return false;
+	return EINVAL;
 }
 
Index: uspace/lib/bithenge/tree.h
===================================================================
--- uspace/lib/bithenge/tree.h	(revision 1c79996e4cd922c6012ceb0b80e17b06cc0ed5e5)
+++ uspace/lib/bithenge/tree.h	(revision a42d7d8a6f7fcbe06ac84827876f2ba001e325a4)
@@ -167,5 +167,5 @@
 int bithenge_new_integer_node(bithenge_node_t **, bithenge_int_t);
 int bithenge_new_string_node(bithenge_node_t **, const char *, bool);
-bool bithenge_node_equal(bithenge_node_t *, bithenge_node_t *);
+int bithenge_node_equal(bool *, bithenge_node_t *, bithenge_node_t *);
 
 #endif
