aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk7
-rw-r--r--include/rfs.h17
-rw-r--r--samsung-ipc/Makefile.am2
-rw-r--r--samsung-ipc/device/crespo/crespo_ipc.c5
-rw-r--r--samsung-ipc/ipc_util.c14
-rw-r--r--samsung-ipc/rfs.c622
6 files changed, 653 insertions, 14 deletions
diff --git a/Android.mk b/Android.mk
index 582ba6f..0525ce2 100644
--- a/Android.mk
+++ b/Android.mk
@@ -11,6 +11,7 @@ samsung-ipc_files := \
samsung-ipc/ipc.c \
samsung-ipc/ipc_util.c \
samsung-ipc/util.c \
+ samsung-ipc/rfs.c \
samsung-ipc/gen.c \
samsung-ipc/gprs.c \
samsung-ipc/misc.c \
@@ -18,10 +19,10 @@ samsung-ipc_files := \
samsung-ipc/sec.c \
samsung-ipc/device/$(TARGET_DEVICE)/$(TARGET_DEVICE)_ipc.c
+LOCAL_CFLAGS += -Iexternal/openssl/include
+LOCAL_LDFLAGS += -lcrypto
+
ifeq ($(TARGET_DEVICE),crespo)
- device_files := samsung-ipc/device/$(TARGET_DEVICE)/$(TARGET_DEVICE)_nv_data.c
- LOCAL_CFLAGS += -Iexternal/openssl/include
- LOCAL_LDFLAGS += -lcrypto
LOCAL_CFLAGS += -DDEVICE_CRESPO
endif
diff --git a/include/rfs.h b/include/rfs.h
index 7ce8cc2..3f8708d 100644
--- a/include/rfs.h
+++ b/include/rfs.h
@@ -21,11 +21,17 @@
#ifndef __RFS_H__
#define __RFS_H__
+struct ipc_client;
struct ipc_message_info;
#define IPC_RFS_NV_READ_ITEM 0x4201
#define IPC_RFS_NV_WRITE_ITEM 0x4202
+#define NV_DATA_MD5_SECRET "Samsung_Android_RIL"
+#define NV_DATA_SIZE 0x200000
+
+#define MD5_STRING_SIZE MD5_DIGEST_LENGTH * 2 + 1
+
struct ipc_rfs_io {
unsigned int offset;
unsigned int length;
@@ -37,6 +43,17 @@ struct ipc_rfs_io_confirm {
unsigned int length;
} __attribute__((__packed__));
+void md5hash2string(char *out, uint8_t *in);
+void nv_data_generate(struct ipc_client *client);
+void nv_data_md5_compute(void *data_p, int size, void *hash);
+void nv_data_md5_generate(struct ipc_client *client);
+void nv_data_backup_create(struct ipc_client *client);
+void nv_data_backup_restore(struct ipc_client *client);
+void nv_data_check(struct ipc_client *client);
+void nv_data_md5_check(struct ipc_client *client);
+int nv_data_read(struct ipc_client *client, int offset, int length, char *buf);
+int nv_data_write(struct ipc_client *client, int offset, int length, char *buf);
+
#endif
// vim:ts=4:sw=4:expandtab
diff --git a/samsung-ipc/Makefile.am b/samsung-ipc/Makefile.am
index 0a6e595..d21c172 100644
--- a/samsung-ipc/Makefile.am
+++ b/samsung-ipc/Makefile.am
@@ -19,6 +19,7 @@ libsamsung_ipc_la_SOURCES = \
ipc_util.c \
util.c \
\
+ rfs.c \
misc.c \
sec.c \
gen.c \
@@ -31,7 +32,6 @@ libsamsung_ipc_la_SOURCES = \
if WANT_PROTOCOL_VERISON_CRESPO
libsamsung_ipc_la_SOURCES += \
device/crespo/crespo_ipc.c \
- device/crespo/crespo_nv_data.c \
$(NULL)
AM_CFLAGS += -DDEVICE_CRESPO
endif
diff --git a/samsung-ipc/device/crespo/crespo_ipc.c b/samsung-ipc/device/crespo/crespo_ipc.c
index 5c890c1..8cb0501 100644
--- a/samsung-ipc/device/crespo/crespo_ipc.c
+++ b/samsung-ipc/device/crespo/crespo_ipc.c
@@ -36,7 +36,6 @@
#include <radio.h>
#include "crespo_modem_ctl.h"
-#include "crespo_nv_data.h"
#include "crespo_ipc.h"
#include "ipc_private.h"
@@ -484,8 +483,8 @@ int crespo_ipc_rfs_client_recv(struct ipc_client *client, struct ipc_message_inf
response->data = NULL;
ipc_client_log(client, "crespo_ipc_rfs_client_recv: RECV RFS (id=%d cmd=%d size=%d)!", modem_data.id, modem_data.cmd, modem_data.size);
- ipc_client_log(client, "crespo_ipc_rfs_client_recv: IPC response (aseq=0x%02x command=%s (0x%04x) type=%s)",
- response->mseq, ipc_command_to_str(IPC_COMMAND(response)), IPC_COMMAND(response), ipc_response_type_to_str(response->type));
+ ipc_client_log(client, "crespo_ipc_rfs_client_recv: IPC response (aseq=0x%02x command=%s (0x%04x))",
+ response->mseq, ipc_command_to_str(IPC_COMMAND(response)), IPC_COMMAND(response));
if(response->length > 0)
{
diff --git a/samsung-ipc/ipc_util.c b/samsung-ipc/ipc_util.c
index a9eab07..8001e5e 100644
--- a/samsung-ipc/ipc_util.c
+++ b/samsung-ipc/ipc_util.c
@@ -182,8 +182,8 @@ const char *ipc_command_to_str(int command) {
IPC_STR(IPC_SAT_CALL_PROCESSING)
IPC_STR(IPC_IMEI_START)
IPC_STR(IPC_IMEI_CHECK_DEVICE_INFO)
- default:
- return "IPC_UNKNOWN";
+ default:
+ return "IPC_UNKNOWN";
}
}
@@ -253,7 +253,7 @@ void *ipc_mtd_read(struct ipc_client *client, char *mtd_name, int size, int bloc
if(mtd_name == NULL || size <= 0 || block_size <= 0)
goto error;
- ipc_client_log(client, "mtd_read: reading 0x%x bytes from %s with 0x%x bytes block size\n", size, mtd_name, block_size);
+ ipc_client_log(client, "ipc_mtd_read: reading 0x%x bytes from %s with 0x%x bytes block size\n", size, mtd_name, block_size);
fd=open(mtd_name, O_RDONLY);
if(fd < 0)
@@ -272,7 +272,7 @@ void *ipc_mtd_read(struct ipc_client *client, char *mtd_name, int size, int bloc
offs = i * block_size;
if(ioctl(fd, MEMGETBADBLOCK, &offs) == 1)
{
- ipc_client_log(client, "mtd_read: warning: bad block at offset %lld\n", (long long int) offs);
+ ipc_client_log(client, "ipc_mtd_read: warning: bad block at offset %lld\n", (long long int) offs);
data_p+=block_size;
continue;
}
@@ -286,7 +286,7 @@ void *ipc_mtd_read(struct ipc_client *client, char *mtd_name, int size, int bloc
return mtd_p;
error:
- ipc_client_log(client, "%s: something went wrong\n", __func__);
+ ipc_client_log(client, "ipc_mtd_read: something went wrong\n");
return NULL;
}
@@ -301,7 +301,7 @@ void *ipc_file_read(struct ipc_client *client, char *file_name, int size, int bl
if(file_name == NULL || size <= 0 || block_size <= 0)
goto error;
- ipc_client_log(client, "file_read: reading 0x%x bytes from %s with 0x%x bytes block size\n", size, file_name, block_size);
+ ipc_client_log(client, "ipc_file_read: reading 0x%x bytes from %s with 0x%x bytes block size\n", size, file_name, block_size);
fd=open(file_name, O_RDONLY);
if(fd < 0)
@@ -326,7 +326,7 @@ void *ipc_file_read(struct ipc_client *client, char *file_name, int size, int bl
return file_p;
error:
- ipc_client_log(client, "%s: something went wrong\n", __func__);
+ ipc_client_log(client, "ipc_file_read: something went wrong\n");
return NULL;
}
diff --git a/samsung-ipc/rfs.c b/samsung-ipc/rfs.c
new file mode 100644
index 0000000..61659b8
--- /dev/null
+++ b/samsung-ipc/rfs.c
@@ -0,0 +1,622 @@
+/**
+ * This file is part of libsamsung-ipc.
+ *
+ * Copyright (C) 2011 Paul Kocialkowski <contact@paulk.fr>
+ *
+ * libsamsung-ipc is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libsamsung-ipc 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <openssl/md5.h>
+
+#include <radio.h>
+
+#include "ipc_private.h"
+
+void md5hash2string(char *out, uint8_t *in)
+{
+ int i;
+
+ for(i=0 ; i < MD5_DIGEST_LENGTH ; i++)
+ {
+ /* After the first iteration, we override \0. */
+ if(*in < 0x10)
+ sprintf(out, "0%x", *in);
+ else
+ sprintf(out, "%x", *in);
+ in++;
+ out+=2;
+ }
+}
+
+void nv_data_generate(struct ipc_client *client)
+{
+ ipc_client_log(client, "This feature isn't present yet\n");
+
+// nv_data_backup_create();
+}
+
+void nv_data_md5_compute(void *data_p, int size, void *hash)
+{
+ MD5_CTX ctx;
+
+// MD5((unsigned char *)nv_data_p, nv_data_stat.st_size, nv_data_md5_hash);
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, data_p, size);
+ MD5_Update(&ctx, NV_DATA_MD5_SECRET, sizeof(NV_DATA_MD5_SECRET) - 1);
+ MD5_Final(hash, &ctx);
+}
+
+void nv_data_md5_generate(struct ipc_client *client)
+{
+ uint8_t nv_data_md5_hash[MD5_DIGEST_LENGTH];
+ char *nv_data_md5_hash_string;
+ void *nv_data_p;
+ int fd;
+
+ ipc_client_log(client, "nv_data_md5_generate: enter\n");
+
+ ipc_client_log(client, "nv_data_md5_generate: generating MD5 hash\n");
+ nv_data_p=ipc_file_read(client, "/efs/nv_data.bin", NV_DATA_SIZE, NV_DATA_SIZE / 10);
+ nv_data_md5_compute(nv_data_p, NV_DATA_SIZE, nv_data_md5_hash);
+ free(nv_data_p);
+
+ /* Alloc the memory for the md5 hash string. */
+ nv_data_md5_hash_string = malloc(MD5_STRING_SIZE);
+ memset(nv_data_md5_hash_string, 0, MD5_STRING_SIZE);
+
+ md5hash2string(nv_data_md5_hash_string, nv_data_md5_hash);
+
+ ipc_client_log(client, "nv_data_md5_generate: new MD5 hash is %s\n", nv_data_md5_hash_string);
+
+ ipc_client_log(client, "nv_data_md5_generate: writing MD5 hash\n");
+ /* Write the MD5 hash in nv_data.bin.md5. */
+ fd = open("/efs/nv_data.bin.md5", O_RDWR | O_CREAT | O_TRUNC, 0644);
+
+ if(fd < 0)
+ {
+ ipc_client_log(client, "nv_data_md5_generate: fd open failed\n");
+ goto exit;
+ }
+
+ write(fd, nv_data_md5_hash_string, MD5_STRING_SIZE);
+ close(fd);
+
+exit:
+ free(nv_data_md5_hash_string);
+
+ ipc_client_log(client, "nv_data_md5_generate: exit\n");
+}
+
+void nv_data_backup_create(struct ipc_client *client)
+{
+ uint8_t nv_data_md5_hash[MD5_DIGEST_LENGTH];
+ char *nv_data_md5_hash_string;
+ char *nv_data_md5_hash_read;
+ int nv_data_write_tries = 0;
+
+ struct stat nv_stat;
+ void *nv_data_p;
+ void *nv_data_bak_p;
+ uint8_t data;
+
+ int fd;
+ int rc;
+ int i;
+
+ ipc_client_log(client, "nv_data_backup_create: enter\n");
+
+ if(stat("/efs/nv_data.bin", &nv_stat) < 0)
+ {
+ ipc_client_log(client, "nv_data_backup_create: nv_data.bin missing\n");
+ nv_data_generate(client);
+ }
+
+ if(nv_stat.st_size != NV_DATA_SIZE)
+ {
+ ipc_client_log(client, "nv_data_backup_create: wrong nv_data.bin size\n");
+ nv_data_generate(client);
+ return;
+ }
+
+ if(stat("/efs/nv_data.bin.md5", &nv_stat) < 0)
+ {
+ ipc_client_log(client, "nv_data_backup_create: nv_data.bin.md5 missing\n");
+ nv_data_generate(client);
+ return;
+ }
+
+ /* Alloc the memory for the md5 hashes strings. */
+ nv_data_md5_hash_string=malloc(MD5_STRING_SIZE);
+ nv_data_md5_hash_read=malloc(MD5_STRING_SIZE);
+
+ memset(nv_data_md5_hash_read, 0, MD5_STRING_SIZE);
+ memset(nv_data_md5_hash_string, 0, MD5_STRING_SIZE);
+
+ /* Read the content of the backup file. */
+ nv_data_p=ipc_file_read(client, "/efs/nv_data.bin", NV_DATA_SIZE, NV_DATA_SIZE / 10);
+
+ /* Compute the backup file MD5 hash. */
+ nv_data_md5_compute(nv_data_p, NV_DATA_SIZE, nv_data_md5_hash);
+ md5hash2string(nv_data_md5_hash_string, nv_data_md5_hash);
+
+ /* Read the stored backup file MD5 hash. */
+ fd=open("/efs/nv_data.bin.md5", O_RDONLY);
+ read(fd, nv_data_md5_hash_read, MD5_STRING_SIZE);
+ close(fd);
+
+ /* Add 0x0 to end the string: not sure this is always part of the file. */
+ nv_data_md5_hash_read[MD5_STRING_SIZE - 1]='\0';
+
+ ipc_client_log(client, "nv_data_backup_create: backup file computed MD5: %s read MD5: %s\n",
+ nv_data_md5_hash_string, nv_data_md5_hash_read);
+
+ if(strcmp(nv_data_md5_hash_string, nv_data_md5_hash_read) != 0)
+ {
+ ipc_client_log(client, "nv_data_backup_create: MD5 hash mismatch on backup file\n");
+ ipc_client_log(client, "nv_data_backup_create: Consider the computed one as correct\n");
+
+ fd=open("/efs/nv_data.bin.md5", O_WRONLY);
+ read(fd, nv_data_md5_hash_string, MD5_STRING_SIZE);
+ close(fd);
+
+ /*
+ nv_data_backup_generate(client);
+ nv_data_backup_create(client);
+ return;
+ */
+ }
+
+ /* Assume the read string is the computated one */
+ memcpy(nv_data_md5_hash_read, nv_data_md5_hash_string, MD5_STRING_SIZE);
+ memset(nv_data_md5_hash_string, 0, MD5_STRING_SIZE);
+
+nv_data_backup_create_write:
+ while(nv_data_write_tries < 5)
+ {
+ ipc_client_log(client, "nv_data_backup_create: .nv_data.bak write try #%d\n", nv_data_write_tries + 1);
+
+ fd=open("/efs/.nv_data.bak", O_RDWR | O_CREAT | O_TRUNC, 0644);
+ if(fd < 0)
+ {
+ ipc_client_log(client, "nv_data_backup_create: negative fd while opening /efs/.nv_data.bak, error: %s\n", strerror(errno));
+ nv_data_write_tries++;
+ continue;
+ }
+
+ rc = write(fd, nv_data_p, NV_DATA_SIZE);
+ if(rc < NV_DATA_SIZE)
+ {
+ ipc_client_log(client, "nv_data_backup_create: wrote less (%d) than what we expected (%d) on /efs/.nv_data.bak, error: %s\n", strerror(errno));
+ close(fd);
+ nv_data_write_tries++;
+ continue;
+ }
+
+ close(fd);
+ break;
+ }
+
+ if(nv_data_write_tries == 5)
+ {
+ ipc_client_log(client, "nv_data_backup_create: writing nv_data.bin to .nv_data.bak failed too many times\n");
+ unlink("/efs/.nv_data.bak");
+ goto exit;
+
+ }
+
+ /* Read the newly-written .nv_data.bak. */
+ nv_data_bak_p=ipc_file_read(client, "/efs/.nv_data.bak", NV_DATA_SIZE, NV_DATA_SIZE / 10);
+
+ /* Compute the MD5 hash for nv_data.bin. */
+ nv_data_md5_compute(nv_data_bak_p, NV_DATA_SIZE, nv_data_md5_hash);
+ md5hash2string(nv_data_md5_hash_string, nv_data_md5_hash);
+
+ free(nv_data_bak_p);
+
+ ipc_client_log(client, "nv_data_backup_create: written file computed MD5: %s read MD5: %s\n",
+ nv_data_md5_hash_string, nv_data_md5_hash_read);
+
+ /* Make sure both hashes are the same. */
+ if(strcmp(nv_data_md5_hash_string, nv_data_md5_hash_read) != 0)
+ {
+ ipc_client_log(client, "nv_data_backup_create: MD5 hash mismatch on written file\n");
+ ipc_client_log(client, "nv_data_backup_create: Writing again\n");
+
+ goto nv_data_backup_create_write;
+ }
+
+ /* Write the MD5 hash in .nv_data.bak.md5. */
+ fd=open("/efs/.nv_data.bak.md5", O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ write(fd, nv_data_md5_hash_read, MD5_STRING_SIZE);
+ close(fd);
+
+ /* Write the correct .nv_state. */
+ fd=open("/efs/.nv_state", O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ data='1';
+ write(fd, &data, sizeof(data));
+ close(fd);
+
+exit:
+ free(nv_data_p);
+ free(nv_data_md5_hash_string);
+ free(nv_data_md5_hash_read);
+
+ ipc_client_log(client, "nv_data_backup_create: exit\n");
+}
+
+void nv_data_backup_restore(struct ipc_client *client)
+{
+ uint8_t nv_data_md5_hash[MD5_DIGEST_LENGTH];
+ char *nv_data_md5_hash_string;
+ char *nv_data_md5_hash_read;
+ int nv_data_write_tries = 0;
+
+ struct stat nv_stat;
+ void *nv_data_p;
+ void *nv_data_bak_p;
+ uint8_t data;
+
+ int fd;
+ int rc;
+ int i;
+
+ ipc_client_log(client, "nv_data_backup_restore: enter\n");
+
+ if(stat("/efs/.nv_data.bak", &nv_stat) < 0)
+ {
+ ipc_client_log(client, "nv_data_backup_restore: .nv_data.bak missing\n");
+ nv_data_generate(client);
+ nv_data_backup_create(client);
+ return;
+ }
+
+ if(nv_stat.st_size != NV_DATA_SIZE)
+ {
+ ipc_client_log(client, "nv_data_backup_restore: wrong .nv_data.bak size\n");
+ nv_data_generate(client);
+ nv_data_backup_create(client);
+ return;
+ }
+
+ if(stat("/efs/.nv_data.bak.md5", &nv_stat) < 0)
+ {
+ ipc_client_log(client, "nv_data_backup_restore: .nv_data.bak.md5 missing\n");
+ nv_data_generate(client);
+ nv_data_backup_create(client);
+ return;
+ }
+
+ /* Alloc the memory for the md5 hashes strings. */
+ nv_data_md5_hash_string=malloc(MD5_STRING_SIZE);
+ nv_data_md5_hash_read=malloc(MD5_STRING_SIZE);
+
+ memset(nv_data_md5_hash_read, 0, MD5_STRING_SIZE);
+ memset(nv_data_md5_hash_string, 0, MD5_STRING_SIZE);
+
+ /* Read the content of the backup file. */
+ nv_data_bak_p=ipc_file_read(client, "/efs/.nv_data.bak", NV_DATA_SIZE, NV_DATA_SIZE / 10);
+
+ /* Compute the backup file MD5 hash. */
+ nv_data_md5_compute(nv_data_bak_p, NV_DATA_SIZE, nv_data_md5_hash);
+ md5hash2string(nv_data_md5_hash_string, nv_data_md5_hash);
+
+ /* Read the stored backup file MD5 hash. */
+ fd=open("/efs/.nv_data.bak.md5", O_RDONLY);
+ read(fd, nv_data_md5_hash_read, MD5_STRING_SIZE);
+ close(fd);
+
+ /* Add 0x0 to end the string: not sure this is always part of the file. */
+ nv_data_md5_hash_read[MD5_STRING_SIZE - 1]='\0';
+
+ ipc_client_log(client, "nv_data_backup_restore: backup file computed MD5: %s read MD5: %s\n",
+ nv_data_md5_hash_string, nv_data_md5_hash_read);
+
+ if(strcmp(nv_data_md5_hash_string, nv_data_md5_hash_read) != 0)
+ {
+ ipc_client_log(client, "nv_data_backup_restore: MD5 hash mismatch on backup file\n");
+ ipc_client_log(client, "nv_data_backup_restore: Consider the computed one as correct\n");
+
+ fd=open("/efs/.nv_data.bak.md5", O_WRONLY);
+ read(fd, nv_data_md5_hash_string, MD5_STRING_SIZE);
+ close(fd);
+
+ /*
+ nv_data_backup_generate(client);
+ nv_data_backup_create(client);
+ return;
+ */
+ }
+
+ /* Assume the read string is the computated one */
+ memcpy(nv_data_md5_hash_read, nv_data_md5_hash_string, MD5_STRING_SIZE);
+ memset(nv_data_md5_hash_string, 0, MD5_STRING_SIZE);
+
+nv_data_backup_restore_write:
+ while(nv_data_write_tries < 5)
+ {
+ ipc_client_log(client, "nv_data_backup_restore: nv_data.bin write try #%d\n", nv_data_write_tries + 1);
+
+ fd=open("/efs/nv_data.bin", O_RDWR | O_CREAT | O_TRUNC, 0644);
+ if(fd < 0)
+ {
+ ipc_client_log(client, "nv_data_backup_restore: negative fd while opening /efs/nv_data.bin, error: %s\n", strerror(errno));
+ nv_data_write_tries++;
+ continue;
+ }
+
+ rc = write(fd, nv_data_bak_p, NV_DATA_SIZE);
+ if(rc < NV_DATA_SIZE)
+ {
+ ipc_client_log(client, "nv_data_backup_restore: wrote less (%d) than what we expected (%d) on /efs/nv_data.bin, error: %s\n", strerror(errno));
+ close(fd);
+ nv_data_write_tries++;
+ continue;
+ }
+
+ close(fd);
+ break;
+ }
+
+ if(nv_data_write_tries == 5)
+ {
+ ipc_client_log(client, "nv_data_backup_restore: writing the backup to nv_data.bin failed too many times\n");
+ unlink("/efs/nv_data.bin");
+ goto exit;
+
+ }
+
+ /* Read the newly-written nv_data.bin. */
+ nv_data_p=ipc_file_read(client, "/efs/nv_data.bin", NV_DATA_SIZE, NV_DATA_SIZE / 10);
+
+ /* Compute the MD5 hash for nv_data.bin. */
+ nv_data_md5_compute(nv_data_p, NV_DATA_SIZE, nv_data_md5_hash);
+ md5hash2string(nv_data_md5_hash_string, nv_data_md5_hash);
+
+ free(nv_data_p);
+
+ ipc_client_log(client, "nv_data_backup_restore: written file computed MD5: %s read MD5: %s\n",
+ nv_data_md5_hash_string, nv_data_md5_hash_read);
+
+ /* Make sure both hashes are the same. */
+ if(strcmp(nv_data_md5_hash_string, nv_data_md5_hash_read) != 0)
+ {
+ ipc_client_log(client, "nv_data_backup_restore: MD5 hash mismatch on written file\n");
+ ipc_client_log(client, "nv_data_backup_restore: Writing again\n");
+
+ goto nv_data_backup_restore_write;
+ }
+
+ /* Write the MD5 hash in nv_data.bin.md5. */
+ fd=open("/efs/nv_data.bin.md5", O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ write(fd, nv_data_md5_hash_read, MD5_STRING_SIZE);
+ close(fd);
+
+ /* Write the correct .nv_state. */
+ fd=open("/efs/.nv_state", O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ data='1';
+ write(fd, &data, sizeof(data));
+ close(fd);
+
+exit:
+ free(nv_data_bak_p);
+ free(nv_data_md5_hash_string);
+ free(nv_data_md5_hash_read);
+
+ ipc_client_log(client, "nv_data_backup_restore: exit\n");
+}
+
+void nv_data_check(struct ipc_client *client)
+{
+ struct stat nv_stat;
+ int nv_state_fd=-1;
+ int nv_state=0;
+
+ ipc_client_log(client, "nv_data_check: enter\n");
+
+ if(stat("/efs/nv_data.bin", &nv_stat) < 0)
+ {
+ ipc_client_log(client, "nv_data_check: nv_data.bin missing\n");
+ nv_data_backup_restore(client);
+ stat("/efs/nv_data.bin", &nv_stat);
+ }
+
+ if(nv_stat.st_size != NV_DATA_SIZE)
+ {
+ ipc_client_log(client, "nv_data_check: wrong nv_data.bin size\n");
+ nv_data_backup_restore(client);
+ }
+
+ if(stat("/efs/nv_data.bin.md5", &nv_stat) < 0)
+ {
+ ipc_client_log(client, "nv_data_check: nv_data.bin.md5 missing\n");
+ nv_data_backup_restore(client);
+ }
+
+ if(stat("/efs/.nv_data.bak", &nv_stat) < 0 || stat("/efs/.nv_data.bak.md5", &nv_stat) < 0)
+ {
+ ipc_client_log(client, "nv_data_check: .nv_data.bak or .nv_data.bak.md5 missing\n");
+ nv_data_backup_create(client);
+ }
+
+ nv_state_fd=open("/efs/.nv_state", O_RDONLY);
+
+ if(nv_state_fd < 0 || fstat(nv_state_fd, &nv_stat) < 0)
+ {
+ ipc_client_log(client, "nv_data_check: .nv_state missing\n");
+ nv_data_backup_restore(client);
+ }
+
+ read(nv_state_fd, &nv_state, sizeof(nv_state));
+
+ close(nv_state_fd);
+
+ if(nv_state != '1')
+ {
+ ipc_client_log(client, "nv_data_check: bad nv_state\n");
+ nv_data_backup_restore(client);
+ }
+
+ ipc_client_log(client, "nv_data_check: everything should be alright\n");
+ ipc_client_log(client, "nv_data_check: exit\n");
+}
+
+void nv_data_md5_check(struct ipc_client *client)
+{
+ struct stat nv_stat;
+ uint8_t nv_data_md5_hash[MD5_DIGEST_LENGTH];
+ char *nv_data_md5_hash_string;
+ char *nv_data_md5_hash_read;
+ void *nv_data_p;
+
+ int fd;
+ uint8_t *data_p;
+
+ ipc_client_log(client, "nv_data_md5_check: enter\n");
+
+ nv_data_md5_hash_string=malloc(MD5_STRING_SIZE);
+ nv_data_md5_hash_read=malloc(MD5_STRING_SIZE);
+
+ memset(nv_data_md5_hash_read, 0, MD5_STRING_SIZE);
+ memset(nv_data_md5_hash_string, 0, MD5_STRING_SIZE);
+
+ nv_data_p=ipc_file_read(client, "/efs/nv_data.bin", NV_DATA_SIZE, 1024);
+ data_p=nv_data_p;
+
+ nv_data_md5_compute(data_p, NV_DATA_SIZE, nv_data_md5_hash);
+
+ md5hash2string(nv_data_md5_hash_string, nv_data_md5_hash);
+
+ free(nv_data_p);
+
+ fd=open("/efs/nv_data.bin.md5", O_RDONLY);
+
+ /* Read the md5 stored in the file. */
+ read(fd, nv_data_md5_hash_read, MD5_STRING_SIZE);
+
+ /* Add 0x0 to end the string: not sure this is part of the file. */
+ nv_data_md5_hash_read[MD5_STRING_SIZE - 1]='\0';
+
+ ipc_client_log(client, "nv_data_md5_check: computed MD5: %s read MD5: %s\n",
+ nv_data_md5_hash_string, nv_data_md5_hash_read);
+
+ if(strcmp(nv_data_md5_hash_string, nv_data_md5_hash_read) != 0)
+ {
+ ipc_client_log(client, "nv_data_md5_check: MD5 hash mismatch\n");
+ nv_data_backup_restore(client);
+ }
+
+ free(nv_data_md5_hash_string);
+ free(nv_data_md5_hash_read);
+
+ ipc_client_log(client, "nv_data_md5_check: exit\n");
+}
+
+int nv_data_read(struct ipc_client *client, int offset, int length, char *buf)
+{
+ int fd;
+ int rc;
+
+ ipc_client_log(client, "nv_data_read: enter\n");
+
+ if(offset <= 0 || length <= 0) {
+ ipc_client_log(client, "nv_data_read: offset or length <= 0\n");
+ return -1;
+ }
+
+ if(buf == NULL) {
+ ipc_client_log(client, "nv_data_read: provided output buf is NULL\n");
+ return -1;
+ }
+
+ nv_data_check(client);
+
+ fd = open("/efs/nv_data.bin", O_RDONLY);
+
+ if(fd < 0) {
+ ipc_client_log(client, "nv_data_read: nv_data file fd is negative\n");
+ return -1;
+ }
+
+ lseek(fd, offset, SEEK_SET);
+
+ rc = read(fd, buf, length);
+
+ if(rc < length) {
+ ipc_client_log(client, "nv_data_read: read less than what we expected\n");
+ return -1;
+ }
+
+ ipc_client_log(client, "nv_data_read: exit\n");
+
+ return 0;
+}
+
+int nv_data_write(struct ipc_client *client, int offset, int length, char *buf)
+{
+ int fd;
+ int rc;
+
+ ipc_client_log(client, "nv_data_write: enter\n");
+
+ if(offset <= 0 || length <= 0) {
+ ipc_client_log(client, "nv_data_write: offset or length <= 0\n");
+ return -1;
+ }
+
+ if(buf == NULL) {
+ ipc_client_log(client, "nv_data_write: provided input buf is NULL\n");
+ return -1;
+ }
+
+ nv_data_check(client);
+
+ fd = open("/efs/nv_data.bin", O_WRONLY);
+
+ if(fd < 0) {
+ ipc_client_log(client, "nv_data_write: nv_data file fd is negative\n");
+ return -1;
+ }
+
+ lseek(fd, offset, SEEK_SET);
+
+ rc = write(fd, buf, length);
+
+ close(fd);
+
+ if(rc < length) {
+ ipc_client_log(client, "nv_data_write: wrote less (%d) than what we expected (%d), error: %s, restoring backup\n", rc, length, strerror(errno));
+ nv_data_backup_restore(client);
+ return -1;
+ }
+
+ ipc_client_log(client, "nv_data_write: writing new md5sum\n");
+ nv_data_md5_generate(client);
+
+ ipc_client_log(client, "nv_data_write: exit\n");
+
+ return 0;
+}
+
+// vim:ts=4:sw=4:expandtab