48d5d8a43401100e7871e7991de9d7b387857df5
[fw/altos] / src / test / ao_fat_test.c
1 /*
2  * Copyright © 2013 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include <stdint.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <stdarg.h>
22 #include <string.h>
23 #include <getopt.h>
24 #include <math.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <openssl/md5.h>
28
29 #define AO_FAT_TEST
30
31 void
32 ao_mutex_get(uint8_t *mutex)
33 {
34 }
35
36 void
37 ao_mutex_put(uint8_t *mutex)
38 {
39 }
40
41 void
42 ao_panic(uint8_t panic)
43 {
44         printf ("panic %d\n", panic);
45         abort();
46 }
47
48 #define AO_PANIC_BUFIO  15
49
50 #define ao_cmd_success  0
51
52 uint8_t ao_cmd_status;
53 uint32_t ao_cmd_lex_u32;
54
55 void
56 ao_cmd_decimal()
57 {
58 }
59
60 #define ao_cmd_register(x)
61
62 struct ao_cmds {
63         void            (*func)(void);
64         const char      *help;
65 };
66
67 int fs_fd;
68
69 uint64_t        total_reads, total_writes;
70
71 uint8_t
72 ao_sdcard_read_block(uint32_t block, uint8_t *data)
73 {
74         ++total_reads;
75         lseek(fs_fd, block * 512, 0);
76         return read(fs_fd, data, 512) == 512;
77 }
78
79 uint8_t
80 ao_sdcard_write_block(uint32_t block, uint8_t *data)
81 {
82         ++total_writes;
83         lseek(fs_fd, block * 512, 0);
84         return write(fs_fd, data, 512) == 512;
85 }
86
87 struct fs_param {
88         int     fat;
89         int     blocks;
90 } fs_params[] = {
91         { .fat = 16, .blocks = 16384 },
92         { .fat = 32, .blocks = 16384 },
93         { .fat = 16, .blocks = 65536 },
94         { .fat = 32, .blocks = 65536 },
95         { .fat = 16, .blocks = 1048576 },
96         { .fat = 32, .blocks = 1048576 },
97         { .fat = 0, .blocks = 0 },
98 };
99
100 char            *fs = "fs.fat";
101 struct fs_param *param;
102
103 void
104 ao_sdcard_init(void)
105 {
106         char    cmd[1024];
107
108         if (fs_fd) {
109                 close(fs_fd);
110                 fs_fd = 0;
111         }
112         snprintf(cmd, sizeof(cmd), "rm -f %s && mkfs.vfat -F %d -C %s %d",
113                  fs, param->fat, fs, param->blocks);
114         if (system (cmd) != 0) {
115                 fprintf(stderr, "'%s' failed\n", cmd);
116                 exit(1);
117         }
118         fs_fd = open(fs, 2);
119         if (fs_fd < 0) {
120                 perror (fs);
121                 exit(1);
122         }
123 }
124
125 #include "ao_bufio.c"
126 void
127 check_bufio(char *where)
128 {
129         int     b;
130
131         for (b = 0; b < AO_NUM_BUF; b++) {
132                 if (ao_bufio[b].busy) {
133                         printf ("%s: buffer %d busy. block %d seqno %u\n",
134                                 where, b, ao_bufio[b].block, ao_bufio[b].seqno & 0xffff);
135                         abort();
136                 }
137         }
138 }
139
140
141 void
142 check_fat(void);
143
144 #include "ao_fat.c"
145
146 /* Get the next cluster entry in the chain */
147 static cluster_t
148 ao_fat_entry_raw_read(cluster_t cluster, uint8_t fat)
149 {
150         sector_t                sector;
151         cluster_offset_t        offset;
152         uint8_t                 *buf;
153         cluster_t               ret;
154
155         if (fat32)
156                 cluster <<= 2;
157         else
158                 cluster <<= 1;
159         sector = cluster >> SECTOR_SHIFT;
160         offset = cluster & SECTOR_MASK;
161         buf = ao_fat_sector_get(fat_start + fat * sectors_per_fat + sector);
162         if (!buf)
163                 return 0;
164         if (fat32)
165                 ret = get_u32(buf + offset);
166         else
167                 ret = get_u16(buf + offset);
168         ao_fat_sector_put(buf, 0);
169         return ret;
170 }
171
172 void
173 dump_fat(void)
174 {
175         int     e;
176
177         printf ("\n **** FAT ****\n\n");
178         for (e = 0; e < number_cluster; e++) {
179                 if ((e & 0xf) == 0x0)
180                         printf ("%04x: ", e);
181                 if (fat32)
182                         printf (" %08x", ao_fat_entry_raw_read(e, 0));
183                 else
184                         printf (" %04x", ao_fat_entry_raw_read(e, 0));
185                 if ((e & 0xf) == 0xf)
186                         putchar ('\n');
187         }
188         if (e & 0xf)
189                 putchar('\n');
190 }
191
192 void
193 fat_list(void)
194 {
195         dirent_t                entry = 0;
196         struct ao_fat_dirent    dirent;
197
198         printf ("  **** Root directory ****\n");
199         while (ao_fat_readdir(&entry, &dirent)) {
200                 printf ("%04x: %-8.8s.%-3.3s %02x %04x %d\n",
201                         entry,
202                         dirent.name,
203                         dirent.name + 8,
204                         dirent.attr,
205                         dirent.cluster,
206                         dirent.size);
207         }
208
209         printf ("  **** End of root directory ****\n");
210 }
211
212 void
213 fatal(char *msg, ...)
214 {
215 //      dump_fat();
216 //      fat_list();
217
218         va_list l;
219         va_start(l, msg);
220         vfprintf(stderr, msg, l);
221         va_end(l);
222
223         abort();
224 }
225
226 void
227 check_fat(void)
228 {
229         int     e;
230         int     f;
231
232         for (e = 0; e < number_cluster; e++) {
233                 cluster_t       v = ao_fat_entry_raw_read(e, 0);
234                 for (f = 1; f < number_fat; f++) {
235                         if (ao_fat_entry_raw_read(e, f) != v)
236                                 fatal ("fats differ at %d\n", e);
237                 }
238         }
239 }
240
241 cluster_t
242 check_file(dirent_t dent, cluster_t first_cluster, dirent_t *used)
243 {
244         cluster_t       clusters = 0;
245         cluster_t       cluster;
246
247         if (!first_cluster)
248                 return 0;
249         
250         for (cluster = first_cluster;
251              fat32 ? !AO_FAT_IS_LAST_CLUSTER(cluster) : !AO_FAT_IS_LAST_CLUSTER16(cluster);
252              cluster = ao_fat_entry_raw_read(cluster, 0))
253         {
254                 if (!ao_fat_cluster_valid(cluster))
255                         fatal("file %d: invalid cluster %08x\n", dent, cluster);
256                 if (used[cluster])
257                         fatal("file %d: duplicate cluster %08x also in file %d\n", dent, cluster, used[cluster]-1);
258                 used[cluster] = dent;
259                 clusters++;
260         }
261         return clusters;
262 }
263
264 void
265 check_fs(void)
266 {
267         dirent_t        r;
268         cluster_t       cluster, chain;
269         dirent_t        *used;
270         uint8_t         *dent;
271
272         check_fat();
273
274         used = calloc(sizeof (dirent_t), number_cluster);
275
276         for (r = 0; (dent = ao_fat_root_get(r)); r++) {
277                 cluster_t       clusters;
278                 offset_t        size;
279                 cluster_t       first_cluster;
280                 char            name[11];
281
282                 if (!dent)
283                         fatal("cannot map dent %d\n", r);
284                 memcpy(name, dent+0, 11);
285                 first_cluster = get_u16(dent + 0x1a);
286                 if (fat32)
287                         first_cluster |= (cluster_t) get_u16(dent + 0x14) << 16;
288                 size = get_u32(dent + 0x1c);
289                 ao_fat_root_put(dent, r, 0);
290
291                 if (name[0] == AO_FAT_DENT_END) {
292                         break;
293                 }
294
295                 clusters = check_file(r + 1, first_cluster, used);
296                 if (size == 0) {
297                         if (clusters != 0)
298                                 fatal("file %d: zero sized, but %d clusters\n", clusters);
299                 } else {
300                         if (size > clusters * bytes_per_cluster)
301                                 fatal("file %d: size %u beyond clusters %d (%u)\n",
302                                       r, size, clusters, clusters * bytes_per_cluster);
303                         if (size <= (clusters - 1) * bytes_per_cluster)
304                                 fatal("file %d: size %u too small clusters %d (%u)\n",
305                                       r, size, clusters, clusters * bytes_per_cluster);
306                 }
307         }
308         if (!fat32) {
309                 for (; r < root_entries; r++) {
310                         uint8_t *dent = ao_fat_root_get(r);
311                         if (!dent)
312                                 fatal("cannot map dent %d\n", r);
313                         if (dent[0] != AO_FAT_DENT_END)
314                                 fatal("found non-zero dent past end %d\n", r);
315                         ao_fat_root_put(dent, r, 0);
316                 }
317         } else {
318                 check_file((dirent_t) -1, root_cluster, used);
319         }
320
321         for (cluster = 0; cluster < 2; cluster++) {
322                 chain = ao_fat_entry_raw_read(cluster, 0);
323
324                 if (fat32) {
325                         if ((chain & 0xffffff8) != 0xffffff8)
326                                 fatal("cluster %d: not marked busy\n", cluster);
327                 } else {
328                         if ((chain & 0xfff8) != 0xfff8)
329                                 fatal("cluster %d: not marked busy\n", cluster);
330                 }
331         }
332         for (; cluster < number_cluster; cluster++) {
333                 chain = ao_fat_entry_raw_read(cluster, 0);
334
335                 if (chain != 0) {
336                         if (used[cluster] == 0)
337                                 fatal("cluster %d: marked busy, but not in any file\n", cluster);
338                 } else {
339                         if (used[cluster] != 0)
340                                 fatal("cluster %d: marked free, but found in file %d\n", cluster, used[cluster]-1);
341                 }
342         }
343 }
344
345 #define NUM_FILES       100
346 #define LINES_FILE      500000
347
348 uint32_t                sizes[NUM_FILES];
349
350 unsigned char           md5[NUM_FILES][MD5_DIGEST_LENGTH];
351
352 void
353 short_test_fs(void)
354 {
355         int     len;
356         char    buf[345];
357
358         if (ao_fat_open("HELLO   TXT",AO_FAT_OPEN_READ) == AO_FAT_SUCCESS) {
359                 printf ("File contents for HELLO.TXT\n");
360                 while ((len = ao_fat_read(buf, sizeof(buf))))
361                         write(1, buf, len);
362                 ao_fat_close();
363         }
364         
365         if (ao_fat_creat("NEWFILE TXT") == AO_FAT_SUCCESS) {
366                 printf ("Create new file\n");
367                 for (len = 0; len < 2; len++)
368                         ao_fat_write("hello, world!\n", 14);
369                 ao_fat_seek(0, AO_FAT_SEEK_SET);
370                 printf ("read new file\n");
371                 while ((len = ao_fat_read(buf, sizeof (buf))))
372                         write (1, buf, len);
373                 ao_fat_close();
374         }
375
376         check_fs();
377 }
378
379 void
380 long_test_fs(void)
381 {
382         char    name[12];
383         int     id;
384         MD5_CTX ctx;
385         unsigned char   md5_check[MD5_DIGEST_LENGTH];
386         char buf[337];
387         int     len;
388         uint64_t        total_file_size = 0;
389
390         total_reads = total_writes = 0;
391
392         printf ("   **** Creating %d files\n", NUM_FILES);
393
394         memset(sizes, '\0', sizeof (sizes));
395         for (id = 0; id < NUM_FILES; id++) {
396                 sprintf(name, "D%07dTXT", id);
397                 if ((id % (NUM_FILES/50)) == 0) {
398                         printf ("."); fflush(stdout);
399                 }
400                 if (ao_fat_creat(name) == AO_FAT_SUCCESS) {
401                         int j;
402                         char    line[64];
403                         check_bufio("file created");
404                         MD5_Init(&ctx);
405                         for (j = 0; j < LINES_FILE; j++) {
406                                 int len, ret;
407                                 sprintf (line, "Hello, world %d %d\r\n", id, j);
408                                 len = strlen(line);
409                                 ret = ao_fat_write((uint8_t *) line, len);
410                                 if (ret <= 0)
411                                         break;
412                                 total_file_size += ret;
413                                 MD5_Update(&ctx, line, ret);
414                                 sizes[id] += ret;
415                                 if (ret != len)
416                                         printf ("write failed %d\n", ret);
417                         }
418                         ao_fat_close();
419                         MD5_Final(&md5[id][0], &ctx);
420                         check_bufio("file written");
421                 }
422         }
423
424         printf ("\n   **** Write IO: read %llu write %llu data sectors %llu\n", total_reads, total_writes, (total_file_size + 511) / 512);
425
426         check_bufio("all files created");
427         printf ("   **** All done creating files\n");
428         check_fs();
429
430         total_reads = total_writes = 0;
431
432         printf ("   **** Comparing %d files\n", NUM_FILES);
433
434         for (id = 0; id < NUM_FILES; id++) {
435                 uint32_t size;
436                 sprintf(name, "D%07dTXT", id);
437                 size = 0;
438                 if ((id % (NUM_FILES/50)) == 0) {
439                         printf ("."); fflush(stdout);
440                 }
441                 if (ao_fat_open(name, AO_FAT_OPEN_READ) == AO_FAT_SUCCESS) {
442                         MD5_Init(&ctx);
443                         while ((len = ao_fat_read((uint8_t *) buf, sizeof(buf))) > 0) {
444                                 MD5_Update(&ctx, buf, len);
445                                 size += len;
446                         }
447                         ao_fat_close();
448                         MD5_Final(md5_check, &ctx);
449                         if (size != sizes[id])
450                                 fatal("file %d: size differs %d written %d read\n",
451                                       id, sizes[id], size);
452                         if (memcmp(md5_check, &md5[id][0], sizeof (md5_check)) != 0)
453                                 fatal ("file %d: checksum failed\n", id);
454                         check_bufio("file shown");
455                 }
456         }
457         printf ("\n  **** Read IO: read %llu write %llu\n", total_reads, total_writes);
458 }
459
460 char *params[] = {
461         "-F 16 -C %s 16384",
462         "-F 32 -C %s 16384",
463         "-F 16 -C %s 65536",
464         "-F 32 -C %s 65536",
465         "-F 16 -C %s 1048576",
466         "-F 32 -C %s 1048576",
467         NULL
468 };
469
470 int
471 main(int argc, char **argv)
472 {
473         int     p;
474
475         if (argv[1])
476                 fs = argv[1];
477
478         for (p = 0; fs_params[p].fat; p++) {
479                 param = &fs_params[p];
480                 ao_fat_init();
481
482                 check_bufio("top");
483                 ao_fat_setup();
484
485                 check_fs();
486                 check_bufio("after setup");
487
488 #ifdef SIMPLE_TEST
489                 short_test_fs();
490 #else
491                 long_test_fs();
492 #endif
493         }
494
495         return 0;
496 }