1[cases.test_dirs_root]
2code = '''
3    lfs_t lfs;
4    lfs_format(&lfs, cfg) => 0;
5    lfs_mount(&lfs, cfg) => 0;
6    lfs_dir_t dir;
7    lfs_dir_open(&lfs, &dir, "/") => 0;
8    struct lfs_info info;
9    lfs_dir_read(&lfs, &dir, &info) => 1;
10    assert(info.type == LFS_TYPE_DIR);
11    assert(strcmp(info.name, ".") == 0);
12    lfs_dir_read(&lfs, &dir, &info) => 1;
13    assert(info.type == LFS_TYPE_DIR);
14    assert(strcmp(info.name, "..") == 0);
15    lfs_dir_read(&lfs, &dir, &info) => 0;
16    lfs_dir_close(&lfs, &dir) => 0;
17    lfs_unmount(&lfs) => 0;
18'''
19
20[cases.test_dirs_many_creation]
21defines.N = 'range(3, 100, 3)'
22if = 'N < BLOCK_COUNT/2'
23code = '''
24    lfs_t lfs;
25    lfs_format(&lfs, cfg) => 0;
26
27    lfs_mount(&lfs, cfg) => 0;
28    for (int i = 0; i < N; i++) {
29        char path[1024];
30        sprintf(path, "dir%03d", i);
31        lfs_mkdir(&lfs, path) => 0;
32    }
33    lfs_unmount(&lfs) => 0;
34
35    lfs_mount(&lfs, cfg) => 0;
36    lfs_dir_t dir;
37    lfs_dir_open(&lfs, &dir, "/") => 0;
38    struct lfs_info info;
39    lfs_dir_read(&lfs, &dir, &info) => 1;
40    assert(info.type == LFS_TYPE_DIR);
41    assert(strcmp(info.name, ".") == 0);
42    lfs_dir_read(&lfs, &dir, &info) => 1;
43    assert(info.type == LFS_TYPE_DIR);
44    assert(strcmp(info.name, "..") == 0);
45    for (int i = 0; i < N; i++) {
46        char path[1024];
47        sprintf(path, "dir%03d", i);
48        lfs_dir_read(&lfs, &dir, &info) => 1;
49        assert(info.type == LFS_TYPE_DIR);
50        assert(strcmp(info.name, path) == 0);
51    }
52    lfs_dir_read(&lfs, &dir, &info) => 0;
53    lfs_dir_close(&lfs, &dir) => 0;
54    lfs_unmount(&lfs) => 0;
55'''
56
57[cases.test_dirs_many_removal]
58defines.N = 'range(3, 100, 11)'
59if = 'N < BLOCK_COUNT/2'
60code = '''
61    lfs_t lfs;
62    lfs_format(&lfs, cfg) => 0;
63
64    lfs_mount(&lfs, cfg) => 0;
65    for (int i = 0; i < N; i++) {
66        char path[1024];
67        sprintf(path, "removeme%03d", i);
68        lfs_mkdir(&lfs, path) => 0;
69    }
70    lfs_unmount(&lfs) => 0;
71
72    lfs_mount(&lfs, cfg) => 0;
73    lfs_dir_t dir;
74    lfs_dir_open(&lfs, &dir, "/") => 0;
75    struct lfs_info info;
76    lfs_dir_read(&lfs, &dir, &info) => 1;
77    assert(info.type == LFS_TYPE_DIR);
78    assert(strcmp(info.name, ".") == 0);
79    lfs_dir_read(&lfs, &dir, &info) => 1;
80    assert(info.type == LFS_TYPE_DIR);
81    assert(strcmp(info.name, "..") == 0);
82    for (int i = 0; i < N; i++) {
83        char path[1024];
84        sprintf(path, "removeme%03d", i);
85        lfs_dir_read(&lfs, &dir, &info) => 1;
86        assert(info.type == LFS_TYPE_DIR);
87        assert(strcmp(info.name, path) == 0);
88    }
89    lfs_dir_read(&lfs, &dir, &info) => 0;
90    lfs_dir_close(&lfs, &dir) => 0;
91    lfs_unmount(&lfs);
92
93    lfs_mount(&lfs, cfg) => 0;
94    for (int i = 0; i < N; i++) {
95        char path[1024];
96        sprintf(path, "removeme%03d", i);
97        lfs_remove(&lfs, path) => 0;
98    }
99    lfs_unmount(&lfs);
100
101    lfs_mount(&lfs, cfg) => 0;
102    lfs_dir_open(&lfs, &dir, "/") => 0;
103    lfs_dir_read(&lfs, &dir, &info) => 1;
104    assert(info.type == LFS_TYPE_DIR);
105    assert(strcmp(info.name, ".") == 0);
106    lfs_dir_read(&lfs, &dir, &info) => 1;
107    assert(info.type == LFS_TYPE_DIR);
108    assert(strcmp(info.name, "..") == 0);
109    lfs_dir_read(&lfs, &dir, &info) => 0;
110    lfs_dir_close(&lfs, &dir) => 0;
111    lfs_unmount(&lfs) => 0;
112'''
113
114[cases.test_dirs_many_rename]
115defines.N = 'range(3, 100, 11)'
116if = 'N < BLOCK_COUNT/2'
117code = '''
118    lfs_t lfs;
119    lfs_format(&lfs, cfg) => 0;
120
121    lfs_mount(&lfs, cfg) => 0;
122    for (int i = 0; i < N; i++) {
123        char path[1024];
124        sprintf(path, "test%03d", i);
125        lfs_mkdir(&lfs, path) => 0;
126    }
127    lfs_unmount(&lfs) => 0;
128
129    lfs_mount(&lfs, cfg) => 0;
130    lfs_dir_t dir;
131    lfs_dir_open(&lfs, &dir, "/") => 0;
132    struct lfs_info info;
133    lfs_dir_read(&lfs, &dir, &info) => 1;
134    assert(info.type == LFS_TYPE_DIR);
135    assert(strcmp(info.name, ".") == 0);
136    lfs_dir_read(&lfs, &dir, &info) => 1;
137    assert(info.type == LFS_TYPE_DIR);
138    assert(strcmp(info.name, "..") == 0);
139    for (int i = 0; i < N; i++) {
140        char path[1024];
141        sprintf(path, "test%03d", i);
142        lfs_dir_read(&lfs, &dir, &info) => 1;
143        assert(info.type == LFS_TYPE_DIR);
144        assert(strcmp(info.name, path) == 0);
145    }
146    lfs_dir_read(&lfs, &dir, &info) => 0;
147    lfs_dir_close(&lfs, &dir) => 0;
148    lfs_unmount(&lfs);
149
150    lfs_mount(&lfs, cfg) => 0;
151    for (int i = 0; i < N; i++) {
152        char oldpath[128];
153        char newpath[128];
154        sprintf(oldpath, "test%03d", i);
155        sprintf(newpath, "tedd%03d", i);
156        lfs_rename(&lfs, oldpath, newpath) => 0;
157    }
158    lfs_unmount(&lfs);
159
160    lfs_mount(&lfs, cfg) => 0;
161    lfs_dir_open(&lfs, &dir, "/") => 0;
162    lfs_dir_read(&lfs, &dir, &info) => 1;
163    assert(info.type == LFS_TYPE_DIR);
164    assert(strcmp(info.name, ".") == 0);
165    lfs_dir_read(&lfs, &dir, &info) => 1;
166    assert(info.type == LFS_TYPE_DIR);
167    assert(strcmp(info.name, "..") == 0);
168    for (int i = 0; i < N; i++) {
169        char path[1024];
170        sprintf(path, "tedd%03d", i);
171        lfs_dir_read(&lfs, &dir, &info) => 1;
172        assert(info.type == LFS_TYPE_DIR);
173        assert(strcmp(info.name, path) == 0);
174    }
175    lfs_dir_read(&lfs, &dir, &info) => 0;
176    lfs_dir_close(&lfs, &dir) => 0;
177    lfs_unmount(&lfs);
178'''
179
180[cases.test_dirs_many_reentrant]
181defines.N = [5, 11]
182if = 'BLOCK_COUNT >= 4*N'
183reentrant = true
184code = '''
185    lfs_t lfs;
186    int err = lfs_mount(&lfs, cfg);
187    if (err) {
188        lfs_format(&lfs, cfg) => 0;
189        lfs_mount(&lfs, cfg) => 0;
190    }
191
192    for (int i = 0; i < N; i++) {
193        char path[1024];
194        sprintf(path, "hi%03d", i);
195        err = lfs_mkdir(&lfs, path);
196        assert(err == 0 || err == LFS_ERR_EXIST);
197    }
198
199    for (int i = 0; i < N; i++) {
200        char path[1024];
201        sprintf(path, "hello%03d", i);
202        err = lfs_remove(&lfs, path);
203        assert(err == 0 || err == LFS_ERR_NOENT);
204    }
205
206    lfs_dir_t dir;
207    lfs_dir_open(&lfs, &dir, "/") => 0;
208    struct lfs_info info;
209    lfs_dir_read(&lfs, &dir, &info) => 1;
210    assert(info.type == LFS_TYPE_DIR);
211    assert(strcmp(info.name, ".") == 0);
212    lfs_dir_read(&lfs, &dir, &info) => 1;
213    assert(info.type == LFS_TYPE_DIR);
214    assert(strcmp(info.name, "..") == 0);
215    for (int i = 0; i < N; i++) {
216        char path[1024];
217        sprintf(path, "hi%03d", i);
218        lfs_dir_read(&lfs, &dir, &info) => 1;
219        assert(info.type == LFS_TYPE_DIR);
220        assert(strcmp(info.name, path) == 0);
221    }
222    lfs_dir_read(&lfs, &dir, &info) => 0;
223    lfs_dir_close(&lfs, &dir) => 0;
224
225    for (int i = 0; i < N; i++) {
226        char oldpath[128];
227        char newpath[128];
228        sprintf(oldpath, "hi%03d", i);
229        sprintf(newpath, "hello%03d", i);
230        // YES this can overwrite an existing newpath
231        lfs_rename(&lfs, oldpath, newpath) => 0;
232    }
233
234    lfs_dir_open(&lfs, &dir, "/") => 0;
235    lfs_dir_read(&lfs, &dir, &info) => 1;
236    assert(info.type == LFS_TYPE_DIR);
237    assert(strcmp(info.name, ".") == 0);
238    lfs_dir_read(&lfs, &dir, &info) => 1;
239    assert(info.type == LFS_TYPE_DIR);
240    assert(strcmp(info.name, "..") == 0);
241    for (int i = 0; i < N; i++) {
242        char path[1024];
243        sprintf(path, "hello%03d", i);
244        lfs_dir_read(&lfs, &dir, &info) => 1;
245        assert(info.type == LFS_TYPE_DIR);
246        assert(strcmp(info.name, path) == 0);
247    }
248    lfs_dir_read(&lfs, &dir, &info) => 0;
249    lfs_dir_close(&lfs, &dir) => 0;
250
251    for (int i = 0; i < N; i++) {
252        char path[1024];
253        sprintf(path, "hello%03d", i);
254        lfs_remove(&lfs, path) => 0;
255    }
256
257    lfs_dir_open(&lfs, &dir, "/") => 0;
258    lfs_dir_read(&lfs, &dir, &info) => 1;
259    assert(info.type == LFS_TYPE_DIR);
260    assert(strcmp(info.name, ".") == 0);
261    lfs_dir_read(&lfs, &dir, &info) => 1;
262    assert(info.type == LFS_TYPE_DIR);
263    assert(strcmp(info.name, "..") == 0);
264    lfs_dir_read(&lfs, &dir, &info) => 0;
265    lfs_dir_close(&lfs, &dir) => 0;
266    lfs_unmount(&lfs) => 0;
267'''
268
269[cases.test_dirs_file_creation]
270defines.N = 'range(3, 100, 11)'
271if = 'N < BLOCK_COUNT/2'
272code = '''
273    lfs_t lfs;
274    lfs_format(&lfs, cfg) => 0;
275
276    lfs_mount(&lfs, cfg) => 0;
277    for (int i = 0; i < N; i++) {
278        char path[1024];
279        sprintf(path, "file%03d", i);
280        lfs_file_t file;
281        lfs_file_open(&lfs, &file, path,
282                LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
283        lfs_file_close(&lfs, &file) => 0;
284    }
285    lfs_unmount(&lfs) => 0;
286
287    lfs_mount(&lfs, cfg) => 0;
288    lfs_dir_t dir;
289    lfs_dir_open(&lfs, &dir, "/") => 0;
290    struct lfs_info info;
291    lfs_dir_read(&lfs, &dir, &info) => 1;
292    assert(info.type == LFS_TYPE_DIR);
293    assert(strcmp(info.name, ".") == 0);
294    lfs_dir_read(&lfs, &dir, &info) => 1;
295    assert(info.type == LFS_TYPE_DIR);
296    assert(strcmp(info.name, "..") == 0);
297    for (int i = 0; i < N; i++) {
298        char path[1024];
299        sprintf(path, "file%03d", i);
300        lfs_dir_read(&lfs, &dir, &info) => 1;
301        assert(info.type == LFS_TYPE_REG);
302        assert(strcmp(info.name, path) == 0);
303    }
304    lfs_dir_read(&lfs, &dir, &info) => 0;
305    lfs_dir_close(&lfs, &dir) => 0;
306    lfs_unmount(&lfs);
307'''
308
309[cases.test_dirs_file_removal]
310defines.N = 'range(3, 100, 11)'
311if = 'N < BLOCK_COUNT/2'
312code = '''
313    lfs_t lfs;
314    lfs_format(&lfs, cfg) => 0;
315
316    lfs_mount(&lfs, cfg) => 0;
317    for (int i = 0; i < N; i++) {
318        char path[1024];
319        sprintf(path, "removeme%03d", i);
320        lfs_file_t file;
321        lfs_file_open(&lfs, &file, path,
322                LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
323        lfs_file_close(&lfs, &file) => 0;
324    }
325    lfs_unmount(&lfs) => 0;
326
327    lfs_mount(&lfs, cfg) => 0;
328    lfs_dir_t dir;
329    lfs_dir_open(&lfs, &dir, "/") => 0;
330    struct lfs_info info;
331    lfs_dir_read(&lfs, &dir, &info) => 1;
332    assert(info.type == LFS_TYPE_DIR);
333    assert(strcmp(info.name, ".") == 0);
334    lfs_dir_read(&lfs, &dir, &info) => 1;
335    assert(info.type == LFS_TYPE_DIR);
336    assert(strcmp(info.name, "..") == 0);
337    for (int i = 0; i < N; i++) {
338        char path[1024];
339        sprintf(path, "removeme%03d", i);
340        lfs_dir_read(&lfs, &dir, &info) => 1;
341        assert(info.type == LFS_TYPE_REG);
342        assert(strcmp(info.name, path) == 0);
343    }
344    lfs_dir_read(&lfs, &dir, &info) => 0;
345    lfs_dir_close(&lfs, &dir) => 0;
346    lfs_unmount(&lfs);
347
348    lfs_mount(&lfs, cfg) => 0;
349    for (int i = 0; i < N; i++) {
350        char path[1024];
351        sprintf(path, "removeme%03d", i);
352        lfs_remove(&lfs, path) => 0;
353    }
354    lfs_unmount(&lfs);
355
356    lfs_mount(&lfs, cfg) => 0;
357    lfs_dir_open(&lfs, &dir, "/") => 0;
358    lfs_dir_read(&lfs, &dir, &info) => 1;
359    assert(info.type == LFS_TYPE_DIR);
360    assert(strcmp(info.name, ".") == 0);
361    lfs_dir_read(&lfs, &dir, &info) => 1;
362    assert(info.type == LFS_TYPE_DIR);
363    assert(strcmp(info.name, "..") == 0);
364    lfs_dir_read(&lfs, &dir, &info) => 0;
365    lfs_dir_close(&lfs, &dir) => 0;
366    lfs_unmount(&lfs) => 0;
367'''
368
369[cases.test_dirs_file_rename]
370defines.N = 'range(3, 100, 11)'
371if = 'N < BLOCK_COUNT/2'
372code = '''
373    lfs_t lfs;
374    lfs_format(&lfs, cfg) => 0;
375
376    lfs_mount(&lfs, cfg) => 0;
377    for (int i = 0; i < N; i++) {
378        char path[1024];
379        sprintf(path, "test%03d", i);
380        lfs_file_t file;
381        lfs_file_open(&lfs, &file, path,
382                LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
383        lfs_file_close(&lfs, &file) => 0;
384    }
385    lfs_unmount(&lfs) => 0;
386
387    lfs_mount(&lfs, cfg) => 0;
388    lfs_dir_t dir;
389    lfs_dir_open(&lfs, &dir, "/") => 0;
390    struct lfs_info info;
391    lfs_dir_read(&lfs, &dir, &info) => 1;
392    assert(info.type == LFS_TYPE_DIR);
393    assert(strcmp(info.name, ".") == 0);
394    lfs_dir_read(&lfs, &dir, &info) => 1;
395    assert(info.type == LFS_TYPE_DIR);
396    assert(strcmp(info.name, "..") == 0);
397    for (int i = 0; i < N; i++) {
398        char path[1024];
399        sprintf(path, "test%03d", i);
400        lfs_dir_read(&lfs, &dir, &info) => 1;
401        assert(info.type == LFS_TYPE_REG);
402        assert(strcmp(info.name, path) == 0);
403    }
404    lfs_dir_read(&lfs, &dir, &info) => 0;
405    lfs_dir_close(&lfs, &dir) => 0;
406    lfs_unmount(&lfs);
407
408    lfs_mount(&lfs, cfg) => 0;
409    for (int i = 0; i < N; i++) {
410        char oldpath[128];
411        char newpath[128];
412        sprintf(oldpath, "test%03d", i);
413        sprintf(newpath, "tedd%03d", i);
414        lfs_rename(&lfs, oldpath, newpath) => 0;
415    }
416    lfs_unmount(&lfs);
417
418    lfs_mount(&lfs, cfg) => 0;
419    lfs_dir_open(&lfs, &dir, "/") => 0;
420    lfs_dir_read(&lfs, &dir, &info) => 1;
421    assert(info.type == LFS_TYPE_DIR);
422    assert(strcmp(info.name, ".") == 0);
423    lfs_dir_read(&lfs, &dir, &info) => 1;
424    assert(info.type == LFS_TYPE_DIR);
425    assert(strcmp(info.name, "..") == 0);
426    for (int i = 0; i < N; i++) {
427        char path[1024];
428        sprintf(path, "tedd%03d", i);
429        lfs_dir_read(&lfs, &dir, &info) => 1;
430        assert(info.type == LFS_TYPE_REG);
431        assert(strcmp(info.name, path) == 0);
432    }
433    lfs_dir_read(&lfs, &dir, &info) => 0;
434    lfs_dir_close(&lfs, &dir) => 0;
435    lfs_unmount(&lfs);
436'''
437
438[cases.test_dirs_file_reentrant]
439defines.N = [5, 25]
440if = 'N < BLOCK_COUNT/2'
441reentrant = true
442code = '''
443    lfs_t lfs;
444    int err = lfs_mount(&lfs, cfg);
445    if (err) {
446        lfs_format(&lfs, cfg) => 0;
447        lfs_mount(&lfs, cfg) => 0;
448    }
449
450    for (int i = 0; i < N; i++) {
451        char path[1024];
452        sprintf(path, "hi%03d", i);
453        lfs_file_t file;
454        lfs_file_open(&lfs, &file, path, LFS_O_CREAT | LFS_O_WRONLY) => 0;
455        lfs_file_close(&lfs, &file) => 0;
456    }
457
458    for (int i = 0; i < N; i++) {
459        char path[1024];
460        sprintf(path, "hello%03d", i);
461        err = lfs_remove(&lfs, path);
462        assert(err == 0 || err == LFS_ERR_NOENT);
463    }
464
465    lfs_dir_t dir;
466    lfs_dir_open(&lfs, &dir, "/") => 0;
467    struct lfs_info info;
468    lfs_dir_read(&lfs, &dir, &info) => 1;
469    assert(info.type == LFS_TYPE_DIR);
470    assert(strcmp(info.name, ".") == 0);
471    lfs_dir_read(&lfs, &dir, &info) => 1;
472    assert(info.type == LFS_TYPE_DIR);
473    assert(strcmp(info.name, "..") == 0);
474    for (int i = 0; i < N; i++) {
475        char path[1024];
476        sprintf(path, "hi%03d", i);
477        lfs_dir_read(&lfs, &dir, &info) => 1;
478        assert(info.type == LFS_TYPE_REG);
479        assert(strcmp(info.name, path) == 0);
480    }
481    lfs_dir_read(&lfs, &dir, &info) => 0;
482    lfs_dir_close(&lfs, &dir) => 0;
483
484    for (int i = 0; i < N; i++) {
485        char oldpath[128];
486        char newpath[128];
487        sprintf(oldpath, "hi%03d", i);
488        sprintf(newpath, "hello%03d", i);
489        // YES this can overwrite an existing newpath
490        lfs_rename(&lfs, oldpath, newpath) => 0;
491    }
492
493    lfs_dir_open(&lfs, &dir, "/") => 0;
494    lfs_dir_read(&lfs, &dir, &info) => 1;
495    assert(info.type == LFS_TYPE_DIR);
496    assert(strcmp(info.name, ".") == 0);
497    lfs_dir_read(&lfs, &dir, &info) => 1;
498    assert(info.type == LFS_TYPE_DIR);
499    assert(strcmp(info.name, "..") == 0);
500    for (int i = 0; i < N; i++) {
501        char path[1024];
502        sprintf(path, "hello%03d", i);
503        lfs_dir_read(&lfs, &dir, &info) => 1;
504        assert(info.type == LFS_TYPE_REG);
505        assert(strcmp(info.name, path) == 0);
506    }
507    lfs_dir_read(&lfs, &dir, &info) => 0;
508    lfs_dir_close(&lfs, &dir) => 0;
509
510    for (int i = 0; i < N; i++) {
511        char path[1024];
512        sprintf(path, "hello%03d", i);
513        lfs_remove(&lfs, path) => 0;
514    }
515
516    lfs_dir_open(&lfs, &dir, "/") => 0;
517    lfs_dir_read(&lfs, &dir, &info) => 1;
518    assert(info.type == LFS_TYPE_DIR);
519    assert(strcmp(info.name, ".") == 0);
520    lfs_dir_read(&lfs, &dir, &info) => 1;
521    assert(info.type == LFS_TYPE_DIR);
522    assert(strcmp(info.name, "..") == 0);
523    lfs_dir_read(&lfs, &dir, &info) => 0;
524    lfs_dir_close(&lfs, &dir) => 0;
525    lfs_unmount(&lfs) => 0;
526'''
527
528[cases.test_dirs_nested]
529code = '''
530    lfs_t lfs;
531    lfs_format(&lfs, cfg) => 0;
532    lfs_mount(&lfs, cfg) => 0;
533    lfs_mkdir(&lfs, "potato") => 0;
534    lfs_file_t file;
535    lfs_file_open(&lfs, &file, "burito",
536            LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
537    lfs_file_close(&lfs, &file) => 0;
538    lfs_unmount(&lfs) => 0;
539
540    lfs_mount(&lfs, cfg) => 0;
541    lfs_mkdir(&lfs, "potato/baked") => 0;
542    lfs_mkdir(&lfs, "potato/sweet") => 0;
543    lfs_mkdir(&lfs, "potato/fried") => 0;
544    lfs_unmount(&lfs) => 0;
545
546    lfs_mount(&lfs, cfg) => 0;
547    lfs_dir_t dir;
548    lfs_dir_open(&lfs, &dir, "potato") => 0;
549    struct lfs_info info;
550    lfs_dir_read(&lfs, &dir, &info) => 1;
551    assert(strcmp(info.name, ".") == 0);
552    info.type => LFS_TYPE_DIR;
553    lfs_dir_read(&lfs, &dir, &info) => 1;
554    assert(strcmp(info.name, "..") == 0);
555    info.type => LFS_TYPE_DIR;
556    lfs_dir_read(&lfs, &dir, &info) => 1;
557    assert(strcmp(info.name, "baked") == 0);
558    info.type => LFS_TYPE_DIR;
559    lfs_dir_read(&lfs, &dir, &info) => 1;
560    assert(strcmp(info.name, "fried") == 0);
561    info.type => LFS_TYPE_DIR;
562    lfs_dir_read(&lfs, &dir, &info) => 1;
563    assert(strcmp(info.name, "sweet") == 0);
564    info.type => LFS_TYPE_DIR;
565    lfs_dir_read(&lfs, &dir, &info) => 0;
566    lfs_dir_close(&lfs, &dir) => 0;
567    lfs_unmount(&lfs) => 0;
568
569    // try removing?
570    lfs_mount(&lfs, cfg) => 0;
571    lfs_remove(&lfs, "potato") => LFS_ERR_NOTEMPTY;
572    lfs_unmount(&lfs) => 0;
573
574    // try renaming?
575    lfs_mount(&lfs, cfg) => 0;
576    lfs_rename(&lfs, "potato", "coldpotato") => 0;
577    lfs_unmount(&lfs) => 0;
578
579    lfs_mount(&lfs, cfg) => 0;
580    lfs_rename(&lfs, "coldpotato", "warmpotato") => 0;
581    lfs_rename(&lfs, "warmpotato", "hotpotato") => 0;
582    lfs_unmount(&lfs) => 0;
583
584    lfs_mount(&lfs, cfg) => 0;
585    lfs_remove(&lfs, "potato") => LFS_ERR_NOENT;
586    lfs_remove(&lfs, "coldpotato") => LFS_ERR_NOENT;
587    lfs_remove(&lfs, "warmpotato") => LFS_ERR_NOENT;
588    lfs_remove(&lfs, "hotpotato") => LFS_ERR_NOTEMPTY;
589    lfs_unmount(&lfs) => 0;
590
591    // try cross-directory renaming
592    lfs_mount(&lfs, cfg) => 0;
593    lfs_mkdir(&lfs, "coldpotato") => 0;
594    lfs_rename(&lfs, "hotpotato/baked", "coldpotato/baked") => 0;
595    lfs_rename(&lfs, "coldpotato", "hotpotato") => LFS_ERR_NOTEMPTY;
596    lfs_remove(&lfs, "coldpotato") => LFS_ERR_NOTEMPTY;
597    lfs_remove(&lfs, "hotpotato") => LFS_ERR_NOTEMPTY;
598    lfs_rename(&lfs, "hotpotato/fried", "coldpotato/fried") => 0;
599    lfs_rename(&lfs, "coldpotato", "hotpotato") => LFS_ERR_NOTEMPTY;
600    lfs_remove(&lfs, "coldpotato") => LFS_ERR_NOTEMPTY;
601    lfs_remove(&lfs, "hotpotato") => LFS_ERR_NOTEMPTY;
602    lfs_rename(&lfs, "hotpotato/sweet", "coldpotato/sweet") => 0;
603    lfs_rename(&lfs, "coldpotato", "hotpotato") => 0;
604    lfs_remove(&lfs, "coldpotato") => LFS_ERR_NOENT;
605    lfs_remove(&lfs, "hotpotato") => LFS_ERR_NOTEMPTY;
606    lfs_unmount(&lfs) => 0;
607
608    lfs_mount(&lfs, cfg) => 0;
609    lfs_dir_open(&lfs, &dir, "hotpotato") => 0;
610    lfs_dir_read(&lfs, &dir, &info) => 1;
611    assert(strcmp(info.name, ".") == 0);
612    info.type => LFS_TYPE_DIR;
613    lfs_dir_read(&lfs, &dir, &info) => 1;
614    assert(strcmp(info.name, "..") == 0);
615    info.type => LFS_TYPE_DIR;
616    lfs_dir_read(&lfs, &dir, &info) => 1;
617    assert(strcmp(info.name, "baked") == 0);
618    info.type => LFS_TYPE_DIR;
619    lfs_dir_read(&lfs, &dir, &info) => 1;
620    assert(strcmp(info.name, "fried") == 0);
621    info.type => LFS_TYPE_DIR;
622    lfs_dir_read(&lfs, &dir, &info) => 1;
623    assert(strcmp(info.name, "sweet") == 0);
624    info.type => LFS_TYPE_DIR;
625    lfs_dir_read(&lfs, &dir, &info) => 0;
626    lfs_dir_close(&lfs, &dir) => 0;
627    lfs_unmount(&lfs) => 0;
628
629    // final remove
630    lfs_mount(&lfs, cfg) => 0;
631    lfs_remove(&lfs, "hotpotato") => LFS_ERR_NOTEMPTY;
632    lfs_remove(&lfs, "hotpotato/baked") => 0;
633    lfs_remove(&lfs, "hotpotato") => LFS_ERR_NOTEMPTY;
634    lfs_remove(&lfs, "hotpotato/fried") => 0;
635    lfs_remove(&lfs, "hotpotato") => LFS_ERR_NOTEMPTY;
636    lfs_remove(&lfs, "hotpotato/sweet") => 0;
637    lfs_remove(&lfs, "hotpotato") => 0;
638    lfs_unmount(&lfs) => 0;
639
640    lfs_mount(&lfs, cfg) => 0;
641    lfs_dir_open(&lfs, &dir, "/") => 0;
642    lfs_dir_read(&lfs, &dir, &info) => 1;
643    assert(strcmp(info.name, ".") == 0);
644    info.type => LFS_TYPE_DIR;
645    lfs_dir_read(&lfs, &dir, &info) => 1;
646    assert(strcmp(info.name, "..") == 0);
647    info.type => LFS_TYPE_DIR;
648    lfs_dir_read(&lfs, &dir, &info) => 1;
649    assert(strcmp(info.name, "burito") == 0);
650    info.type => LFS_TYPE_REG;
651    lfs_dir_read(&lfs, &dir, &info) => 0;
652    lfs_dir_close(&lfs, &dir) => 0;
653    lfs_unmount(&lfs) => 0;
654'''
655
656[cases.test_dirs_recursive_remove]
657defines.N = [10, 100]
658if = 'N < BLOCK_COUNT/2'
659code = '''
660    lfs_t lfs;
661    lfs_format(&lfs, cfg) => 0;
662    lfs_mount(&lfs, cfg) => 0;
663    lfs_mkdir(&lfs, "prickly-pear") => 0;
664    for (int i = 0; i < N; i++) {
665        char path[1024];
666        sprintf(path, "prickly-pear/cactus%03d", i);
667        lfs_mkdir(&lfs, path) => 0;
668    }
669    lfs_dir_t dir;
670    lfs_dir_open(&lfs, &dir, "prickly-pear") => 0;
671    struct lfs_info info;
672    lfs_dir_read(&lfs, &dir, &info) => 1;
673    assert(info.type == LFS_TYPE_DIR);
674    assert(strcmp(info.name, ".") == 0);
675    lfs_dir_read(&lfs, &dir, &info) => 1;
676    assert(info.type == LFS_TYPE_DIR);
677    assert(strcmp(info.name, "..") == 0);
678    for (int i = 0; i < N; i++) {
679        char path[1024];
680        sprintf(path, "cactus%03d", i);
681        lfs_dir_read(&lfs, &dir, &info) => 1;
682        assert(info.type == LFS_TYPE_DIR);
683        assert(strcmp(info.name, path) == 0);
684    }
685    lfs_dir_read(&lfs, &dir, &info) => 0;
686    lfs_dir_close(&lfs, &dir) => 0;
687    lfs_unmount(&lfs);
688
689    lfs_mount(&lfs, cfg) => 0;
690    lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOTEMPTY;
691
692    lfs_dir_open(&lfs, &dir, "prickly-pear") => 0;
693    lfs_dir_read(&lfs, &dir, &info) => 1;
694    assert(info.type == LFS_TYPE_DIR);
695    assert(strcmp(info.name, ".") == 0);
696    lfs_dir_read(&lfs, &dir, &info) => 1;
697    assert(info.type == LFS_TYPE_DIR);
698    assert(strcmp(info.name, "..") == 0);
699    for (int i = 0; i < N; i++) {
700        char path[1024];
701        sprintf(path, "cactus%03d", i);
702        lfs_dir_read(&lfs, &dir, &info) => 1;
703        assert(info.type == LFS_TYPE_DIR);
704        assert(strcmp(info.name, path) == 0);
705        sprintf(path, "prickly-pear/%s", info.name);
706        lfs_remove(&lfs, path) => 0;
707    }
708    lfs_dir_read(&lfs, &dir, &info) => 0;
709    lfs_dir_close(&lfs, &dir) => 0;
710
711    lfs_remove(&lfs, "prickly-pear") => 0;
712    lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOENT;
713    lfs_unmount(&lfs) => 0;
714
715    lfs_mount(&lfs, cfg) => 0;
716    lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOENT;
717    lfs_unmount(&lfs) => 0;
718'''
719
720[cases.test_dirs_other_errors]
721code = '''
722    lfs_t lfs;
723    lfs_format(&lfs, cfg) => 0;
724    lfs_mount(&lfs, cfg) => 0;
725    lfs_mkdir(&lfs, "potato") => 0;
726    lfs_file_t file;
727    lfs_file_open(&lfs, &file, "burito",
728            LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
729    lfs_file_close(&lfs, &file) => 0;
730    lfs_unmount(&lfs) => 0;
731
732    lfs_mount(&lfs, cfg) => 0;
733
734    lfs_mkdir(&lfs, "potato") => LFS_ERR_EXIST;
735    lfs_mkdir(&lfs, "burito") => LFS_ERR_EXIST;
736    lfs_file_open(&lfs, &file, "burito",
737            LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_EXIST;
738    lfs_file_open(&lfs, &file, "potato",
739            LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_EXIST;
740    lfs_dir_t dir;
741    lfs_dir_open(&lfs, &dir, "tomato") => LFS_ERR_NOENT;
742    lfs_dir_open(&lfs, &dir, "burito") => LFS_ERR_NOTDIR;
743    lfs_file_open(&lfs, &file, "tomato", LFS_O_RDONLY) => LFS_ERR_NOENT;
744    lfs_file_open(&lfs, &file, "potato", LFS_O_RDONLY) => LFS_ERR_ISDIR;
745    lfs_file_open(&lfs, &file, "tomato", LFS_O_WRONLY) => LFS_ERR_NOENT;
746    lfs_file_open(&lfs, &file, "potato", LFS_O_WRONLY) => LFS_ERR_ISDIR;
747    lfs_file_open(&lfs, &file, "potato",
748            LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_ISDIR;
749
750    lfs_mkdir(&lfs, "/") => LFS_ERR_EXIST;
751    lfs_file_open(&lfs, &file, "/",
752            LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_EXIST;
753    lfs_file_open(&lfs, &file, "/", LFS_O_RDONLY) => LFS_ERR_ISDIR;
754    lfs_file_open(&lfs, &file, "/", LFS_O_WRONLY) => LFS_ERR_ISDIR;
755    lfs_file_open(&lfs, &file, "/",
756            LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_ISDIR;
757
758    // check that errors did not corrupt directory
759    lfs_dir_open(&lfs, &dir, "/") => 0;
760    struct lfs_info info;
761    lfs_dir_read(&lfs, &dir, &info) => 1;
762    assert(info.type == LFS_TYPE_DIR);
763    assert(strcmp(info.name, ".") == 0);
764    lfs_dir_read(&lfs, &dir, &info) => 1;
765    assert(info.type == LFS_TYPE_DIR);
766    assert(strcmp(info.name, "..") == 0);
767    lfs_dir_read(&lfs, &dir, &info) => 1;
768    assert(info.type == LFS_TYPE_REG);
769    assert(strcmp(info.name, "burito") == 0);
770    lfs_dir_read(&lfs, &dir, &info) => 1;
771    assert(info.type == LFS_TYPE_DIR);
772    assert(strcmp(info.name, "potato") == 0);
773    lfs_dir_read(&lfs, &dir, &info) => 0;
774    lfs_dir_close(&lfs, &dir) => 0;
775
776    lfs_unmount(&lfs) => 0;
777
778    // or on disk
779    lfs_mount(&lfs, cfg) => 0;
780    lfs_dir_open(&lfs, &dir, "/") => 0;
781    lfs_dir_read(&lfs, &dir, &info) => 1;
782    assert(info.type == LFS_TYPE_DIR);
783    assert(strcmp(info.name, ".") == 0);
784    lfs_dir_read(&lfs, &dir, &info) => 1;
785    assert(info.type == LFS_TYPE_DIR);
786    assert(strcmp(info.name, "..") == 0);
787    lfs_dir_read(&lfs, &dir, &info) => 1;
788    assert(info.type == LFS_TYPE_REG);
789    assert(strcmp(info.name, "burito") == 0);
790    lfs_dir_read(&lfs, &dir, &info) => 1;
791    assert(info.type == LFS_TYPE_DIR);
792    assert(strcmp(info.name, "potato") == 0);
793    lfs_dir_read(&lfs, &dir, &info) => 0;
794    lfs_dir_close(&lfs, &dir) => 0;
795    lfs_unmount(&lfs) => 0;
796'''
797
798[cases.test_dirs_seek]
799defines.COUNT = [4, 128, 132]
800if = 'COUNT < BLOCK_COUNT/2'
801code = '''
802    lfs_t lfs;
803    lfs_format(&lfs, cfg) => 0;
804    lfs_mount(&lfs, cfg) => 0;
805    lfs_mkdir(&lfs, "hello") => 0;
806    for (int i = 0; i < COUNT; i++) {
807        char path[1024];
808        sprintf(path, "hello/kitty%03d", i);
809        lfs_mkdir(&lfs, path) => 0;
810    }
811    lfs_unmount(&lfs) => 0;
812
813    // try seeking to each dir entry
814    for (int j = 0; j < COUNT; j++) {
815        lfs_mount(&lfs, cfg) => 0;
816        lfs_dir_t dir;
817        lfs_dir_open(&lfs, &dir, "hello") => 0;
818        struct lfs_info info;
819        lfs_dir_read(&lfs, &dir, &info) => 1;
820        assert(strcmp(info.name, ".") == 0);
821        assert(info.type == LFS_TYPE_DIR);
822        lfs_dir_read(&lfs, &dir, &info) => 1;
823        assert(strcmp(info.name, "..") == 0);
824        assert(info.type == LFS_TYPE_DIR);
825
826        for (int i = 0; i < j; i++) {
827            char path[1024];
828            sprintf(path, "kitty%03d", i);
829            lfs_dir_read(&lfs, &dir, &info) => 1;
830            assert(strcmp(info.name, path) == 0);
831            assert(info.type == LFS_TYPE_DIR);
832        }
833        lfs_soff_t pos = lfs_dir_tell(&lfs, &dir);
834        assert(pos >= 0);
835
836        lfs_dir_seek(&lfs, &dir, pos) => 0;
837        char path[1024];
838        sprintf(path, "kitty%03d", j);
839        lfs_dir_read(&lfs, &dir, &info) => 1;
840        assert(strcmp(info.name, path) == 0);
841        assert(info.type == LFS_TYPE_DIR);
842
843        lfs_dir_rewind(&lfs, &dir) => 0;
844        sprintf(path, "kitty%03u", 0);
845        lfs_dir_read(&lfs, &dir, &info) => 1;
846        assert(strcmp(info.name, ".") == 0);
847        assert(info.type == LFS_TYPE_DIR);
848        lfs_dir_read(&lfs, &dir, &info) => 1;
849        assert(strcmp(info.name, "..") == 0);
850        assert(info.type == LFS_TYPE_DIR);
851        lfs_dir_read(&lfs, &dir, &info) => 1;
852        assert(strcmp(info.name, path) == 0);
853        assert(info.type == LFS_TYPE_DIR);
854
855        lfs_dir_seek(&lfs, &dir, pos) => 0;
856        sprintf(path, "kitty%03d", j);
857        lfs_dir_read(&lfs, &dir, &info) => 1;
858        assert(strcmp(info.name, path) == 0);
859        assert(info.type == LFS_TYPE_DIR);
860
861        lfs_dir_close(&lfs, &dir) => 0;
862        lfs_unmount(&lfs) => 0;
863    }
864
865    // try seeking to end of dir
866    lfs_mount(&lfs, cfg) => 0;
867    lfs_dir_t dir;
868    lfs_dir_open(&lfs, &dir, "hello") => 0;
869    struct lfs_info info;
870    lfs_dir_read(&lfs, &dir, &info) => 1;
871    assert(strcmp(info.name, ".") == 0);
872    assert(info.type == LFS_TYPE_DIR);
873    lfs_dir_read(&lfs, &dir, &info) => 1;
874    assert(strcmp(info.name, "..") == 0);
875    assert(info.type == LFS_TYPE_DIR);
876
877    for (int i = 0; i < COUNT; i++) {
878        char path[1024];
879        sprintf(path, "kitty%03d", i);
880        lfs_dir_read(&lfs, &dir, &info) => 1;
881        assert(strcmp(info.name, path) == 0);
882        assert(info.type == LFS_TYPE_DIR);
883    }
884    lfs_soff_t pos = lfs_dir_tell(&lfs, &dir);
885    assert(pos >= 0);
886
887    lfs_dir_read(&lfs, &dir, &info) => 0;
888
889    lfs_dir_seek(&lfs, &dir, pos) => 0;
890    lfs_dir_read(&lfs, &dir, &info) => 0;
891
892    lfs_dir_rewind(&lfs, &dir) => 0;
893    char path[1024];
894    sprintf(path, "kitty%03d", 0);
895    lfs_dir_read(&lfs, &dir, &info) => 1;
896    assert(strcmp(info.name, ".") == 0);
897    assert(info.type == LFS_TYPE_DIR);
898    lfs_dir_read(&lfs, &dir, &info) => 1;
899    assert(strcmp(info.name, "..") == 0);
900    assert(info.type == LFS_TYPE_DIR);
901    lfs_dir_read(&lfs, &dir, &info) => 1;
902    assert(strcmp(info.name, path) == 0);
903    assert(info.type == LFS_TYPE_DIR);
904
905    lfs_dir_seek(&lfs, &dir, pos) => 0;
906    lfs_dir_read(&lfs, &dir, &info) => 0;
907
908    lfs_dir_close(&lfs, &dir) => 0;
909    lfs_unmount(&lfs) => 0;
910'''
911
912[cases.test_dirs_toot_seek]
913defines.COUNT = [4, 128, 132]
914if = 'COUNT < BLOCK_COUNT/2'
915code = '''
916    lfs_t lfs;
917    lfs_format(&lfs, cfg) => 0;
918    lfs_mount(&lfs, cfg) => 0;
919    for (int i = 0; i < COUNT; i++) {
920        char path[1024];
921        sprintf(path, "hi%03d", i);
922        lfs_mkdir(&lfs, path) => 0;
923    }
924    lfs_unmount(&lfs) => 0;
925
926    for (int j = 0; j < COUNT; j++) {
927        lfs_mount(&lfs, cfg) => 0;
928        lfs_dir_t dir;
929        lfs_dir_open(&lfs, &dir, "/") => 0;
930        struct lfs_info info;
931        lfs_dir_read(&lfs, &dir, &info) => 1;
932        assert(strcmp(info.name, ".") == 0);
933        assert(info.type == LFS_TYPE_DIR);
934        lfs_dir_read(&lfs, &dir, &info) => 1;
935        assert(strcmp(info.name, "..") == 0);
936        assert(info.type == LFS_TYPE_DIR);
937
938        for (int i = 0; i < j; i++) {
939            char path[1024];
940            sprintf(path, "hi%03d", i);
941            lfs_dir_read(&lfs, &dir, &info) => 1;
942            assert(strcmp(info.name, path) == 0);
943            assert(info.type == LFS_TYPE_DIR);
944        }
945        lfs_soff_t pos = lfs_dir_tell(&lfs, &dir);
946        assert(pos >= 0);
947
948        lfs_dir_seek(&lfs, &dir, pos) => 0;
949        char path[1024];
950        sprintf(path, "hi%03d", j);
951        lfs_dir_read(&lfs, &dir, &info) => 1;
952        assert(strcmp(info.name, path) == 0);
953        assert(info.type == LFS_TYPE_DIR);
954
955        lfs_dir_rewind(&lfs, &dir) => 0;
956        sprintf(path, "hi%03u", 0);
957        lfs_dir_read(&lfs, &dir, &info) => 1;
958        assert(strcmp(info.name, ".") == 0);
959        assert(info.type == LFS_TYPE_DIR);
960        lfs_dir_read(&lfs, &dir, &info) => 1;
961        assert(strcmp(info.name, "..") == 0);
962        assert(info.type == LFS_TYPE_DIR);
963        lfs_dir_read(&lfs, &dir, &info) => 1;
964        assert(strcmp(info.name, path) == 0);
965        assert(info.type == LFS_TYPE_DIR);
966
967        lfs_dir_seek(&lfs, &dir, pos) => 0;
968        sprintf(path, "hi%03d", j);
969        lfs_dir_read(&lfs, &dir, &info) => 1;
970        assert(strcmp(info.name, path) == 0);
971        assert(info.type == LFS_TYPE_DIR);
972
973        lfs_dir_close(&lfs, &dir) => 0;
974        lfs_unmount(&lfs) => 0;
975    }
976
977    // try seeking to end of dir
978    lfs_mount(&lfs, cfg) => 0;
979    lfs_dir_t dir;
980    lfs_dir_open(&lfs, &dir, "/") => 0;
981    struct lfs_info info;
982    lfs_dir_read(&lfs, &dir, &info) => 1;
983    assert(strcmp(info.name, ".") == 0);
984    assert(info.type == LFS_TYPE_DIR);
985    lfs_dir_read(&lfs, &dir, &info) => 1;
986    assert(strcmp(info.name, "..") == 0);
987    assert(info.type == LFS_TYPE_DIR);
988
989    for (int i = 0; i < COUNT; i++) {
990        char path[1024];
991        sprintf(path, "hi%03d", i);
992        lfs_dir_read(&lfs, &dir, &info) => 1;
993        assert(strcmp(info.name, path) == 0);
994        assert(info.type == LFS_TYPE_DIR);
995    }
996    lfs_soff_t pos = lfs_dir_tell(&lfs, &dir);
997    assert(pos >= 0);
998
999    lfs_dir_read(&lfs, &dir, &info) => 0;
1000
1001    lfs_dir_seek(&lfs, &dir, pos) => 0;
1002    lfs_dir_read(&lfs, &dir, &info) => 0;
1003
1004    lfs_dir_rewind(&lfs, &dir) => 0;
1005    char path[1024];
1006    sprintf(path, "hi%03d", 0);
1007    lfs_dir_read(&lfs, &dir, &info) => 1;
1008    assert(strcmp(info.name, ".") == 0);
1009    assert(info.type == LFS_TYPE_DIR);
1010    lfs_dir_read(&lfs, &dir, &info) => 1;
1011    assert(strcmp(info.name, "..") == 0);
1012    assert(info.type == LFS_TYPE_DIR);
1013    lfs_dir_read(&lfs, &dir, &info) => 1;
1014    assert(strcmp(info.name, path) == 0);
1015    assert(info.type == LFS_TYPE_DIR);
1016
1017    lfs_dir_seek(&lfs, &dir, pos) => 0;
1018    lfs_dir_read(&lfs, &dir, &info) => 0;
1019
1020    lfs_dir_close(&lfs, &dir) => 0;
1021    lfs_unmount(&lfs) => 0;
1022'''
1023
1024