rfs.c: Fix coding style and compilation warnings
[libsamsung-ipc.git] / samsung-ipc / rfs.c
1 /*
2  * This file is part of libsamsung-ipc.
3  *
4  * Copyright (C) 2011 Paul Kocialkowski <contact@paulk.fr>
5  *
6  * libsamsung-ipc is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * libsamsung-ipc is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with libsamsung-ipc.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stdint.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <sys/stat.h>
29
30 #include <openssl/md5.h>
31
32 #include <samsung-ipc.h>
33
34 #include "ipc.h"
35 #include "util.h"
36
37 void md5hash2string(char *out, unsigned char *in)
38 {
39     int i;
40
41     for (i = 0; i < MD5_DIGEST_LENGTH; i++)
42     {
43         /* After the first iteration, we override \0. */
44         if (*in < 0x10)
45             sprintf(out, "0%x", *in);
46         else
47             sprintf(out, "%x", *in);
48
49         in++;
50         out += 2;
51     }
52 }
53
54 char *nv_data_path(struct ipc_client *client)
55 {
56     if (client == NULL ||
57         client->nv_data_specs == NULL ||
58         client->nv_data_specs->nv_data_path == NULL)
59         return NV_DATA_PATH_DEFAULT;
60
61     return client->nv_data_specs->nv_data_path;
62 }
63
64 char *nv_data_md5_path(struct ipc_client *client)
65 {
66     if (client == NULL ||
67         client->nv_data_specs == NULL ||
68         client->nv_data_specs->nv_data_md5_path == NULL)
69         return NV_DATA_MD5_PATH_DEFAULT;
70
71     return client->nv_data_specs->nv_data_md5_path;
72 }
73
74 char *nv_data_bak_path(struct ipc_client *client)
75 {
76     if (client == NULL ||
77         client->nv_data_specs == NULL ||
78         client->nv_data_specs->nv_data_bak_path == NULL)
79         return NV_DATA_BAK_PATH_DEFAULT;
80
81     return client->nv_data_specs->nv_data_bak_path;
82 }
83
84 char *nv_data_md5_bak_path(struct ipc_client *client)
85 {
86     if (client == NULL ||
87         client->nv_data_specs == NULL ||
88         client->nv_data_specs->nv_data_md5_bak_path == NULL)
89         return NV_DATA_MD5_BAK_PATH_DEFAULT;
90
91     return client->nv_data_specs->nv_data_md5_bak_path;
92 }
93
94 char *nv_state_path(struct ipc_client *client)
95 {
96     if (client == NULL ||
97         client->nv_data_specs == NULL ||
98         client->nv_data_specs->nv_state_path == NULL)
99         return NV_STATE_PATH_DEFAULT;
100
101     return client->nv_data_specs->nv_state_path;
102 }
103
104 char *nv_data_secret(struct ipc_client *client)
105 {
106     if (client == NULL ||
107         client->nv_data_specs == NULL ||
108         client->nv_data_specs->nv_data_secret == NULL)
109         return NV_DATA_SECRET_DEFAULT;
110
111     return client->nv_data_specs->nv_data_secret;
112 }
113
114 int nv_data_size(struct ipc_client *client)
115 {
116     if (client == NULL ||
117         client->nv_data_specs == NULL ||
118         client->nv_data_specs->nv_data_size == 0)
119         return NV_DATA_SIZE_DEFAULT;
120
121     return client->nv_data_specs->nv_data_size;
122 }
123
124 int nv_data_chunk_size(struct ipc_client *client)
125 {
126     if (client == NULL ||
127         client->nv_data_specs == NULL ||
128         client->nv_data_specs->nv_data_chunk_size == 0)
129         return NV_DATA_CHUNK_SIZE_DEFAULT;
130
131     return client->nv_data_specs->nv_data_chunk_size;
132 }
133
134 void nv_data_generate(struct ipc_client *client)
135 {
136     return;
137 }
138
139 void nv_data_md5_compute(void *data_p, int size, char *secret, void *hash)
140 {
141     MD5_CTX ctx;
142
143     MD5_Init(&ctx);
144     MD5_Update(&ctx, data_p, size);
145     MD5_Update(&ctx, secret, strlen(secret));
146     MD5_Final(hash, &ctx);
147 }
148
149 void nv_data_md5_generate(struct ipc_client *client)
150 {
151     uint8_t nv_data_md5_hash[MD5_DIGEST_LENGTH];
152     char *nv_data_md5_hash_string = NULL;
153     void *nv_data_p = NULL;
154     int fd;
155     int rc;
156
157     ipc_client_log(client, "nv_data_md5_generate: enter");
158
159     ipc_client_log(client, "nv_data_md5_generate: generating MD5 hash");
160     nv_data_p = file_data_read(nv_data_path(client),
161         nv_data_size(client), nv_data_chunk_size(client));
162     nv_data_md5_compute(nv_data_p, nv_data_size(client), nv_data_secret(client), nv_data_md5_hash);
163     free(nv_data_p);
164
165     /* Alloc the memory for the md5 hash string. */
166     nv_data_md5_hash_string = malloc(MD5_STRING_SIZE);
167     memset(nv_data_md5_hash_string, 0, MD5_STRING_SIZE);
168
169     md5hash2string(nv_data_md5_hash_string, nv_data_md5_hash);
170
171     ipc_client_log(client, "nv_data_md5_generate: new MD5 hash is %s", nv_data_md5_hash_string);
172
173     ipc_client_log(client, "nv_data_md5_generate: writing MD5 hash");
174
175     /* Write the MD5 hash in nv_data.bin.md5. */
176     fd = open(nv_data_md5_path(client), O_RDWR | O_CREAT | O_TRUNC, 0644);
177     if (fd < 0)
178     {
179         ipc_client_log(client, "nv_data_md5_generate: fd open failed");
180         goto exit;
181     }
182
183     rc = write(fd, nv_data_md5_hash_string, MD5_STRING_SIZE);
184     if (rc < 0)
185     {
186         ipc_client_log(client, "nv_data_md5_generate: failed to write MD5 hash to file");
187         close(fd);
188         goto exit;
189     }
190
191     close(fd);
192
193 exit:
194     if (nv_data_md5_hash_string != NULL)
195         free(nv_data_md5_hash_string);
196
197     ipc_client_log(client, "nv_data_md5_generate: exit");
198 }
199
200 void nv_data_backup_create(struct ipc_client *client)
201 {
202     uint8_t nv_data_md5_hash[MD5_DIGEST_LENGTH];
203     char *nv_data_md5_hash_string = NULL;
204     char *nv_data_md5_hash_read = NULL;
205     int nv_data_write_tries = 0;
206
207     struct stat nv_stat;
208     void *nv_data_p = NULL;
209     void *nv_data_bak_p = NULL;
210     uint8_t data;
211
212     int fd;
213     int rc;
214     int i;
215
216     ipc_client_log(client, "nv_data_backup_create: enter");
217
218     if (stat(nv_data_path(client), &nv_stat) < 0)
219     {
220         ipc_client_log(client, "nv_data_backup_create: nv_data.bin missing");
221         nv_data_generate(client);
222     }
223
224     if (nv_stat.st_size != nv_data_size(client))
225     {
226         ipc_client_log(client, "nv_data_backup_create: wrong nv_data.bin size");
227         nv_data_generate(client);
228         return;
229     }
230
231     if (stat(nv_data_md5_path(client), &nv_stat) < 0)
232     {
233         ipc_client_log(client, "nv_data_backup_create: nv_data.bin.md5 missing");
234         nv_data_generate(client);
235         return;
236     }
237
238     /* Alloc the memory for the md5 hashes strings. */
239     nv_data_md5_hash_string = malloc(MD5_STRING_SIZE);
240     nv_data_md5_hash_read = malloc(MD5_STRING_SIZE);
241
242     memset(nv_data_md5_hash_read, 0, MD5_STRING_SIZE);
243     memset(nv_data_md5_hash_string, 0, MD5_STRING_SIZE);
244
245     /* Read the content of the backup file. */
246     nv_data_p = file_data_read(nv_data_path(client),
247         nv_data_size(client), nv_data_chunk_size(client));
248
249     /* Compute the backup file MD5 hash. */
250     nv_data_md5_compute(nv_data_p, nv_data_size(client), nv_data_secret(client), nv_data_md5_hash);
251     md5hash2string(nv_data_md5_hash_string, nv_data_md5_hash);
252
253     /* Read the stored backup file MD5 hash. */
254     fd = open(nv_data_md5_path(client), O_RDONLY);
255     if (fd < 0)
256     {
257         ipc_client_log(client, "nv_data_backup_create: failed to openstored backup file with MD5 hash");
258         goto exit;
259     }
260
261     rc = read(fd, nv_data_md5_hash_read, MD5_STRING_SIZE);
262     if (rc < 0)
263     {
264         ipc_client_log(client, "nv_data_backup_create: failed to read MD5 hash from backup file");
265         close(fd);
266         goto exit;
267     }
268
269     close(fd);
270
271     /* Add 0x0 to end the string: not sure this is always part of the file. */
272     nv_data_md5_hash_read[MD5_STRING_SIZE - 1] = '\0';
273
274     ipc_client_log(client, "nv_data_backup_create: backup file computed MD5: %s read MD5: %s",
275         nv_data_md5_hash_string, nv_data_md5_hash_read);
276
277     if (strcmp(nv_data_md5_hash_string, nv_data_md5_hash_read) != 0)
278     {
279         ipc_client_log(client, "nv_data_backup_create: MD5 hash mismatch on backup file");
280         ipc_client_log(client, "nv_data_backup_create: Consider the computed one as correct");
281
282         fd = open(nv_data_md5_path(client), O_WRONLY);
283         if (fd < 0)
284         {
285             ipc_client_log(client, "nv_data_backup_create: failed to open file with MD5 hash of data file");
286             goto exit;
287         }
288
289         rc = read(fd, nv_data_md5_hash_string, MD5_STRING_SIZE);
290         if (rc < 0)
291         {
292             ipc_client_log(client, "nv_data_backup_create: failed to read MD5 hash for data file from file");
293             goto exit;
294         }
295
296         close(fd);
297
298         /*
299         nv_data_backup_generate(client);
300         nv_data_backup_create(client);
301         return;
302         */
303     }
304
305     /* Assume the read string is the computated one */
306     memcpy(nv_data_md5_hash_read, nv_data_md5_hash_string, MD5_STRING_SIZE);
307     memset(nv_data_md5_hash_string, 0, MD5_STRING_SIZE);
308
309 nv_data_backup_create_write:
310    while (nv_data_write_tries < 5)
311     {
312         ipc_client_log(client, "nv_data_backup_create: .nv_data.bak write try #%d", nv_data_write_tries + 1);
313
314         fd = open(nv_data_bak_path(client), O_RDWR | O_CREAT | O_TRUNC, 0644);
315         if (fd < 0)
316         {
317             ipc_client_log(client, "nv_data_backup_create: negative fd while opening /efs/.nv_data.bak, error: %s", strerror(errno));
318             nv_data_write_tries++;
319             continue;
320         }
321
322         rc = write(fd, nv_data_p, nv_data_size(client));
323         if (rc < nv_data_size(client))
324         {
325             ipc_client_log(client, "nv_data_backup_create: wrote less (%d) than what we expected (%d) on /efs/.nv_data.bak, error: %s", strerror(errno));
326             close(fd);
327             nv_data_write_tries++;
328             continue;
329         }
330
331         close(fd);
332         break;
333     }
334
335     if (nv_data_write_tries == 5)
336     {
337         ipc_client_log(client, "nv_data_backup_create: writing nv_data.bin to .nv_data.bak failed too many times");
338         unlink(nv_data_bak_path(client));
339         goto exit;
340     }
341
342     /* Read the newly-written .nv_data.bak. */
343     nv_data_bak_p = file_data_read(nv_data_bak_path(client), 
344         nv_data_size(client), nv_data_chunk_size(client));
345
346     /* Compute the MD5 hash for nv_data.bin. */
347     nv_data_md5_compute(nv_data_bak_p, nv_data_size(client), nv_data_secret(client), nv_data_md5_hash);
348     md5hash2string(nv_data_md5_hash_string, nv_data_md5_hash);
349
350     if (nv_data_bak_p != NULL)
351         free(nv_data_bak_p);
352
353     ipc_client_log(client, "nv_data_backup_create: written file computed MD5: %s read MD5: %s",
354         nv_data_md5_hash_string, nv_data_md5_hash_read);
355
356     /* Make sure both hashes are the same. */
357     if (strcmp(nv_data_md5_hash_string, nv_data_md5_hash_read) != 0)
358     {
359         ipc_client_log(client, "nv_data_backup_create: MD5 hash mismatch on written file");
360         ipc_client_log(client, "nv_data_backup_create: Writing again");
361
362         goto nv_data_backup_create_write;
363     }
364
365     /* Write the MD5 hash in .nv_data.bak.md5. */
366     fd = open(nv_data_md5_bak_path(client), O_WRONLY | O_CREAT | O_TRUNC, 0644);
367     if (fd < 0)
368     {
369         ipc_client_log(client, "nv_data_backup_create: failed to open MD5 hash file");
370         goto exit;
371     }
372
373     rc = write(fd, nv_data_md5_hash_read, MD5_STRING_SIZE);
374     if (rc < 0)
375     {
376         ipc_client_log(client, "nv_data_backup_create: failed to write MD5 hash to file");
377         close(fd);
378         goto exit;
379     }
380     close(fd);
381
382     /* Write the correct .nv_state. */
383     fd = open(nv_state_path(client), O_WRONLY | O_CREAT | O_TRUNC, 0644);
384     if (fd < 0)
385     {
386         ipc_client_log(client, "nv_data_backup_create: failed to open NV state file");
387         goto exit;
388     }
389
390     data = '1';
391     rc = write(fd, &data, sizeof(data));
392     if (rc < 0)
393     {
394         ipc_client_log(client, "nv_data_backup_create: failed to write state of NV data");
395         close(fd);
396         goto exit;
397     }
398
399     close(fd);
400
401 exit:
402     if (nv_data_p != NULL)
403         free(nv_data_p);
404     if (nv_data_md5_hash_string != NULL)
405         free(nv_data_md5_hash_string);
406     if (nv_data_md5_hash_read)
407         free(nv_data_md5_hash_read);
408
409     ipc_client_log(client, "nv_data_backup_create: exit");
410 }
411
412 void nv_data_backup_restore(struct ipc_client *client)
413 {
414     uint8_t nv_data_md5_hash[MD5_DIGEST_LENGTH];
415     char *nv_data_md5_hash_string = NULL;
416     char *nv_data_md5_hash_read = NULL;
417     int nv_data_write_tries = 0;
418
419     struct stat nv_stat;
420     void *nv_data_p = NULL;
421     void *nv_data_bak_p = NULL;
422     uint8_t data;
423
424     int fd;
425     int rc;
426     int i;
427
428     ipc_client_log(client, "nv_data_backup_restore: enter");
429
430     if (stat(nv_data_bak_path(client), &nv_stat) < 0)
431     {
432         ipc_client_log(client, "nv_data_backup_restore: .nv_data.bak missing");
433         nv_data_generate(client);
434         nv_data_backup_create(client);
435         return;
436     }
437
438     if (nv_stat.st_size != nv_data_size(client))
439     {
440         ipc_client_log(client, "nv_data_backup_restore: wrong .nv_data.bak size");
441         nv_data_generate(client);
442         nv_data_backup_create(client);
443         return;
444     }
445
446     if (stat(nv_data_md5_bak_path(client), &nv_stat) < 0)
447     {
448         ipc_client_log(client, "nv_data_backup_restore: .nv_data.bak.md5 missing");
449         nv_data_generate(client);
450         nv_data_backup_create(client);
451         return;
452     }
453
454     /* Alloc the memory for the md5 hashes strings. */
455     nv_data_md5_hash_string = malloc(MD5_STRING_SIZE);
456     nv_data_md5_hash_read = malloc(MD5_STRING_SIZE);
457
458     memset(nv_data_md5_hash_read, 0, MD5_STRING_SIZE);
459     memset(nv_data_md5_hash_string, 0, MD5_STRING_SIZE);
460
461     /* Read the content of the backup file. */
462     nv_data_bak_p = file_data_read(nv_data_bak_path(client),
463         nv_data_size(client), nv_data_chunk_size(client));
464
465     /* Compute the backup file MD5 hash. */
466     nv_data_md5_compute(nv_data_bak_p, nv_data_size(client), nv_data_secret(client), nv_data_md5_hash);
467     md5hash2string(nv_data_md5_hash_string, nv_data_md5_hash);
468
469     /* Read the stored backup file MD5 hash. */
470     fd = open(nv_data_md5_bak_path(client), O_RDONLY);
471     rc = read(fd, nv_data_md5_hash_read, MD5_STRING_SIZE);
472     if (rc < 0)
473     {
474         ipc_client_log(client, "nv_data_backup_restore: Failed to read md5 hash for stored back file");
475         close(fd);
476         goto exit;
477     }
478
479     close(fd);
480
481     /* Add 0x0 to end the string: not sure this is always part of the file. */
482     nv_data_md5_hash_read[MD5_STRING_SIZE - 1] = '\0';
483
484     ipc_client_log(client, "nv_data_backup_restore: backup file computed MD5: %s read MD5: %s",
485         nv_data_md5_hash_string, nv_data_md5_hash_read);
486
487     if (strcmp(nv_data_md5_hash_string, nv_data_md5_hash_read) != 0)
488     {
489         ipc_client_log(client, "nv_data_backup_restore: MD5 hash mismatch on backup file");
490         ipc_client_log(client, "nv_data_backup_restore: Consider the computed one as correct");
491
492         fd = open(nv_data_md5_bak_path(client), O_WRONLY);
493         if (fd < 0)
494         {
495             ipc_client_log(client, "nv_data_backup_restore: failed to open MD5 hash backup file");
496             goto exit;
497         }
498
499         rc = read(fd, nv_data_md5_hash_string, MD5_STRING_SIZE);
500         if (rc < 0)
501         {
502             ipc_client_log(client, "nv_data_backup_restore: failed to read MD5 hash from backup file");
503             close(fd);
504             goto exit;
505         }
506
507         close(fd);
508
509         /*
510         nv_data_backup_generate(client);
511         nv_data_backup_create(client);
512         return;
513         */
514     }
515
516     /* Assume the read string is the computated one */
517     memcpy(nv_data_md5_hash_read, nv_data_md5_hash_string, MD5_STRING_SIZE);
518     memset(nv_data_md5_hash_string, 0, MD5_STRING_SIZE);
519
520 nv_data_backup_restore_write:
521    while (nv_data_write_tries < 5)
522     {
523         ipc_client_log(client, "nv_data_backup_restore: nv_data.bin write try #%d", nv_data_write_tries + 1);
524
525         fd = open(nv_data_path(client), O_RDWR | O_CREAT | O_TRUNC, 0644);
526         if (fd < 0)
527         {
528             ipc_client_log(client, "nv_data_backup_restore: negative fd while opening /efs/nv_data.bin, error: %s", strerror(errno));
529             nv_data_write_tries++;
530             continue;
531         }
532
533         rc = write(fd, nv_data_bak_p, nv_data_size(client));
534         if (rc < nv_data_size(client))
535         {
536             ipc_client_log(client, "nv_data_backup_restore: wrote less (%d) than what we expected (%d) on /efs/nv_data.bin, error: %s", strerror(errno));
537             close(fd);
538             nv_data_write_tries++;
539             continue;
540         }
541
542         close(fd);
543         break;
544     }
545
546     if (nv_data_write_tries == 5)
547     {
548         ipc_client_log(client, "nv_data_backup_restore: writing the backup to nv_data.bin failed too many times");
549         unlink(nv_data_path(client));
550         goto exit;
551     }
552
553     /* Read the newly-written nv_data.bin. */
554     nv_data_p = file_data_read(nv_data_path(client),
555         nv_data_size(client), nv_data_chunk_size(client));
556
557     /* Compute the MD5 hash for nv_data.bin. */
558     nv_data_md5_compute(nv_data_p, nv_data_size(client), nv_data_secret(client), nv_data_md5_hash);
559     md5hash2string(nv_data_md5_hash_string, nv_data_md5_hash);
560
561     if (nv_data_p != NULL)
562     {
563         free(nv_data_p);
564         nv_data_p = NULL;
565     }
566
567     ipc_client_log(client, "nv_data_backup_restore: written file computed MD5: %s read MD5: %s",
568         nv_data_md5_hash_string, nv_data_md5_hash_read);
569
570     /* Make sure both hashes are the same. */
571     if (strcmp(nv_data_md5_hash_string, nv_data_md5_hash_read) != 0)
572     {
573         ipc_client_log(client, "nv_data_backup_restore: MD5 hash mismatch on written file");
574         ipc_client_log(client, "nv_data_backup_restore: Writing again");
575
576         goto nv_data_backup_restore_write;
577     }
578
579     /* Write the MD5 hash in nv_data.bin.md5. */
580     fd = open(nv_data_md5_path(client), O_WRONLY | O_CREAT | O_TRUNC, 0644);
581     if (fd < 0)
582     {
583         ipc_client_log(client, "nv_data_backup_restore: failed to open file with MD5 hash");
584         goto exit;
585     }
586
587     rc = write(fd, nv_data_md5_hash_read, MD5_STRING_SIZE);
588     if (rc < 0)
589     {
590         ipc_client_log(client, "nv_data_backup_restore: failed to write MD5 hash to file");
591         close(fd);
592         goto exit;
593     }
594     close(fd);
595
596     /* Write the correct .nv_state. */
597     fd = open(nv_state_path(client), O_WRONLY | O_CREAT | O_TRUNC, 0644);
598     if (fd < 0)
599     {
600         ipc_client_log(client, "nv_data_backup_restore: failed to open NV state file");
601         goto exit;
602     }
603
604     data = '1';
605     rc = write(fd, &data, sizeof(data));
606     if (rc <  0)
607     {
608         ipc_client_log(client, "nv_data_backup_restore: failed to write state to file");
609         close(fd);
610         goto exit;
611     }
612
613     close(fd);
614
615 exit:
616     if (nv_data_bak_p != NULL)
617         free(nv_data_bak_p);
618     if (nv_data_md5_hash_string != NULL)
619         free(nv_data_md5_hash_string);
620     if (nv_data_md5_hash_read != NULL)
621         free(nv_data_md5_hash_read);
622
623     ipc_client_log(client, "nv_data_backup_restore: exit");
624 }
625
626 int nv_data_check(struct ipc_client *client)
627 {
628     struct stat nv_stat;
629     int nv_state_fd = -1;
630     int nv_state = 0;
631     int rc;
632
633     ipc_client_log(client, "nv_data_check: enter");
634
635     if (stat(nv_data_path(client), &nv_stat) < 0)
636     {
637         ipc_client_log(client, "nv_data_check: nv_data.bin missing");
638         nv_data_backup_restore(client);
639         stat(nv_data_path(client), &nv_stat);
640     }
641
642     if (nv_stat.st_size != nv_data_size(client))
643     {
644         ipc_client_log(client, "nv_data_check: wrong nv_data.bin size");
645         nv_data_backup_restore(client);
646     }
647
648     if (stat(nv_data_md5_path(client), &nv_stat) < 0)
649     {
650         ipc_client_log(client, "nv_data_check: nv_data.bin.md5 missing");
651         nv_data_backup_restore(client);
652     }
653
654     if (stat(nv_data_bak_path(client), &nv_stat) < 0 || stat(nv_data_md5_bak_path(client), &nv_stat) < 0)
655     {
656         ipc_client_log(client, "nv_data_check: .nv_data.bak or .nv_data.bak.md5 missing");
657         nv_data_backup_create(client);
658     }
659
660     nv_state_fd = open(nv_state_path(client), O_RDONLY);
661
662     if (nv_state_fd < 0 || fstat(nv_state_fd, &nv_stat) < 0)
663     {
664         ipc_client_log(client, "nv_data_check: .nv_state missing");
665         nv_data_backup_restore(client);
666     }
667
668     rc = read(nv_state_fd, &nv_state, sizeof(nv_state));
669     if (rc < 0)
670     {
671         ipc_client_log(client, "nv_data_check: couldn't read state of NV item from file");
672         return -1;
673     }
674
675     close(nv_state_fd);
676
677     if (nv_state != '1')
678     {
679         ipc_client_log(client, "nv_data_check: bad nv_state");
680         nv_data_backup_restore(client);
681     }
682
683     ipc_client_log(client, "nv_data_check: everything should be alright");
684     ipc_client_log(client, "nv_data_check: exit");
685
686     return 0;
687 }
688
689 int nv_data_md5_check(struct ipc_client *client)
690 {
691     struct stat nv_stat;
692     uint8_t nv_data_md5_hash[MD5_DIGEST_LENGTH];
693     char *nv_data_md5_hash_string = NULL;
694     char *nv_data_md5_hash_read = NULL;
695     void *nv_data_p = NULL;
696     int fd;
697     int rc;
698     uint8_t *data_p;
699
700     ipc_client_log(client, "nv_data_md5_check: enter");
701
702     nv_data_md5_hash_string = malloc(MD5_STRING_SIZE);
703     nv_data_md5_hash_read = malloc(MD5_STRING_SIZE);
704
705     memset(nv_data_md5_hash_read, 0, MD5_STRING_SIZE);
706     memset(nv_data_md5_hash_string, 0, MD5_STRING_SIZE);
707
708     nv_data_p = file_data_read(nv_data_path(client),
709         nv_data_size(client), nv_data_chunk_size(client));
710     data_p = nv_data_p;
711
712     nv_data_md5_compute(data_p, nv_data_size(client), nv_data_secret(client), nv_data_md5_hash);
713
714     md5hash2string(nv_data_md5_hash_string, nv_data_md5_hash);
715
716     free(nv_data_p);
717
718     fd = open(nv_data_md5_path(client), O_RDONLY);
719
720     /* Read the md5 stored in the file. */
721     rc = read(fd, nv_data_md5_hash_read, MD5_STRING_SIZE);
722     if (rc < 0)
723     {
724         ipc_client_log(client, "nv_data_md5_check: Can't read md5 hash from file");
725         return -1;
726     }
727
728     /* Add 0x0 to end the string: not sure this is part of the file. */
729     nv_data_md5_hash_read[MD5_STRING_SIZE - 1] = '\0';
730
731     ipc_client_log(client, "nv_data_md5_check: computed MD5: %s read MD5: %s", 
732         nv_data_md5_hash_string, nv_data_md5_hash_read);
733
734     if (strcmp(nv_data_md5_hash_string, nv_data_md5_hash_read) != 0)
735     {
736         ipc_client_log(client, "nv_data_md5_check: MD5 hash mismatch");
737         nv_data_backup_restore(client);
738     }
739
740     if (nv_data_md5_hash_string != NULL)
741         free(nv_data_md5_hash_string);
742     if (nv_data_md5_hash_read != NULL)
743         free(nv_data_md5_hash_read);
744
745     ipc_client_log(client, "nv_data_md5_check: exit");
746
747     return 0;
748 }
749
750 int nv_data_read(struct ipc_client *client, int offset, int length, char *buf)
751 {
752     int fd;
753     int rc;
754
755     ipc_client_log(client, "nv_data_read: enter");
756
757     if (offset < 0 || length <= 0) {
758         ipc_client_log(client, "nv_data_read: offset < 0 or length <= 0");
759         return -1;
760     }
761
762     if (buf == NULL) {
763         ipc_client_log(client, "nv_data_read: provided output buf is NULL");
764         return -1;
765     }
766
767     if (nv_data_check(client) < 0)
768         return -1;
769
770     fd = open(nv_data_path(client), O_RDONLY);
771     if (fd < 0) {
772         ipc_client_log(client, "nv_data_read: nv_data file fd is negative");
773         return -1;
774     }
775
776     lseek(fd, offset, SEEK_SET);
777
778     rc = read(fd, buf, length);
779     if (rc < length) {
780         ipc_client_log(client, "nv_data_read: read less than what we expected");
781         return -1;
782     }
783
784     ipc_client_log(client, "nv_data_read: exit");
785
786     return 0;
787 }
788
789 int nv_data_write(struct ipc_client *client, int offset, int length, char *buf)
790 {
791     int fd;
792     int rc;
793
794     ipc_client_log(client, "nv_data_write: enter");
795
796     if (offset < 0 || length <= 0) {
797         ipc_client_log(client, "nv_data_write: offset or length <= 0");
798         return -1;
799     }
800
801     if (buf == NULL) {
802         ipc_client_log(client, "nv_data_write: provided input buf is NULL");
803         return -1;
804     }
805
806     if (nv_data_check(client) < 0)
807         return -1;
808
809     fd = open(nv_data_path(client), O_WRONLY);
810     if (fd < 0) {
811         ipc_client_log(client, "nv_data_write: nv_data file fd is negative");
812         return -1;
813     }
814
815     lseek(fd, offset, SEEK_SET);
816
817     rc = write(fd, buf, length);
818
819     close(fd);
820
821     if (rc < length) {
822         ipc_client_log(client, "nv_data_write: wrote less (%d) than what we expected (%d), error: %s, restoring backup", rc, length, strerror(errno));
823         nv_data_backup_restore(client);
824         return -1;
825     }
826
827     ipc_client_log(client, "nv_data_write: writing new md5sum");
828     nv_data_md5_generate(client);
829
830     ipc_client_log(client, "nv_data_write: exit");
831
832     return 0;
833 }
834
835 void ipc_rfs_send_io_confirm_for_nv_read_item(struct ipc_client *client,
836     struct ipc_message_info *info)
837 {
838     struct ipc_rfs_io *rfs_io = (struct ipc_rfs_io *) info->data;
839     struct ipc_rfs_io_confirm *rfs_io_conf;
840     void *rfs_data;
841     int rc;
842
843     if (rfs_io == NULL)
844     {
845         ipc_client_log(client, "ERROR: Request message is invalid: aseq = %i", info->aseq);
846         return;
847     }
848
849     rfs_io_conf = malloc(rfs_io->length + sizeof(struct ipc_rfs_io_confirm));
850     memset(rfs_io_conf, 0, rfs_io->length + sizeof(struct ipc_rfs_io_confirm));
851     rfs_data = rfs_io_conf + sizeof(struct ipc_rfs_io_confirm);
852
853     ipc_client_log(client, "Asked to read 0x%x bytes at offset 0x%x", rfs_io->length, rfs_io->offset);
854     rc = nv_data_read(client, rfs_io->offset, rfs_io->length, rfs_data);
855
856 #ifdef DEBUG
857     ipc_client_log(client, "Read rfs_data dump:");
858     ipc_client_hex_dump(client, rfs_data, rfs_io->length);
859 #endif
860
861     ipc_client_log(client, "Preparing RFS IO Confirm message (rc is %d)", rc);
862     rfs_io_conf->confirm = rc < 0 ? 0 : 1;
863     rfs_io_conf->offset = rfs_io->offset;
864     rfs_io_conf->length = rfs_io->length;
865
866     ipc_client_send(client, IPC_RFS_NV_READ_ITEM, 0, (unsigned char *) rfs_io_conf,
867                     rfs_io->length + sizeof(struct ipc_rfs_io_confirm), info->aseq);
868     free(rfs_io_conf);
869 }
870
871 void ipc_rfs_send_io_confirm_for_nv_write_item(struct ipc_client *client,
872     struct ipc_message_info *info)
873 {
874     struct ipc_rfs_io *rfs_io = (struct ipc_rfs_io *) info->data;
875     struct ipc_rfs_io_confirm *rfs_io_conf;
876     void *rfs_data;
877     int rc;
878
879     if (rfs_io == NULL)
880     {
881         ipc_client_log(client, "ERROR: Request message is invalid: aseq = %i", info->aseq);
882         return;
883     }
884
885     rfs_data = info->data + sizeof(struct ipc_rfs_io);
886
887 #ifdef DEBUG
888     ipc_client_log(client, "Write rfs_data dump:");
889     ipc_client_hex_dump(client, rfs_data, rfs_io->length);
890 #endif
891
892     ipc_client_log(client, "Asked to write 0x%x bytes at offset 0x%x", rfs_io->length, rfs_io->offset);
893     rc = nv_data_write(client, rfs_io->offset, rfs_io->length, rfs_data);
894
895     ipc_client_log(client, "Sending RFS IO Confirm message (rc is %d)", rc);
896     rfs_io_conf = (struct ipc_rfs_io_confirm*) malloc(sizeof(struct ipc_rfs_io_confirm));
897     rfs_io_conf->confirm = rc < 0 ? 0 : 1;
898     rfs_io_conf->offset = rfs_io->offset;
899     rfs_io_conf->length = rfs_io->length;
900
901     ipc_client_send(client, IPC_RFS_NV_WRITE_ITEM, 0, (unsigned char *) rfs_io_conf,
902                     sizeof(struct ipc_rfs_io_confirm), info->aseq);
903     free(rfs_io_conf);
904 }
905
906 // vim:ts=4:sw=4:expandtab