altos: Add a simple cache for the FAT position->cluster computation
[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 char    *fs = "fs.fat";
88
89 void
90 ao_sdcard_init(void)
91 {
92         char    cmd[1024];
93
94         snprintf(cmd, sizeof(cmd), "rm -f %s && mkfs.vfat -C %s 16384", fs, fs);
95         if (system (cmd) != 0) {
96                 fprintf(stderr, "'%s' failed\n", cmd);
97                 exit(1);
98         }
99         fs_fd = open(fs, 2);
100         if (fs_fd < 0) {
101                 perror (fs);
102                 exit(1);
103         }
104 }
105
106 #include "ao_bufio.c"
107 void
108 check_bufio(char *where)
109 {
110         int     b;
111
112         for (b = 0; b < AO_NUM_BUF; b++) {
113                 if (ao_bufio[b].busy) {
114                         printf ("%s: buffer %d busy. block %d seqno %u\n",
115                                 where, b, ao_bufio[b].block, ao_bufio[b].seqno & 0xffff);
116                         abort();
117                 }
118         }
119 }
120
121
122 void
123 check_fat(void);
124
125 #include "ao_fat.c"
126
127 /* Get the next cluster entry in the chain */
128 static uint16_t
129 ao_fat_entry_raw_read(uint16_t cluster, uint8_t fat)
130 {
131         uint32_t        sector;
132         uint16_t        offset;
133         uint8_t         *buf;
134         uint16_t        ret;
135
136 //      cluster -= 2;
137         sector = cluster >> (SECTOR_SHIFT - 1);
138         offset = (cluster << 1) & SECTOR_MASK;
139         buf = ao_fat_sector_get(fat_start + fat * sectors_per_fat + sector);
140         if (!buf)
141                 return 0;
142         ret = get_u16(buf + offset);
143         ao_fat_sector_put(buf, 0);
144         return ret;
145 }
146
147 void
148 dump_fat(void)
149 {
150         int     e;
151
152         printf ("\n **** FAT ****\n\n");
153         for (e = 0; e < number_cluster; e++) {
154                 if ((e & 0xf) == 0x0)
155                         printf ("%04x: ", e);
156                 printf (" %04x", ao_fat_entry_raw_read(e, 0));
157                 if ((e & 0xf) == 0xf)
158                         putchar ('\n');
159         }
160 }
161
162 void
163 fat_list(void)
164 {
165         uint16_t                entry = 0;
166         struct ao_fat_dirent    dirent;
167
168         printf ("  **** Root directory ****\n");
169         while (ao_fat_readdir(&entry, &dirent)) {
170                 printf ("%04x: %-8.8s.%-3.3s %02x %04x %d\n",
171                         entry,
172                         dirent.name,
173                         dirent.name + 8,
174                         dirent.attr,
175                         dirent.cluster,
176                         dirent.size);
177         }
178
179         printf ("  **** End of root directory ****\n");
180 }
181
182 void
183 fatal(char *msg, ...)
184 {
185         dump_fat();
186         fat_list();
187
188         va_list l;
189         va_start(l, msg);
190         vfprintf(stderr, msg, l);
191         va_end(l);
192
193         abort();
194 }
195
196 void
197 check_fat(void)
198 {
199         int     e;
200         int     f;
201
202         for (e = 0; e < number_cluster; e++) {
203                 uint16_t        v = ao_fat_entry_raw_read(e, 0);
204                 for (f = 1; f < number_fat; f++) {
205                         if (ao_fat_entry_raw_read(e, f) != v)
206                                 fatal ("fats differ at %d\n", e);
207                 }
208         }
209 }
210
211 uint16_t
212 check_file(uint16_t dent, uint16_t first_cluster, uint8_t *used)
213 {
214         uint16_t        clusters = 0;
215         uint16_t        cluster;
216
217         if (!first_cluster)
218                 return 0;
219         
220         for (cluster = first_cluster;
221              (cluster & 0xfff8) != 0xfff8;
222              cluster = ao_fat_entry_raw_read(cluster, 0))
223         {
224                 if (!ao_fat_cluster_valid(cluster))
225                         fatal("file %d: invalid cluster %04x\n", dent, cluster);
226                 if (used[cluster])
227                         fatal("file %d: duplicate cluster %04x\n", dent, cluster);
228                 used[cluster] = 1;
229                 clusters++;
230         }
231         return clusters;
232 }
233
234 void
235 check_fs(void)
236 {
237         uint16_t        r;
238         uint16_t        cluster, chain;
239         uint8_t         *used;
240
241         check_fat();
242
243         used = calloc(1, number_cluster);
244
245         for (r = 0; r < root_entries; r++) {
246                 uint8_t         *dent = ao_fat_root_get(r);
247                 uint16_t        clusters;
248                 uint32_t        size;
249                 uint16_t        first_cluster;
250                 uint8_t         name[11];
251
252                 if (!dent)
253                         fatal("cannot map dent %d\n", r);
254                 memcpy(name, dent+0, 11);
255                 first_cluster = get_u16(dent + 0x1a);
256                 size = get_u32(dent + 0x1c);
257                 ao_fat_root_put(dent, r, 0);
258
259                 if (name[0] == AO_FAT_DENT_END) {
260                         break;
261                 }
262
263                 clusters = check_file(r, first_cluster, used);
264                 if (size == 0) {
265                         if (clusters != 0)
266                                 fatal("file %d: zero sized, but %d clusters\n", clusters);
267                 } else {
268                         if (size > clusters * bytes_per_cluster)
269                                 fatal("file %d: size %u beyond clusters %d (%u)\n",
270                                       r, size, clusters, clusters * bytes_per_cluster);
271                         if (size <= (clusters - 1) * bytes_per_cluster)
272                                 fatal("file %d: size %u too small clusters %d (%u)\n",
273                                       r, size, clusters, clusters * bytes_per_cluster);
274                 }
275         }
276         for (; r < root_entries; r++) {
277                 uint8_t *dent = ao_fat_root_get(r);
278                 if (!dent)
279                         fatal("cannot map dent %d\n", r);
280                 if (dent[0] != AO_FAT_DENT_END)
281                         fatal("found non-zero dent past end %d\n", r);
282                 ao_fat_root_put(dent, r, 0);
283         }
284
285         for (cluster = 0; cluster < 2; cluster++) {
286                 chain = ao_fat_entry_raw_read(cluster, 0);
287
288                 if ((chain & 0xfff8) != 0xfff8)
289                         fatal("cluster %d: not marked busy\n", cluster);
290         }
291         for (; cluster < number_cluster; cluster++) {
292                 chain = ao_fat_entry_raw_read(cluster, 0);
293
294                 if (chain != 0) {
295                         if (used[cluster] == 0)
296                                 fatal("cluster %d: marked busy, but not in any file\n", cluster);
297                 } else {
298                         if (used[cluster] != 0)
299                                 fatal("cluster %d: marked free, but foudn in file\n", cluster);
300                 }
301         }
302 }
303
304 #define NUM_FILES       10
305 #define LINES_FILE      80000
306
307 uint32_t                sizes[NUM_FILES];
308
309 unsigned char           md5[NUM_FILES][MD5_DIGEST_LENGTH];
310
311 int
312 main(int argc, char **argv)
313 {
314         char    name[12];
315         int     id;
316         MD5_CTX ctx;
317         unsigned char   md5_check[MD5_DIGEST_LENGTH];
318
319         if (argv[1])
320                 fs = argv[1];
321
322         ao_fat_init();
323
324         check_bufio("top");
325         ao_fat_setup();
326
327         check_fs();
328         check_bufio("after setup");
329         printf ("   **** Creating %d files\n", NUM_FILES);
330
331         for (id = 0; id < NUM_FILES; id++) {
332                 sprintf(name, "D%07dTXT", id);
333                 if (ao_fat_creat(name) == AO_FAT_SUCCESS) {
334                         int j;
335                         char    line[64];
336                         check_bufio("file created");
337                         MD5_Init(&ctx);
338                         for (j = 0; j < LINES_FILE; j++) {
339                                 int len, ret;
340                                 sprintf (line, "Hello, world %d %d\r\n", id, j);
341                                 len = strlen(line);
342                                 ret = ao_fat_write((uint8_t *) line, len);
343                                 if (ret <= 0)
344                                         break;
345                                 MD5_Update(&ctx, line, ret);
346                                 sizes[id] += ret;
347                                 if (ret != len)
348                                         printf ("write failed %d\n", ret);
349                         }
350                         ao_fat_close();
351                         MD5_Final(&md5[id][0], &ctx);
352                         check_bufio("file written");
353                 }
354         }
355
356         check_bufio("all files created");
357         printf ("   **** All done creating files\n");
358         check_fs();
359
360         printf ("   **** Comparing %d files\n", NUM_FILES);
361
362         for (id = 0; id < NUM_FILES; id++) {
363                 char buf[337];
364                 uint32_t size;
365                 sprintf(name, "D%07dTXT", id);
366                 size = 0;
367                 if (ao_fat_open(name, AO_FAT_OPEN_READ) == AO_FAT_SUCCESS) {
368                         int     len;
369
370                         MD5_Init(&ctx);
371                         while ((len = ao_fat_read((uint8_t *) buf, sizeof(buf))) > 0) {
372                                 MD5_Update(&ctx, buf, len);
373                                 size += len;
374                         }
375                         ao_fat_close();
376                         MD5_Final(md5_check, &ctx);
377                         if (size != sizes[id])
378                                 fatal("file %d: size differs %d written %d read\n",
379                                       id, sizes[id], size);
380                         if (memcmp(md5_check, &md5[id][0], sizeof (md5_check)) != 0)
381                                 fatal ("file %d: checksum failed\n", id);
382                         check_bufio("file shown");
383                 }
384         }
385
386         printf ("\n    **** Total IO: read %llu write %llu\n", total_reads, total_writes);
387         return 0;
388 }