libfdt: Implement fdt_move()

Implement the fdt_move() function for copying/moving device trees
to a new buffer, or within an existing buffer.
diff --git a/fdt.c b/fdt.c
index b24a3fe..0c1a5f0 100644
--- a/fdt.c
+++ b/fdt.c
@@ -23,6 +23,25 @@
 
 #include "libfdt_internal.h"
 
+int _fdt_check_header(const struct fdt_header *fdt)
+{
+	if (fdt_magic(fdt) == FDT_MAGIC) {
+		/* Complete tree */
+		if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
+			return FDT_ERR_BADVERSION;
+		if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)
+			return FDT_ERR_BADVERSION;
+	} else if (fdt_magic(fdt) == SW_MAGIC) {
+		/* Unfinished sequential-write blob */
+		if (sw_size_dt_struct(fdt) == 0)
+			return FDT_ERR_BADSTATE;
+	} else {
+		return FDT_ERR_BADMAGIC;
+	}
+
+	return 0;
+}
+
 void *fdt_offset_ptr(const struct fdt_header *fdt, int offset, int len)
 {
 	void *p;
@@ -72,3 +91,17 @@
 
 	return tag;
 }
+
+struct fdt_header *fdt_move(const struct fdt_header *fdt, void *buf, int bufsize)
+{
+	int err = _fdt_check_header(fdt);
+
+	if (err)
+		return PTR_ERROR(err);
+
+	if (fdt_totalsize(fdt) > bufsize)
+		return PTR_ERROR(FDT_ERR_NOSPACE);
+
+	memmove(buf, fdt, fdt_totalsize(fdt));
+	return (struct fdt_header *)buf;
+}
diff --git a/fdt_ro.c b/fdt_ro.c
index db5281c..bbc380a 100644
--- a/fdt_ro.c
+++ b/fdt_ro.c
@@ -23,29 +23,10 @@
 
 #include "libfdt_internal.h"
 
-static int check_header(const struct fdt_header *fdt)
-{
-	if (fdt_magic(fdt) == FDT_MAGIC) {
-		/* Complete tree */
-		if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
-			return FDT_ERR_BADVERSION;
-		if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)
-			return FDT_ERR_BADVERSION;
-	} else if (fdt_magic(fdt) == SW_MAGIC) {
-		/* Unfinished sequential-write blob */
-		if (sw_size_dt_struct(fdt) == 0)
-			return FDT_ERR_BADSTATE;
-	} else {
-		return FDT_ERR_BADMAGIC;
-	}
-
-	return 0;
-}
-
 #define OFFSET_CHECK_HEADER(fdt) \
 	{ \
 		int err; \
-		if ((err = check_header(fdt)) != 0) \
+		if ((err = _fdt_check_header(fdt)) != 0) \
 			return OFFSET_ERROR(err); \
 	}
 
diff --git a/libfdt.h b/libfdt.h
index d4f98dc..c0ee2b7 100644
--- a/libfdt.h
+++ b/libfdt.h
@@ -64,6 +64,8 @@
 #define fdt_ptr_error(ptr) \
 	( (((long)(ptr) < 0) && ((long)(ptr) >= -FDT_ERR_MAX)) ? -(long)(ptr) : 0 )
 
+struct fdt_header *fdt_move(const struct fdt_header *fdt, void *buf, int bufsize);
+
 /* Read-only functions */
 char *fdt_string(const struct fdt_header *fdt, int stroffset);
 
@@ -115,9 +117,6 @@
 int fdt_set_property(struct fdt_header *fdtx, void *node, const char *name,
 		     const void *val, int len);
 int fdt_del_property(struct fdt_header *fdtx, void *node, const char *name);
-
-/* Misc functions */
-struct fdt_header *fdt_move(struct fdt_header *fdt, void *buf, int bufsize);
 #endif
 
 #endif /* _LIBFDT_H */
diff --git a/libfdt_internal.h b/libfdt_internal.h
index 06fa1cc..91361c2 100644
--- a/libfdt_internal.h
+++ b/libfdt_internal.h
@@ -26,6 +26,7 @@
 #define memeq(p, q, n)	(memcmp((p), (q), (n)) == 0)
 #define streq(p, q)	(strcmp((p), (q)) == 0)
 
+int _fdt_check_header(const struct fdt_header *fdt);
 uint32_t _fdt_next_tag(const struct fdt_header *fdt, int startoffset, int *nextoffset);
 struct fdt_property *_fdt_getprop(const struct fdt_header *fdt, int nodeoffset,
 				  const char *name, int *lenp);
diff --git a/tests/Makefile b/tests/Makefile
index b6dc852..b430a68 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -3,7 +3,8 @@
 LIB_TESTS = root_node property_offset subnode_offset path_offset getprop \
 	notfound \
 	setprop_inplace nop_property nop_node \
-	sw_tree1
+	sw_tree1 \
+	move_and_save
 TESTS = $(LIB_TESTS)
 UTILS = dumptrees
 
diff --git a/tests/move_and_save.c b/tests/move_and_save.c
new file mode 100644
index 0000000..526f615
--- /dev/null
+++ b/tests/move_and_save.c
@@ -0,0 +1,71 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ *	Basic testcase for read-only access
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+int main(int argc, char *argv[])
+{
+	struct fdt_header *fdt, *fdt1, *fdt2, *fdt3;
+	void *buf;
+	int shuntsize;
+	int bufsize;
+	int err;
+	const char *inname;
+	char outname[PATH_MAX];
+
+	test_init(argc, argv);
+	fdt = load_blob_arg(argc, argv);
+	inname = argv[1];
+
+	shuntsize = ALIGN(fdt_totalsize(fdt) / 2, sizeof(uint64_t));
+	bufsize = fdt_totalsize(fdt) + shuntsize;
+	buf = xmalloc(bufsize);
+
+	fdt1 = fdt_move(fdt, buf, bufsize);
+	if ((err = fdt_ptr_error(fdt1)))
+		FAIL("Failed to move tree into new buffer: %s",
+		     fdt_strerror(err));
+	sprintf(outname, "moved.%s", inname);
+	save_blob(outname, fdt1);
+
+	fdt2 = fdt_move(fdt1, buf + shuntsize, bufsize-shuntsize);
+	if ((err = fdt_ptr_error(fdt2)))
+		FAIL("Failed to shunt tree %d bytes: %s",
+		     shuntsize, fdt_strerror(err));
+	sprintf(outname, "shunted.%s", inname);
+	save_blob(outname, fdt2);
+
+	fdt3 = fdt_move(fdt2, buf, bufsize);
+	if ((err = fdt_ptr_error(fdt3)))
+		FAIL("Failed to deshunt tree %d bytes: %s",
+		     shuntsize, fdt_strerror(err));
+	sprintf(outname, "deshunted.%s", inname);
+	save_blob(outname, fdt3);
+
+	PASS();
+}
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index 338e47a..fba7413 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -36,6 +36,15 @@
     run_test sw_tree1
     tree1_tests sw_tree1.test.dtb
     tree1_tests unfinished_tree1.test.dtb
+
+    # fdt_move tests
+    for tree in test_tree1.dtb sw_tree1.test.dtb unfinished_tree1.test.dtb; do
+	rm -f moved.$tree shunted.$tree deshunted.$tree
+	run_test move_and_save $tree
+	tree1_tests moved.$tree
+	tree1_tests shunted.$tree
+	tree1_tests deshunted.$tree
+    done
 }
 
 stress_tests () {