fw_setenv: avoid writing environment when nothing has changed

In the case where one deletes an already-non-existing variable, or sets
a variable to the value it already has, there is no point in writing the
environment back, thus reducing wear on the underlying storage
device.

In the case of redundant environments, if the two environments
differ (e.g. because one is corrupt), make sure that any call of
fw_setenv causes the two to become synchronized, even if the fw_setenv
call does not change anything in the good copy.

Signed-off-by: Rasmus Villemoes <rasmus.villemoes@prevas.dk>
Acked-by: Joe Hershberger <joe.hershberger@ni.com>
diff --git a/tools/env/fw_env.c b/tools/env/fw_env.c
index 30b5a19..381739d 100644
--- a/tools/env/fw_env.c
+++ b/tools/env/fw_env.c
@@ -112,6 +112,7 @@
 	unsigned char *flags;
 	char *data;
 	enum flag_scheme flag_scheme;
+	int dirty;
 };
 
 static struct environment environment = {
@@ -506,6 +507,9 @@
 	if (!opts)
 		opts = &default_opts;
 
+	if (!environment.dirty)
+		return 0;
+
 	/*
 	 * Update CRC
 	 */
@@ -551,7 +555,8 @@
 
 	deleting = (oldval && !(value && strlen(value)));
 	creating = (!oldval && (value && strlen(value)));
-	overwriting = (oldval && (value && strlen(value)));
+	overwriting = (oldval && (value && strlen(value) &&
+				  strcmp(oldval, value)));
 
 	/* check for permission */
 	if (deleting) {
@@ -591,6 +596,7 @@
 		/* Nothing to do */
 		return 0;
 
+	environment.dirty = 1;
 	if (deleting || overwriting) {
 		if (*++nxt == '\0') {
 			*env = '\0';
@@ -1440,6 +1446,7 @@
 				"Warning: Bad CRC, using default environment\n");
 			memcpy(environment.data, default_environment,
 			       sizeof(default_environment));
+			environment.dirty = 1;
 		}
 	} else {
 		flag0 = *environment.flags;
@@ -1493,6 +1500,16 @@
 		crc1_ok = (crc1 == redundant->crc);
 		flag1 = redundant->flags;
 
+		/*
+		 * environment.data still points to ((struct
+		 * env_image_redundant *)addr0)->data. If the two
+		 * environments differ, or one has bad crc, force a
+		 * write-out by marking the environment dirty.
+		 */
+		if (memcmp(environment.data, redundant->data, ENV_SIZE) ||
+		    !crc0_ok || !crc1_ok)
+			environment.dirty = 1;
+
 		if (crc0_ok && !crc1_ok) {
 			dev_current = 0;
 		} else if (!crc0_ok && crc1_ok) {
@@ -1502,6 +1519,7 @@
 				"Warning: Bad CRC, using default environment\n");
 			memcpy(environment.data, default_environment,
 			       sizeof(default_environment));
+			environment.dirty = 1;
 			dev_current = 0;
 		} else {
 			switch (environment.flag_scheme) {