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