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