vboot2: Add host library functions to read/write files and objects

And unit tests for them.

Move roundup32() into hostlib.

Fix WriteFile() returning success even if it failed to write to the file.

BUG=chromium:423882
BRANCH=none
TEST=VBOOT2=1 make runtests

Change-Id: I8a115335c088dc5c66c88423d1ccbda7eaca1996
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/230844
diff --git a/Makefile b/Makefile
index 2427c83..bf40a58 100644
--- a/Makefile
+++ b/Makefile
@@ -374,6 +374,12 @@
 	host/lib/host_signature.c \
 	host/lib/signature_digest.c
 
+ifneq (${VBOOT2},)
+UTILLIB_SRCS += \
+	host/lib/host_misc2.c \
+
+endif
+
 UTILLIB_OBJS = ${UTILLIB_SRCS:%.c=${BUILD}/%.o}
 ALL_OBJS += ${UTILLIB_OBJS}
 
@@ -626,6 +632,7 @@
 	tests/vb2_common_tests \
 	tests/vb2_common2_tests \
 	tests/vb2_common3_tests \
+	tests/vb2_host_misc_tests \
 	tests/vb2_misc_tests \
 	tests/vb2_misc2_tests \
 	tests/vb2_misc3_tests \
@@ -1160,6 +1167,7 @@
 	${RUNTEST} ${BUILD_RUN}/tests/vb2_common_tests
 	${RUNTEST} ${BUILD_RUN}/tests/vb2_common2_tests ${TEST_KEYS}
 	${RUNTEST} ${BUILD_RUN}/tests/vb2_common3_tests ${TEST_KEYS}
+	${RUNTEST} ${BUILD_RUN}/tests/vb2_host_misc_tests
 	${RUNTEST} ${BUILD_RUN}/tests/vb2_misc_tests
 	${RUNTEST} ${BUILD_RUN}/tests/vb2_misc2_tests
 	${RUNTEST} ${BUILD_RUN}/tests/vb2_misc3_tests
diff --git a/firmware/2lib/include/2return_codes.h b/firmware/2lib/include/2return_codes.h
index fa7437e..69a9494 100644
--- a/firmware/2lib/include/2return_codes.h
+++ b/firmware/2lib/include/2return_codes.h
@@ -20,7 +20,7 @@
 	 * All vboot2 error codes start at a large offset from zero, to reduce
 	 * the risk of overlap with other error codes (TPM, etc.).
 	 */
-	VB2_ERROR_BASE = 0x0100000,
+	VB2_ERROR_BASE = 0x10000000,
 
 	/* Unknown / unspecified error */
 	VB2_ERROR_UNKNOWN = VB2_ERROR_BASE + 1,
@@ -443,12 +443,41 @@
 	/* TPM clear owner not implemented */
 	VB2_ERROR_EX_TPM_CLEAR_OWNER_UNIMPLEMENTED,
 
+
+        /**********************************************************************
+	 * Errors generated by host library (non-firmware) start here.
+	 */
+	VB2_ERROR_HOST_BASE = 0x20000000,
+
+        /**********************************************************************
+	 * Errors generated by host library misc functions
+	 */
+	VB2_ERROR_HOST_MISC = VB2_ERROR_HOST_BASE + 0x010000,
+
+	/* Unable to open file in read_file() */
+	VB2_ERROR_READ_FILE_OPEN,
+
+	/* Bad size in read_file() */
+	VB2_ERROR_READ_FILE_SIZE,
+
+	/* Unable to allocate buffer in read_file() */
+	VB2_ERROR_READ_FILE_ALLOC,
+
+	/* Unable to read data in read_file() */
+	VB2_ERROR_READ_FILE_DATA,
+
+	/* Unable to open file in write_file() */
+	VB2_ERROR_WRITE_FILE_OPEN,
+
+	/* Unable to write data in write_file() */
+	VB2_ERROR_WRITE_FILE_DATA,
+
         /**********************************************************************
 	 * Highest non-zero error generated inside vboot library.  Note that
 	 * error codes passed through vboot when it calls external APIs may
 	 * still be outside this range.
 	 */
-	VB2_ERROR_MAX = VB2_ERROR_BASE + 0xffffff,
+	VB2_ERROR_MAX = VB2_ERROR_BASE + 0x1fffffff,
 };
 
 #endif  /* VBOOT_2_RETURN_CODES_H_ */
diff --git a/host/lib/host_misc.c b/host/lib/host_misc.c
index adc3942..3fb9b24 100644
--- a/host/lib/host_misc.c
+++ b/host/lib/host_misc.c
@@ -108,6 +108,7 @@
     VBDEBUG(("Unable to write to file %s\n", filename));
     fclose(f);
     unlink(filename);  /* Delete any partial file */
+    return 1;
   }
 
   fclose(f);
diff --git a/host/lib/host_misc2.c b/host/lib/host_misc2.c
new file mode 100644
index 0000000..88b58f5
--- /dev/null
+++ b/host/lib/host_misc2.c
@@ -0,0 +1,84 @@
+/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Host functions for verified boot.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "2sysincludes.h"
+#include "2common.h"
+#include "host_common.h"
+
+int vb2_read_file(const char *filename, uint8_t **data_ptr, uint32_t *size_ptr)
+{
+	FILE *f;
+	uint8_t *buf;
+	long size;
+
+	*data_ptr = NULL;
+	*size_ptr = 0;
+
+	f = fopen(filename, "rb");
+	if (!f) {
+		VB2_DEBUG("Unable to open file %s\n", filename);
+		return VB2_ERROR_READ_FILE_OPEN;
+	}
+
+	fseek(f, 0, SEEK_END);
+	size = ftell(f);
+	rewind(f);
+
+	if (size < 0 || size > UINT32_MAX) {
+		fclose(f);
+		return VB2_ERROR_READ_FILE_SIZE;
+	}
+
+	buf = malloc(size);
+	if (!buf) {
+		fclose(f);
+		return VB2_ERROR_READ_FILE_ALLOC;
+	}
+
+	if(1 != fread(buf, size, 1, f)) {
+		VB2_DEBUG("Unable to read file %s\n", filename);
+		fclose(f);
+		free(buf);
+		return VB2_ERROR_READ_FILE_DATA;
+	}
+
+	fclose(f);
+
+	*data_ptr = buf;
+	*size_ptr = size;
+	return VB2_SUCCESS;
+}
+
+int vb2_write_file(const char *filename, const void *buf, uint32_t size)
+{
+	FILE *f = fopen(filename, "wb");
+
+	if (!f) {
+		VB2_DEBUG("Unable to open file %s\n", filename);
+		return VB2_ERROR_WRITE_FILE_OPEN;
+	}
+
+	if (1 != fwrite(buf, size, 1, f)) {
+		VB2_DEBUG("Unable to write to file %s\n", filename);
+		fclose(f);
+		unlink(filename);  /* Delete any partial file */
+		return VB2_ERROR_WRITE_FILE_DATA;
+	}
+
+	fclose(f);
+	return VB2_SUCCESS;
+}
+
+int vb2_write_object(const char *filename, const void *buf)
+{
+	const struct vb2_struct_common *cptr = buf;
+
+	return vb2_write_file(filename, buf, cptr->total_size);
+}
diff --git a/host/lib/include/host_misc.h b/host/lib/include/host_misc.h
index b2b4fb9..2e18d90 100644
--- a/host/lib/include/host_misc.h
+++ b/host/lib/include/host_misc.h
@@ -43,4 +43,45 @@
  * Returns 0 if success, 1 if error. */
 int WriteFile(const char* filename, const void *data, uint64_t size);
 
+/**
+ * Read data from a file into a newly allocated buffer.
+ *
+ * @param filename	Name of file to read from
+ * @param data_ptr	On exit, pointer to newly allocated buffer with data
+ *			will be stored here.  Caller must free() the buffer
+ *			when done with it.
+ * @param size_ptr	On exit, size of data will be stored here.
+ * @return VB2_SUCCESS, or non-zero if error.
+ */
+int vb2_read_file(const char *filename, uint8_t **data_ptr, uint32_t *size_ptr);
+
+/**
+ * Write data to a file from a buffer.
+ *
+ * @param filename	Name of file to write to
+ * @param buf		Buffer to write
+ * @param size		Number of bytes of data to write
+ * @return VB2_SUCCESS, or non-zero if error.
+ */
+int vb2_write_file(const char *filename, const void *buf, uint32_t size);
+
+/**
+ * Write a buffer which starts with a standard vb2_struct_common header.
+ *
+ * Determines the buffer size from the common header total size field.
+ *
+ * @param filename	Name of file to write to
+ * @param buf		Buffer to write
+ * @return VB2_SUCCESS, or non-zero if error.
+ */
+int vb2_write_object(const char *filename, const void *buf);
+
+/**
+ * Round up a size to a multiple of 32 bits (4 bytes).
+ */
+static __inline const uint32_t roundup32(uint32_t v)
+{
+	return (v + 3) & ~3;
+}
+
 #endif  /* VBOOT_REFERENCE_HOST_MISC_H_ */
diff --git a/tests/vb2_convert_structs.c b/tests/vb2_convert_structs.c
index 0c4a7ec..d30b337 100644
--- a/tests/vb2_convert_structs.c
+++ b/tests/vb2_convert_structs.c
@@ -8,6 +8,8 @@
 #include "2sysincludes.h"
 #include "2common.h"
 #include "2rsa.h"
+#include "host_common.h"
+#include "host_misc.h"
 #include "vb2_convert_structs.h"
 #include "vboot_struct.h"  /* For old struct sizes */
 
diff --git a/tests/vb2_convert_structs.h b/tests/vb2_convert_structs.h
index 032b5bf..0ba1ca3 100644
--- a/tests/vb2_convert_structs.h
+++ b/tests/vb2_convert_structs.h
@@ -10,14 +10,6 @@
 #include "2struct.h"
 
 /**
- * Round up a size to a multiple of 32 bits (4 bytes).
- */
-static __inline const uint32_t roundup32(uint32_t v)
-{
-	return (v + 3) & ~3;
-}
-
-/**
  * Convert a packed key from vboot data format to vboot2 data format.
  *
  * Intended for use by unit tests.  Does NOT validate the original struct
diff --git a/tests/vb2_host_misc_tests.c b/tests/vb2_host_misc_tests.c
new file mode 100644
index 0000000..e8c2a58
--- /dev/null
+++ b/tests/vb2_host_misc_tests.c
@@ -0,0 +1,71 @@
+/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Tests for host misc library vboot2 functions
+ */
+
+#include <unistd.h>
+
+#include "2sysincludes.h"
+#include "2common.h"
+#include "host_common.h"
+#include "host_misc.h"
+
+#include "test_common.h"
+
+static void misc_tests(void)
+{
+	TEST_EQ(roundup32(0), 0, "roundup32(0)");
+	TEST_EQ(roundup32(15), 16, "roundup32(15)");
+	TEST_EQ(roundup32(16), 16, "roundup32(16)");
+}
+
+static void file_tests(void)
+{
+	const char *testfile = "file_tests.dat";
+	const uint8_t test_data[] = "Some test data";
+	uint8_t *read_data;
+	uint32_t read_size;
+
+	uint8_t cbuf[sizeof(struct vb2_struct_common) + 12];
+	struct vb2_struct_common *c = (struct vb2_struct_common *)cbuf;
+
+	unlink(testfile);
+
+	TEST_EQ(vb2_read_file(testfile, &read_data, &read_size),
+		VB2_ERROR_READ_FILE_OPEN, "vb2_read_file() missing");
+	TEST_EQ(vb2_write_file("no/such/dir", test_data, sizeof(test_data)),
+		VB2_ERROR_WRITE_FILE_OPEN, "vb2_write_file() open");
+
+	TEST_SUCC(vb2_write_file(testfile, test_data, sizeof(test_data)),
+		  "vb2_write_file() good");
+	TEST_SUCC(vb2_read_file(testfile, &read_data, &read_size),
+		  "vb2_read_file() good");
+	TEST_EQ(read_size, sizeof(test_data), "  data size");
+	TEST_EQ(memcmp(read_data, test_data, read_size), 0, "  data");
+	free(read_data);
+	unlink(testfile);
+
+	memset(cbuf, 0, sizeof(cbuf));
+	c->fixed_size = sizeof(*c);
+	c->total_size = sizeof(cbuf);
+	c->magic = 0x1234;
+	cbuf[sizeof(cbuf) - 1] = 0xed;  /* Some non-zero data at the end */
+	TEST_SUCC(vb2_write_object(testfile, c), "vb2_write_object() good");
+	TEST_SUCC(vb2_read_file(testfile, &read_data, &read_size),
+		  "vb2_read_file() object");
+	TEST_EQ(read_size, c->total_size, "  data size");
+	/* Compare the entire buffer, including the non-zero data at the end */
+	TEST_EQ(memcmp(read_data, c, read_size), 0, "  data");
+	free(read_data);
+	unlink(testfile);
+}
+
+int main(int argc, char* argv[])
+{
+	misc_tests();
+	file_tests();
+
+	return gTestSuccess ? 0 : 255;
+}