1# allocator tests
2# note for these to work there are a number constraints on the device geometry
3if = 'LFS_BLOCK_CYCLES == -1'
4
5[[case]] # parallel allocation test
6define.FILES = 3
7define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-6)) / FILES)'
8code = '''
9    const char *names[FILES] = {"bacon", "eggs", "pancakes"};
10    lfs_file_t files[FILES];
11
12    lfs_format(&lfs, &cfg) => 0;
13    lfs_mount(&lfs, &cfg) => 0;
14    lfs_mkdir(&lfs, "breakfast") => 0;
15    lfs_unmount(&lfs) => 0;
16
17    lfs_mount(&lfs, &cfg) => 0;
18    for (int n = 0; n < FILES; n++) {
19        sprintf(path, "breakfast/%s", names[n]);
20        lfs_file_open(&lfs, &files[n], path,
21                LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
22    }
23    for (int n = 0; n < FILES; n++) {
24        size = strlen(names[n]);
25        for (lfs_size_t i = 0; i < SIZE; i += size) {
26            lfs_file_write(&lfs, &files[n], names[n], size) => size;
27        }
28    }
29    for (int n = 0; n < FILES; n++) {
30        lfs_file_close(&lfs, &files[n]) => 0;
31    }
32    lfs_unmount(&lfs) => 0;
33
34    lfs_mount(&lfs, &cfg) => 0;
35    for (int n = 0; n < FILES; n++) {
36        sprintf(path, "breakfast/%s", names[n]);
37        lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
38        size = strlen(names[n]);
39        for (lfs_size_t i = 0; i < SIZE; i += size) {
40            lfs_file_read(&lfs, &file, buffer, size) => size;
41            assert(memcmp(buffer, names[n], size) == 0);
42        }
43        lfs_file_close(&lfs, &file) => 0;
44    }
45    lfs_unmount(&lfs) => 0;
46'''
47
48[[case]] # serial allocation test
49define.FILES = 3
50define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-6)) / FILES)'
51code = '''
52    const char *names[FILES] = {"bacon", "eggs", "pancakes"};
53
54    lfs_format(&lfs, &cfg) => 0;
55    lfs_mount(&lfs, &cfg) => 0;
56    lfs_mkdir(&lfs, "breakfast") => 0;
57    lfs_unmount(&lfs) => 0;
58
59    for (int n = 0; n < FILES; n++) {
60        lfs_mount(&lfs, &cfg) => 0;
61        sprintf(path, "breakfast/%s", names[n]);
62        lfs_file_open(&lfs, &file, path,
63                LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
64        size = strlen(names[n]);
65        memcpy(buffer, names[n], size);
66        for (int i = 0; i < SIZE; i += size) {
67            lfs_file_write(&lfs, &file, buffer, size) => size;
68        }
69        lfs_file_close(&lfs, &file) => 0;
70        lfs_unmount(&lfs) => 0;
71    }
72
73    lfs_mount(&lfs, &cfg) => 0;
74    for (int n = 0; n < FILES; n++) {
75        sprintf(path, "breakfast/%s", names[n]);
76        lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
77        size = strlen(names[n]);
78        for (int i = 0; i < SIZE; i += size) {
79            lfs_file_read(&lfs, &file, buffer, size) => size;
80            assert(memcmp(buffer, names[n], size) == 0);
81        }
82        lfs_file_close(&lfs, &file) => 0;
83    }
84    lfs_unmount(&lfs) => 0;
85'''
86
87[[case]] # parallel allocation reuse test
88define.FILES = 3
89define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-6)) / FILES)'
90define.CYCLES = [1, 10]
91code = '''
92    const char *names[FILES] = {"bacon", "eggs", "pancakes"};
93    lfs_file_t files[FILES];
94
95    lfs_format(&lfs, &cfg) => 0;
96
97    for (int c = 0; c < CYCLES; c++) {
98        lfs_mount(&lfs, &cfg) => 0;
99        lfs_mkdir(&lfs, "breakfast") => 0;
100        lfs_unmount(&lfs) => 0;
101
102        lfs_mount(&lfs, &cfg) => 0;
103        for (int n = 0; n < FILES; n++) {
104            sprintf(path, "breakfast/%s", names[n]);
105            lfs_file_open(&lfs, &files[n], path,
106                    LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
107        }
108        for (int n = 0; n < FILES; n++) {
109            size = strlen(names[n]);
110            for (int i = 0; i < SIZE; i += size) {
111                lfs_file_write(&lfs, &files[n], names[n], size) => size;
112            }
113        }
114        for (int n = 0; n < FILES; n++) {
115            lfs_file_close(&lfs, &files[n]) => 0;
116        }
117        lfs_unmount(&lfs) => 0;
118
119        lfs_mount(&lfs, &cfg) => 0;
120        for (int n = 0; n < FILES; n++) {
121            sprintf(path, "breakfast/%s", names[n]);
122            lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
123            size = strlen(names[n]);
124            for (int i = 0; i < SIZE; i += size) {
125                lfs_file_read(&lfs, &file, buffer, size) => size;
126                assert(memcmp(buffer, names[n], size) == 0);
127            }
128            lfs_file_close(&lfs, &file) => 0;
129        }
130        lfs_unmount(&lfs) => 0;
131
132        lfs_mount(&lfs, &cfg) => 0;
133        for (int n = 0; n < FILES; n++) {
134            sprintf(path, "breakfast/%s", names[n]);
135            lfs_remove(&lfs, path) => 0;
136        }
137        lfs_remove(&lfs, "breakfast") => 0;
138        lfs_unmount(&lfs) => 0;
139    }
140'''
141
142[[case]] # serial allocation reuse test
143define.FILES = 3
144define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-6)) / FILES)'
145define.CYCLES = [1, 10]
146code = '''
147    const char *names[FILES] = {"bacon", "eggs", "pancakes"};
148
149    lfs_format(&lfs, &cfg) => 0;
150
151    for (int c = 0; c < CYCLES; c++) {
152        lfs_mount(&lfs, &cfg) => 0;
153        lfs_mkdir(&lfs, "breakfast") => 0;
154        lfs_unmount(&lfs) => 0;
155
156        for (int n = 0; n < FILES; n++) {
157            lfs_mount(&lfs, &cfg) => 0;
158            sprintf(path, "breakfast/%s", names[n]);
159            lfs_file_open(&lfs, &file, path,
160                    LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
161            size = strlen(names[n]);
162            memcpy(buffer, names[n], size);
163            for (int i = 0; i < SIZE; i += size) {
164                lfs_file_write(&lfs, &file, buffer, size) => size;
165            }
166            lfs_file_close(&lfs, &file) => 0;
167            lfs_unmount(&lfs) => 0;
168        }
169
170        lfs_mount(&lfs, &cfg) => 0;
171        for (int n = 0; n < FILES; n++) {
172            sprintf(path, "breakfast/%s", names[n]);
173            lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0;
174            size = strlen(names[n]);
175            for (int i = 0; i < SIZE; i += size) {
176                lfs_file_read(&lfs, &file, buffer, size) => size;
177                assert(memcmp(buffer, names[n], size) == 0);
178            }
179            lfs_file_close(&lfs, &file) => 0;
180        }
181        lfs_unmount(&lfs) => 0;
182
183        lfs_mount(&lfs, &cfg) => 0;
184        for (int n = 0; n < FILES; n++) {
185            sprintf(path, "breakfast/%s", names[n]);
186            lfs_remove(&lfs, path) => 0;
187        }
188        lfs_remove(&lfs, "breakfast") => 0;
189        lfs_unmount(&lfs) => 0;
190    }
191'''
192
193[[case]] # exhaustion test
194code = '''
195    lfs_format(&lfs, &cfg) => 0;
196    lfs_mount(&lfs, &cfg) => 0;
197    lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
198    size = strlen("exhaustion");
199    memcpy(buffer, "exhaustion", size);
200    lfs_file_write(&lfs, &file, buffer, size) => size;
201    lfs_file_sync(&lfs, &file) => 0;
202
203    size = strlen("blahblahblahblah");
204    memcpy(buffer, "blahblahblahblah", size);
205    lfs_ssize_t res;
206    while (true) {
207        res = lfs_file_write(&lfs, &file, buffer, size);
208        if (res < 0) {
209            break;
210        }
211
212        res => size;
213    }
214    res => LFS_ERR_NOSPC;
215
216    lfs_file_close(&lfs, &file) => 0;
217    lfs_unmount(&lfs) => 0;
218
219    lfs_mount(&lfs, &cfg) => 0;
220    lfs_file_open(&lfs, &file, "exhaustion", LFS_O_RDONLY);
221    size = strlen("exhaustion");
222    lfs_file_size(&lfs, &file) => size;
223    lfs_file_read(&lfs, &file, buffer, size) => size;
224    memcmp(buffer, "exhaustion", size) => 0;
225    lfs_file_close(&lfs, &file) => 0;
226    lfs_unmount(&lfs) => 0;
227'''
228
229[[case]] # exhaustion wraparound test
230define.SIZE = '(((LFS_BLOCK_SIZE-8)*(LFS_BLOCK_COUNT-4)) / 3)'
231code = '''
232    lfs_format(&lfs, &cfg) => 0;
233    lfs_mount(&lfs, &cfg) => 0;
234
235    lfs_file_open(&lfs, &file, "padding", LFS_O_WRONLY | LFS_O_CREAT);
236    size = strlen("buffering");
237    memcpy(buffer, "buffering", size);
238    for (int i = 0; i < SIZE; i += size) {
239        lfs_file_write(&lfs, &file, buffer, size) => size;
240    }
241    lfs_file_close(&lfs, &file) => 0;
242    lfs_remove(&lfs, "padding") => 0;
243
244    lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
245    size = strlen("exhaustion");
246    memcpy(buffer, "exhaustion", size);
247    lfs_file_write(&lfs, &file, buffer, size) => size;
248    lfs_file_sync(&lfs, &file) => 0;
249
250    size = strlen("blahblahblahblah");
251    memcpy(buffer, "blahblahblahblah", size);
252    lfs_ssize_t res;
253    while (true) {
254        res = lfs_file_write(&lfs, &file, buffer, size);
255        if (res < 0) {
256            break;
257        }
258
259        res => size;
260    }
261    res => LFS_ERR_NOSPC;
262
263    lfs_file_close(&lfs, &file) => 0;
264    lfs_unmount(&lfs) => 0;
265
266    lfs_mount(&lfs, &cfg) => 0;
267    lfs_file_open(&lfs, &file, "exhaustion", LFS_O_RDONLY);
268    size = strlen("exhaustion");
269    lfs_file_size(&lfs, &file) => size;
270    lfs_file_read(&lfs, &file, buffer, size) => size;
271    memcmp(buffer, "exhaustion", size) => 0;
272    lfs_file_close(&lfs, &file) => 0;
273    lfs_remove(&lfs, "exhaustion") => 0;
274    lfs_unmount(&lfs) => 0;
275'''
276
277[[case]] # dir exhaustion test
278code = '''
279    lfs_format(&lfs, &cfg) => 0;
280    lfs_mount(&lfs, &cfg) => 0;
281
282    // find out max file size
283    lfs_mkdir(&lfs, "exhaustiondir") => 0;
284    size = strlen("blahblahblahblah");
285    memcpy(buffer, "blahblahblahblah", size);
286    lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
287    int count = 0;
288    while (true) {
289        err = lfs_file_write(&lfs, &file, buffer, size);
290        if (err < 0) {
291            break;
292        }
293
294        count += 1;
295    }
296    err => LFS_ERR_NOSPC;
297    lfs_file_close(&lfs, &file) => 0;
298
299    lfs_remove(&lfs, "exhaustion") => 0;
300    lfs_remove(&lfs, "exhaustiondir") => 0;
301
302    // see if dir fits with max file size
303    lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
304    for (int i = 0; i < count; i++) {
305        lfs_file_write(&lfs, &file, buffer, size) => size;
306    }
307    lfs_file_close(&lfs, &file) => 0;
308
309    lfs_mkdir(&lfs, "exhaustiondir") => 0;
310    lfs_remove(&lfs, "exhaustiondir") => 0;
311    lfs_remove(&lfs, "exhaustion") => 0;
312
313    // see if dir fits with > max file size
314    lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
315    for (int i = 0; i < count+1; i++) {
316        lfs_file_write(&lfs, &file, buffer, size) => size;
317    }
318    lfs_file_close(&lfs, &file) => 0;
319
320    lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC;
321
322    lfs_remove(&lfs, "exhaustion") => 0;
323    lfs_unmount(&lfs) => 0;
324'''
325
326[[case]] # what if we have a bad block during an allocation scan?
327in = "lfs.c"
328define.LFS_ERASE_CYCLES = 0xffffffff
329define.LFS_BADBLOCK_BEHAVIOR = 'LFS_TESTBD_BADBLOCK_READERROR'
330code = '''
331    lfs_format(&lfs, &cfg) => 0;
332    lfs_mount(&lfs, &cfg) => 0;
333    // first fill to exhaustion to find available space
334    lfs_file_open(&lfs, &file, "pacman", LFS_O_WRONLY | LFS_O_CREAT) => 0;
335    strcpy((char*)buffer, "waka");
336    size = strlen("waka");
337    lfs_size_t filesize = 0;
338    while (true) {
339        lfs_ssize_t res = lfs_file_write(&lfs, &file, buffer, size);
340        assert(res == (lfs_ssize_t)size || res == LFS_ERR_NOSPC);
341        if (res == LFS_ERR_NOSPC) {
342            break;
343        }
344        filesize += size;
345    }
346    lfs_file_close(&lfs, &file) => 0;
347    // now fill all but a couple of blocks of the filesystem with data
348    filesize -= 3*LFS_BLOCK_SIZE;
349    lfs_file_open(&lfs, &file, "pacman", LFS_O_WRONLY | LFS_O_CREAT) => 0;
350    strcpy((char*)buffer, "waka");
351    size = strlen("waka");
352    for (lfs_size_t i = 0; i < filesize/size; i++) {
353        lfs_file_write(&lfs, &file, buffer, size) => size;
354    }
355    lfs_file_close(&lfs, &file) => 0;
356    // also save head of file so we can error during lookahead scan
357    lfs_block_t fileblock = file.ctz.head;
358    lfs_unmount(&lfs) => 0;
359
360    // remount to force an alloc scan
361    lfs_mount(&lfs, &cfg) => 0;
362
363    // but mark the head of our file as a "bad block", this is force our
364    // scan to bail early
365    lfs_testbd_setwear(&cfg, fileblock, 0xffffffff) => 0;
366    lfs_file_open(&lfs, &file, "ghost", LFS_O_WRONLY | LFS_O_CREAT) => 0;
367    strcpy((char*)buffer, "chomp");
368    size = strlen("chomp");
369    while (true) {
370        lfs_ssize_t res = lfs_file_write(&lfs, &file, buffer, size);
371        assert(res == (lfs_ssize_t)size || res == LFS_ERR_CORRUPT);
372        if (res == LFS_ERR_CORRUPT) {
373            break;
374        }
375    }
376    lfs_file_close(&lfs, &file) => 0;
377
378    // now reverse the "bad block" and try to write the file again until we
379    // run out of space
380    lfs_testbd_setwear(&cfg, fileblock, 0) => 0;
381    lfs_file_open(&lfs, &file, "ghost", LFS_O_WRONLY | LFS_O_CREAT) => 0;
382    strcpy((char*)buffer, "chomp");
383    size = strlen("chomp");
384    while (true) {
385        lfs_ssize_t res = lfs_file_write(&lfs, &file, buffer, size);
386        assert(res == (lfs_ssize_t)size || res == LFS_ERR_NOSPC);
387        if (res == LFS_ERR_NOSPC) {
388            break;
389        }
390    }
391    lfs_file_close(&lfs, &file) => 0;
392
393    lfs_unmount(&lfs) => 0;
394
395    // check that the disk isn't hurt
396    lfs_mount(&lfs, &cfg) => 0;
397    lfs_file_open(&lfs, &file, "pacman", LFS_O_RDONLY) => 0;
398    strcpy((char*)buffer, "waka");
399    size = strlen("waka");
400    for (lfs_size_t i = 0; i < filesize/size; i++) {
401        uint8_t rbuffer[4];
402        lfs_file_read(&lfs, &file, rbuffer, size) => size;
403        assert(memcmp(rbuffer, buffer, size) == 0);
404    }
405    lfs_file_close(&lfs, &file) => 0;
406    lfs_unmount(&lfs) => 0;
407'''
408
409
410# Below, I don't like these tests. They're fragile and depend _heavily_
411# on the geometry of the block device. But they are valuable. Eventually they
412# should be removed and replaced with generalized tests.
413
414[[case]] # chained dir exhaustion test
415define.LFS_BLOCK_SIZE = 512
416define.LFS_BLOCK_COUNT = 1024
417if = 'LFS_BLOCK_SIZE == 512 && LFS_BLOCK_COUNT == 1024'
418code = '''
419    lfs_format(&lfs, &cfg) => 0;
420    lfs_mount(&lfs, &cfg) => 0;
421
422    // find out max file size
423    lfs_mkdir(&lfs, "exhaustiondir") => 0;
424    for (int i = 0; i < 10; i++) {
425        sprintf(path, "dirwithanexhaustivelylongnameforpadding%d", i);
426        lfs_mkdir(&lfs, path) => 0;
427    }
428    size = strlen("blahblahblahblah");
429    memcpy(buffer, "blahblahblahblah", size);
430    lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
431    int count = 0;
432    while (true) {
433        err = lfs_file_write(&lfs, &file, buffer, size);
434        if (err < 0) {
435            break;
436        }
437
438        count += 1;
439    }
440    err => LFS_ERR_NOSPC;
441    lfs_file_close(&lfs, &file) => 0;
442
443    lfs_remove(&lfs, "exhaustion") => 0;
444    lfs_remove(&lfs, "exhaustiondir") => 0;
445    for (int i = 0; i < 10; i++) {
446        sprintf(path, "dirwithanexhaustivelylongnameforpadding%d", i);
447        lfs_remove(&lfs, path) => 0;
448    }
449
450    // see that chained dir fails
451    lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
452    for (int i = 0; i < count+1; i++) {
453        lfs_file_write(&lfs, &file, buffer, size) => size;
454    }
455    lfs_file_sync(&lfs, &file) => 0;
456
457    for (int i = 0; i < 10; i++) {
458        sprintf(path, "dirwithanexhaustivelylongnameforpadding%d", i);
459        lfs_mkdir(&lfs, path) => 0;
460    }
461
462    lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC;
463
464    // shorten file to try a second chained dir
465    while (true) {
466        err = lfs_mkdir(&lfs, "exhaustiondir");
467        if (err != LFS_ERR_NOSPC) {
468            break;
469        }
470
471        lfs_ssize_t filesize = lfs_file_size(&lfs, &file);
472        filesize > 0 => true;
473
474        lfs_file_truncate(&lfs, &file, filesize - size) => 0;
475        lfs_file_sync(&lfs, &file) => 0;
476    }
477    err => 0;
478
479    lfs_mkdir(&lfs, "exhaustiondir2") => LFS_ERR_NOSPC;
480
481    lfs_file_close(&lfs, &file) => 0;
482    lfs_unmount(&lfs) => 0;
483'''
484
485[[case]] # split dir test
486define.LFS_BLOCK_SIZE = 512
487define.LFS_BLOCK_COUNT = 1024
488if = 'LFS_BLOCK_SIZE == 512 && LFS_BLOCK_COUNT == 1024'
489code = '''
490    lfs_format(&lfs, &cfg) => 0;
491    lfs_mount(&lfs, &cfg) => 0;
492
493    // create one block hole for half a directory
494    lfs_file_open(&lfs, &file, "bump", LFS_O_WRONLY | LFS_O_CREAT) => 0;
495    for (lfs_size_t i = 0; i < cfg.block_size; i += 2) {
496        memcpy(&buffer[i], "hi", 2);
497    }
498    lfs_file_write(&lfs, &file, buffer, cfg.block_size) => cfg.block_size;
499    lfs_file_close(&lfs, &file) => 0;
500
501    lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
502    size = strlen("blahblahblahblah");
503    memcpy(buffer, "blahblahblahblah", size);
504    for (lfs_size_t i = 0;
505            i < (cfg.block_count-4)*(cfg.block_size-8);
506            i += size) {
507        lfs_file_write(&lfs, &file, buffer, size) => size;
508    }
509    lfs_file_close(&lfs, &file) => 0;
510
511    // remount to force reset of lookahead
512    lfs_unmount(&lfs) => 0;
513    lfs_mount(&lfs, &cfg) => 0;
514
515    // open hole
516    lfs_remove(&lfs, "bump") => 0;
517
518    lfs_mkdir(&lfs, "splitdir") => 0;
519    lfs_file_open(&lfs, &file, "splitdir/bump",
520            LFS_O_WRONLY | LFS_O_CREAT) => 0;
521    for (lfs_size_t i = 0; i < cfg.block_size; i += 2) {
522        memcpy(&buffer[i], "hi", 2);
523    }
524    lfs_file_write(&lfs, &file, buffer, 2*cfg.block_size) => LFS_ERR_NOSPC;
525    lfs_file_close(&lfs, &file) => 0;
526
527    lfs_unmount(&lfs) => 0;
528'''
529
530[[case]] # outdated lookahead test
531define.LFS_BLOCK_SIZE = 512
532define.LFS_BLOCK_COUNT = 1024
533if = 'LFS_BLOCK_SIZE == 512 && LFS_BLOCK_COUNT == 1024'
534code = '''
535    lfs_format(&lfs, &cfg) => 0;
536    lfs_mount(&lfs, &cfg) => 0;
537
538    // fill completely with two files
539    lfs_file_open(&lfs, &file, "exhaustion1",
540            LFS_O_WRONLY | LFS_O_CREAT) => 0;
541    size = strlen("blahblahblahblah");
542    memcpy(buffer, "blahblahblahblah", size);
543    for (lfs_size_t i = 0;
544            i < ((cfg.block_count-2)/2)*(cfg.block_size-8);
545            i += size) {
546        lfs_file_write(&lfs, &file, buffer, size) => size;
547    }
548    lfs_file_close(&lfs, &file) => 0;
549
550    lfs_file_open(&lfs, &file, "exhaustion2",
551            LFS_O_WRONLY | LFS_O_CREAT) => 0;
552    size = strlen("blahblahblahblah");
553    memcpy(buffer, "blahblahblahblah", size);
554    for (lfs_size_t i = 0;
555            i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8);
556            i += size) {
557        lfs_file_write(&lfs, &file, buffer, size) => size;
558    }
559    lfs_file_close(&lfs, &file) => 0;
560
561    // remount to force reset of lookahead
562    lfs_unmount(&lfs) => 0;
563    lfs_mount(&lfs, &cfg) => 0;
564
565    // rewrite one file
566    lfs_file_open(&lfs, &file, "exhaustion1",
567            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
568    lfs_file_sync(&lfs, &file) => 0;
569    size = strlen("blahblahblahblah");
570    memcpy(buffer, "blahblahblahblah", size);
571    for (lfs_size_t i = 0;
572            i < ((cfg.block_count-2)/2)*(cfg.block_size-8);
573            i += size) {
574        lfs_file_write(&lfs, &file, buffer, size) => size;
575    }
576    lfs_file_close(&lfs, &file) => 0;
577
578    // rewrite second file, this requires lookahead does not
579    // use old population
580    lfs_file_open(&lfs, &file, "exhaustion2",
581            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
582    lfs_file_sync(&lfs, &file) => 0;
583    size = strlen("blahblahblahblah");
584    memcpy(buffer, "blahblahblahblah", size);
585    for (lfs_size_t i = 0;
586            i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8);
587            i += size) {
588        lfs_file_write(&lfs, &file, buffer, size) => size;
589    }
590    lfs_file_close(&lfs, &file) => 0;
591
592    lfs_unmount(&lfs) => 0;
593'''
594
595[[case]] # outdated lookahead and split dir test
596define.LFS_BLOCK_SIZE = 512
597define.LFS_BLOCK_COUNT = 1024
598if = 'LFS_BLOCK_SIZE == 512 && LFS_BLOCK_COUNT == 1024'
599code = '''
600    lfs_format(&lfs, &cfg) => 0;
601    lfs_mount(&lfs, &cfg) => 0;
602
603    // fill completely with two files
604    lfs_file_open(&lfs, &file, "exhaustion1",
605            LFS_O_WRONLY | LFS_O_CREAT) => 0;
606    size = strlen("blahblahblahblah");
607    memcpy(buffer, "blahblahblahblah", size);
608    for (lfs_size_t i = 0;
609            i < ((cfg.block_count-2)/2)*(cfg.block_size-8);
610            i += size) {
611        lfs_file_write(&lfs, &file, buffer, size) => size;
612    }
613    lfs_file_close(&lfs, &file) => 0;
614
615    lfs_file_open(&lfs, &file, "exhaustion2",
616            LFS_O_WRONLY | LFS_O_CREAT) => 0;
617    size = strlen("blahblahblahblah");
618    memcpy(buffer, "blahblahblahblah", size);
619    for (lfs_size_t i = 0;
620            i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8);
621            i += size) {
622        lfs_file_write(&lfs, &file, buffer, size) => size;
623    }
624    lfs_file_close(&lfs, &file) => 0;
625
626    // remount to force reset of lookahead
627    lfs_unmount(&lfs) => 0;
628    lfs_mount(&lfs, &cfg) => 0;
629
630    // rewrite one file with a hole of one block
631    lfs_file_open(&lfs, &file, "exhaustion1",
632            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
633    lfs_file_sync(&lfs, &file) => 0;
634    size = strlen("blahblahblahblah");
635    memcpy(buffer, "blahblahblahblah", size);
636    for (lfs_size_t i = 0;
637            i < ((cfg.block_count-2)/2 - 1)*(cfg.block_size-8);
638            i += size) {
639        lfs_file_write(&lfs, &file, buffer, size) => size;
640    }
641    lfs_file_close(&lfs, &file) => 0;
642
643    // try to allocate a directory, should fail!
644    lfs_mkdir(&lfs, "split") => LFS_ERR_NOSPC;
645
646    // file should not fail
647    lfs_file_open(&lfs, &file, "notasplit",
648            LFS_O_WRONLY | LFS_O_CREAT) => 0;
649    lfs_file_write(&lfs, &file, "hi", 2) => 2;
650    lfs_file_close(&lfs, &file) => 0;
651
652    lfs_unmount(&lfs) => 0;
653'''
654