Index: uspace/app/bithenge/expression.c
===================================================================
--- uspace/app/bithenge/expression.c	(revision c97970674c89cafa26e21448d401fd022ab67473)
+++ uspace/app/bithenge/expression.c	(revision 0153c87e5c9c1080a884969b29e7aa3388bd5a64)
@@ -104,5 +104,11 @@
 	case BITHENGE_EXPRESSION_ADD: /* fallthrough */
 	case BITHENGE_EXPRESSION_SUBTRACT: /* fallthrough */
-	case BITHENGE_EXPRESSION_MULTIPLY:
+	case BITHENGE_EXPRESSION_MULTIPLY: /* fallthrough */
+	case BITHENGE_EXPRESSION_INTEGER_DIVIDE: /* fallthrough */
+	case BITHENGE_EXPRESSION_MODULO: /* fallthrough */
+	case BITHENGE_EXPRESSION_LESS_THAN: /* fallthrough */
+	case BITHENGE_EXPRESSION_LESS_THAN_OR_EQUAL: /* fallthrough */
+	case BITHENGE_EXPRESSION_GREATER_THAN: /* fallthrough */
+	case BITHENGE_EXPRESSION_GREATER_THAN_OR_EQUAL:
 		rc = EINVAL;
 		if (bithenge_node_type(a) != BITHENGE_NODE_INTEGER)
@@ -127,6 +133,45 @@
 		rc = bithenge_new_integer_node(out, a_int * b_int);
 		break;
+	case BITHENGE_EXPRESSION_INTEGER_DIVIDE:
+		/* Integer division can behave in three major ways when the
+		 * operands are signed: truncated, floored, or Euclidean. When
+		 * b > 0, we give the same result as floored and Euclidean;
+		 * otherwise, we currently raise an error. See
+		 * https://en.wikipedia.org/wiki/Modulo_operation and its
+		 * references. */
+		if (b_int <= 0) {
+			rc = EINVAL;
+			break;
+		}
+		rc = bithenge_new_integer_node(out,
+		    (a_int / b_int) + (a_int % b_int < 0 ? -1 : 0));
+		break;
+	case BITHENGE_EXPRESSION_MODULO:
+		/* This is consistent with division; see above. */
+		if (b_int <= 0) {
+			rc = EINVAL;
+			break;
+		}
+		rc = bithenge_new_integer_node(out,
+		    (a_int % b_int) + (a_int % b_int < 0 ? b_int : 0));
+		break;
+	case BITHENGE_EXPRESSION_LESS_THAN:
+		rc = bithenge_new_boolean_node(out, a_int < b_int);
+		break;
+	case BITHENGE_EXPRESSION_LESS_THAN_OR_EQUAL:
+		rc = bithenge_new_boolean_node(out, a_int <= b_int);
+		break;
+	case BITHENGE_EXPRESSION_GREATER_THAN:
+		rc = bithenge_new_boolean_node(out, a_int > b_int);
+		break;
+	case BITHENGE_EXPRESSION_GREATER_THAN_OR_EQUAL:
+		rc = bithenge_new_boolean_node(out, a_int >= b_int);
+		break;
 	case BITHENGE_EXPRESSION_EQUALS:
 		rc = bithenge_new_boolean_node(out, bithenge_node_equal(a, b));
+		break;
+	case BITHENGE_EXPRESSION_NOT_EQUALS:
+		rc = bithenge_new_boolean_node(out,
+		    ~bithenge_node_equal(a, b));
 		break;
 	case BITHENGE_EXPRESSION_INVALID_BINARY_OP:
@@ -495,6 +540,8 @@
 	for (; scope && !bithenge_scope_is_barrier(scope);
 	    scope = bithenge_scope_outer(scope)) {
+		bithenge_node_t *cur = bithenge_scope_get_current_node(scope);
+		if (!cur)
+			continue;
 		bithenge_node_inc_ref(self->key);
-		bithenge_node_t *cur = bithenge_scope_get_current_node(scope);
 		int rc = bithenge_node_get(cur, self->key, out);
 		bithenge_node_dec_ref(cur);
Index: uspace/app/bithenge/expression.h
===================================================================
--- uspace/app/bithenge/expression.h	(revision c97970674c89cafa26e21448d401fd022ab67473)
+++ uspace/app/bithenge/expression.h	(revision 0153c87e5c9c1080a884969b29e7aa3388bd5a64)
@@ -96,5 +96,12 @@
 	BITHENGE_EXPRESSION_SUBTRACT,
 	BITHENGE_EXPRESSION_MULTIPLY,
+	BITHENGE_EXPRESSION_INTEGER_DIVIDE,
+	BITHENGE_EXPRESSION_MODULO,
+	BITHENGE_EXPRESSION_LESS_THAN,
+	BITHENGE_EXPRESSION_GREATER_THAN,
+	BITHENGE_EXPRESSION_LESS_THAN_OR_EQUAL,
+	BITHENGE_EXPRESSION_GREATER_THAN_OR_EQUAL,
 	BITHENGE_EXPRESSION_EQUALS,
+	BITHENGE_EXPRESSION_NOT_EQUALS,
 } bithenge_binary_op_t;
 
Index: uspace/app/bithenge/script.c
===================================================================
--- uspace/app/bithenge/script.c	(revision c97970674c89cafa26e21448d401fd022ab67473)
+++ uspace/app/bithenge/script.c	(revision 0153c87e5c9c1080a884969b29e7aa3388bd5a64)
@@ -56,7 +56,11 @@
 	TOKEN_ERROR,
 	TOKEN_EOF,
+	TOKEN_GREATER_THAN_OR_EQUAL,
 	TOKEN_IDENTIFIER,
 	TOKEN_INTEGER,
+	TOKEN_INTEGER_DIVIDE,
 	TOKEN_LEFT_ARROW,
+	TOKEN_LESS_THAN_OR_EQUAL,
+	TOKEN_NOT_EQUAL,
 
 	/* Keywords */
@@ -272,4 +276,14 @@
 			state->buffer_pos++;
 			state->token = TOKEN_LEFT_ARROW;
+		} else if (state->buffer[state->buffer_pos] == '=') {
+			state->buffer_pos++;
+			state->token = TOKEN_LESS_THAN_OR_EQUAL;
+		}
+	} else if (ch == '>') {
+		state->token = ch;
+		state->buffer_pos++;
+		if (state->buffer[state->buffer_pos] == '=') {
+			state->buffer_pos++;
+			state->token = TOKEN_GREATER_THAN_OR_EQUAL;
 		}
 	} else if (ch == '=') {
@@ -280,4 +294,18 @@
 			state->buffer_pos++;
 		}
+	} else if (ch == '/') {
+		state->token = ch;
+		state->buffer_pos++;
+		if (state->buffer[state->buffer_pos] == '/') {
+			state->token = TOKEN_INTEGER_DIVIDE;
+			state->buffer_pos++;
+		}
+	} else if (ch == '!') {
+		state->token = ch;
+		state->buffer_pos++;
+		if (state->buffer[state->buffer_pos] == '=') {
+			state->token = TOKEN_NOT_EQUAL;
+			state->buffer_pos++;
+		}
 	} else {
 		state->token = ch;
@@ -388,4 +416,5 @@
 	PRECEDENCE_NONE,
 	PRECEDENCE_EQUALS,
+	PRECEDENCE_COMPARE,
 	PRECEDENCE_ADD,
 	PRECEDENCE_MULTIPLY,
@@ -401,6 +430,20 @@
 	case '*':
 		return BITHENGE_EXPRESSION_MULTIPLY;
+	case TOKEN_INTEGER_DIVIDE:
+		return BITHENGE_EXPRESSION_INTEGER_DIVIDE;
+	case '%':
+		return BITHENGE_EXPRESSION_MODULO;
+	case '<':
+		return BITHENGE_EXPRESSION_LESS_THAN;
+	case TOKEN_LESS_THAN_OR_EQUAL:
+		return BITHENGE_EXPRESSION_LESS_THAN_OR_EQUAL;
+	case '>':
+		return BITHENGE_EXPRESSION_GREATER_THAN;
+	case TOKEN_GREATER_THAN_OR_EQUAL:
+		return BITHENGE_EXPRESSION_GREATER_THAN_OR_EQUAL;
 	case TOKEN_EQUALS:
 		return BITHENGE_EXPRESSION_EQUALS;
+	case TOKEN_NOT_EQUAL:
+		return BITHENGE_EXPRESSION_NOT_EQUALS;
 	default:
 		return BITHENGE_EXPRESSION_INVALID_BINARY_OP;
@@ -414,7 +457,15 @@
 	case BITHENGE_EXPRESSION_SUBTRACT:
 		return PRECEDENCE_ADD;
-	case BITHENGE_EXPRESSION_MULTIPLY:
+	case BITHENGE_EXPRESSION_MULTIPLY: /* fallthrough */
+	case BITHENGE_EXPRESSION_INTEGER_DIVIDE: /* fallthrough */
+	case BITHENGE_EXPRESSION_MODULO:
 		return PRECEDENCE_MULTIPLY;
-	case BITHENGE_EXPRESSION_EQUALS:
+	case BITHENGE_EXPRESSION_LESS_THAN: /* fallthrough */
+	case BITHENGE_EXPRESSION_LESS_THAN_OR_EQUAL: /* fallthrough */
+	case BITHENGE_EXPRESSION_GREATER_THAN: /* fallthrough */
+	case BITHENGE_EXPRESSION_GREATER_THAN_OR_EQUAL:
+		return PRECEDENCE_COMPARE;
+	case BITHENGE_EXPRESSION_EQUALS: /* fallthrough */
+	case BITHENGE_EXPRESSION_NOT_EQUALS:
 		return PRECEDENCE_EQUALS;
 	default:
@@ -614,5 +665,6 @@
 		next_token(state);
 
-		bithenge_expression_t *expr2 = parse_postfix_expression(state);
+		bithenge_expression_t *expr2 =
+		    parse_expression_precedence(state, precedence);
 		if (state->error != EOK) {
 			bithenge_expression_dec_ref(expr2);
Index: uspace/dist/src/bithenge/fat.bh
===================================================================
--- uspace/dist/src/bithenge/fat.bh	(revision c97970674c89cafa26e21448d401fd022ab67473)
+++ uspace/dist/src/bithenge/fat.bh	(revision 0153c87e5c9c1080a884969b29e7aa3388bd5a64)
@@ -32,4 +32,26 @@
 transform u32 = uint32le;
 
+transform fat_dir_entry(disk) = struct {
+	.filename <- known_length(8);
+	.extension <- known_length(3);
+	.attrs <- u8;
+	.flags <- u8;
+	.ctime_fine <- u8;
+	.ctime <- u16;
+	.cdate <- u16;
+	.adate <- u16;
+	.permissions <- u16;
+	.mtime <- u16;
+	.mdate <- u16;
+	.start <- u16;
+	.size <- u32;
+};
+
+transform fat_table(bits, num_clusters) = switch (bits) {
+	12: partial {repeat(num_clusters) { uint_le(12) }} <- bits_le;
+	16: partial {repeat(num_clusters) { u16 }};
+	32: partial {repeat(num_clusters) { u32 }};
+};
+
 transform fat_super(disk) = struct {
 	.jump_instruction <- known_length(3);
@@ -63,4 +85,22 @@
 	};
 
+	.first_root_sector <- (.num_reserved_sectors + .num_fats * .sectors_per_fat);
+	.first_data_sector <- (.first_root_sector +
+	    (.num_root_entries * 32 + .bytes_per_sector - 1) //
+	    .bytes_per_sector);
+	.num_clusters <- (2 + (.num_sectors - .first_data_sector) // .sectors_per_cluster);
+	.bits <- if (.num_clusters < 4085) { (12) }
+	    else { if (.num_clusters < 65525) { (16) } else { (32) } };
+
+	.fats <- partial(.num_reserved_sectors * .bytes_per_sector) {
+		repeat(.num_fats) {
+			fat_table(.bits, .num_clusters) <-
+				known_length(.sectors_per_fat * .bytes_per_sector)
+		}
+	} <- (disk);
+
+	.root <- partial(.first_root_sector * .bytes_per_sector) {
+		repeat(.num_root_entries) { fat_dir_entry(disk) } } <- (disk);
+
 	.boot_signature <- (disk[510,2]); # b"\x55\xaa"; TODO: what if .bytes_per_sector < 512?
 };
