1[cases.test_move_file]
2code = '''
3    lfs_t lfs;
4    lfs_format(&lfs, cfg) => 0;
5    lfs_mount(&lfs, cfg) => 0;
6    lfs_mkdir(&lfs, "a") => 0;
7    lfs_mkdir(&lfs, "b") => 0;
8    lfs_mkdir(&lfs, "c") => 0;
9    lfs_mkdir(&lfs, "d") => 0;
10    lfs_file_t file;
11    lfs_file_open(&lfs, &file, "a/hello", LFS_O_CREAT | LFS_O_WRONLY) => 0;
12    lfs_file_write(&lfs, &file, "hola\n", 5) => 5;
13    lfs_file_write(&lfs, &file, "bonjour\n", 8) => 8;
14    lfs_file_write(&lfs, &file, "ohayo\n", 6) => 6;
15    lfs_file_close(&lfs, &file) => 0;
16    lfs_unmount(&lfs) => 0;
17
18    lfs_mount(&lfs, cfg) => 0;
19    lfs_rename(&lfs, "a/hello", "c/hello") => 0;
20    lfs_unmount(&lfs) => 0;
21
22    lfs_mount(&lfs, cfg) => 0;
23    lfs_dir_t dir;
24    struct lfs_info info;
25    lfs_dir_open(&lfs, &dir, "a") => 0;
26    lfs_dir_read(&lfs, &dir, &info) => 1;
27    assert(strcmp(info.name, ".") == 0);
28    assert(info.type == LFS_TYPE_DIR);
29    lfs_dir_read(&lfs, &dir, &info) => 1;
30    assert(strcmp(info.name, "..") == 0);
31    assert(info.type == LFS_TYPE_DIR);
32    lfs_dir_read(&lfs, &dir, &info) => 0;
33    lfs_dir_close(&lfs, &dir) => 0;
34    lfs_dir_open(&lfs, &dir, "c") => 0;
35    lfs_dir_read(&lfs, &dir, &info) => 1;
36    assert(strcmp(info.name, ".") == 0);
37    assert(info.type == LFS_TYPE_DIR);
38    lfs_dir_read(&lfs, &dir, &info) => 1;
39    assert(strcmp(info.name, "..") == 0);
40    assert(info.type == LFS_TYPE_DIR);
41    lfs_dir_read(&lfs, &dir, &info) => 1;
42    assert(strcmp(info.name, "hello") == 0);
43    assert(info.type == LFS_TYPE_REG);
44    assert(info.size == 5+8+6);
45    lfs_dir_read(&lfs, &dir, &info) => 0;
46    lfs_dir_close(&lfs, &dir) => 0;
47
48    lfs_file_open(&lfs, &file, "a/hello", LFS_O_RDONLY) => LFS_ERR_NOENT;
49    lfs_file_open(&lfs, &file, "b/hello", LFS_O_RDONLY) => LFS_ERR_NOENT;
50    lfs_file_open(&lfs, &file, "c/hello", LFS_O_RDONLY) => 0;
51    uint8_t buffer[1024];
52    lfs_file_read(&lfs, &file, buffer, 5) => 5;
53    memcmp(buffer, "hola\n", 5) => 0;
54    lfs_file_read(&lfs, &file, buffer, 8) => 8;
55    memcmp(buffer, "bonjour\n", 8) => 0;
56    lfs_file_read(&lfs, &file, buffer, 6) => 6;
57    memcmp(buffer, "ohayo\n", 6) => 0;
58    lfs_file_close(&lfs, &file) => 0;
59    lfs_file_open(&lfs, &file, "d/hello", LFS_O_RDONLY) => LFS_ERR_NOENT;
60    lfs_unmount(&lfs) => 0;
61'''
62
63[cases.test_move_nop] # yes this is legal
64code = '''
65    lfs_t lfs;
66    lfs_format(&lfs, cfg) => 0;
67    lfs_mount(&lfs, cfg) => 0;
68    lfs_mkdir(&lfs, "hi") => 0;
69    lfs_rename(&lfs, "hi", "hi") => 0;
70    lfs_mkdir(&lfs, "hi/hi") => 0;
71    lfs_rename(&lfs, "hi/hi", "hi/hi") => 0;
72    lfs_mkdir(&lfs, "hi/hi/hi") => 0;
73    lfs_rename(&lfs, "hi/hi/hi", "hi/hi/hi") => 0;
74    struct lfs_info info;
75    lfs_stat(&lfs, "hi/hi/hi", &info) => 0;
76    assert(strcmp(info.name, "hi") == 0);
77    assert(info.type == LFS_TYPE_DIR);
78    lfs_unmount(&lfs) => 0;
79'''
80
81[cases.test_move_file_corrupt_source]
82in = "lfs.c"
83code = '''
84    lfs_t lfs;
85    lfs_format(&lfs, cfg) => 0;
86    lfs_mount(&lfs, cfg) => 0;
87    lfs_mkdir(&lfs, "a") => 0;
88    lfs_mkdir(&lfs, "b") => 0;
89    lfs_mkdir(&lfs, "c") => 0;
90    lfs_mkdir(&lfs, "d") => 0;
91    lfs_file_t file;
92    lfs_file_open(&lfs, &file, "a/hello", LFS_O_CREAT | LFS_O_WRONLY) => 0;
93    lfs_file_write(&lfs, &file, "hola\n", 5) => 5;
94    lfs_file_write(&lfs, &file, "bonjour\n", 8) => 8;
95    lfs_file_write(&lfs, &file, "ohayo\n", 6) => 6;
96    lfs_file_close(&lfs, &file) => 0;
97    lfs_unmount(&lfs) => 0;
98
99    lfs_mount(&lfs, cfg) => 0;
100    lfs_rename(&lfs, "a/hello", "c/hello") => 0;
101    lfs_unmount(&lfs) => 0;
102
103    // corrupt the source
104    lfs_mount(&lfs, cfg) => 0;
105    lfs_dir_t dir;
106    struct lfs_info info;
107    lfs_dir_open(&lfs, &dir, "a") => 0;
108    lfs_block_t block = dir.m.pair[0];
109    lfs_dir_close(&lfs, &dir) => 0;
110    lfs_unmount(&lfs) => 0;
111    uint8_t buffer[BLOCK_SIZE];
112    cfg->read(cfg, block, 0, buffer, BLOCK_SIZE) => 0;
113    int off = BLOCK_SIZE-1;
114    while (off >= 0 && buffer[off] == ERASE_VALUE) {
115        off -= 1;
116    }
117    memset(&buffer[off-3], BLOCK_SIZE, 3);
118    cfg->erase(cfg, block) => 0;
119    cfg->prog(cfg, block, 0, buffer, BLOCK_SIZE) => 0;
120    cfg->sync(cfg) => 0;
121
122    lfs_mount(&lfs, cfg) => 0;
123    lfs_dir_open(&lfs, &dir, "a") => 0;
124    lfs_dir_read(&lfs, &dir, &info) => 1;
125    assert(strcmp(info.name, ".") == 0);
126    assert(info.type == LFS_TYPE_DIR);
127    lfs_dir_read(&lfs, &dir, &info) => 1;
128    assert(strcmp(info.name, "..") == 0);
129    assert(info.type == LFS_TYPE_DIR);
130    lfs_dir_read(&lfs, &dir, &info) => 0;
131    lfs_dir_close(&lfs, &dir) => 0;
132    lfs_dir_open(&lfs, &dir, "c") => 0;
133    lfs_dir_read(&lfs, &dir, &info) => 1;
134    assert(strcmp(info.name, ".") == 0);
135    assert(info.type == LFS_TYPE_DIR);
136    lfs_dir_read(&lfs, &dir, &info) => 1;
137    assert(strcmp(info.name, "..") == 0);
138    assert(info.type == LFS_TYPE_DIR);
139    lfs_dir_read(&lfs, &dir, &info) => 1;
140    assert(strcmp(info.name, "hello") == 0);
141    assert(info.type == LFS_TYPE_REG);
142    assert(info.size == 5+8+6);
143    lfs_dir_read(&lfs, &dir, &info) => 0;
144    lfs_dir_close(&lfs, &dir) => 0;
145
146    lfs_file_open(&lfs, &file, "a/hello", LFS_O_RDONLY) => LFS_ERR_NOENT;
147    lfs_file_open(&lfs, &file, "b/hello", LFS_O_RDONLY) => LFS_ERR_NOENT;
148    lfs_file_open(&lfs, &file, "c/hello", LFS_O_RDONLY) => 0;
149    lfs_file_read(&lfs, &file, buffer, 5) => 5;
150    memcmp(buffer, "hola\n", 5) => 0;
151    lfs_file_read(&lfs, &file, buffer, 8) => 8;
152    memcmp(buffer, "bonjour\n", 8) => 0;
153    lfs_file_read(&lfs, &file, buffer, 6) => 6;
154    memcmp(buffer, "ohayo\n", 6) => 0;
155    lfs_file_close(&lfs, &file) => 0;
156    lfs_file_open(&lfs, &file, "d/hello", LFS_O_RDONLY) => LFS_ERR_NOENT;
157    lfs_unmount(&lfs) => 0;
158'''
159
160# move file corrupt source and dest
161[cases.test_move_file_corrupt_source_dest]
162in = "lfs.c"
163if = 'PROG_SIZE <= 0x3fe' # only works with one crc per commit
164code = '''
165    lfs_t lfs;
166    lfs_format(&lfs, cfg) => 0;
167    lfs_mount(&lfs, cfg) => 0;
168    lfs_mkdir(&lfs, "a") => 0;
169    lfs_mkdir(&lfs, "b") => 0;
170    lfs_mkdir(&lfs, "c") => 0;
171    lfs_mkdir(&lfs, "d") => 0;
172    lfs_file_t file;
173    lfs_file_open(&lfs, &file, "a/hello", LFS_O_CREAT | LFS_O_WRONLY) => 0;
174    lfs_file_write(&lfs, &file, "hola\n", 5) => 5;
175    lfs_file_write(&lfs, &file, "bonjour\n", 8) => 8;
176    lfs_file_write(&lfs, &file, "ohayo\n", 6) => 6;
177    lfs_file_close(&lfs, &file) => 0;
178    lfs_unmount(&lfs) => 0;
179
180    lfs_mount(&lfs, cfg) => 0;
181    lfs_rename(&lfs, "a/hello", "c/hello") => 0;
182    lfs_unmount(&lfs) => 0;
183
184    // corrupt the source
185    lfs_mount(&lfs, cfg) => 0;
186    lfs_dir_t dir;
187    struct lfs_info info;
188    lfs_dir_open(&lfs, &dir, "a") => 0;
189    lfs_block_t block = dir.m.pair[0];
190    lfs_dir_close(&lfs, &dir) => 0;
191    lfs_unmount(&lfs) => 0;
192    uint8_t buffer[BLOCK_SIZE];
193    cfg->read(cfg, block, 0, buffer, BLOCK_SIZE) => 0;
194    int off = BLOCK_SIZE-1;
195    while (off >= 0 && buffer[off] == ERASE_VALUE) {
196        off -= 1;
197    }
198    memset(&buffer[off-3], BLOCK_SIZE, 3);
199    cfg->erase(cfg, block) => 0;
200    cfg->prog(cfg, block, 0, buffer, BLOCK_SIZE) => 0;
201    cfg->sync(cfg) => 0;
202
203    // corrupt the destination
204    lfs_mount(&lfs, cfg) => 0;
205    lfs_dir_open(&lfs, &dir, "c") => 0;
206    block = dir.m.pair[0];
207    lfs_dir_close(&lfs, &dir) => 0;
208    lfs_unmount(&lfs) => 0;
209    cfg->read(cfg, block, 0, buffer, BLOCK_SIZE) => 0;
210    off = BLOCK_SIZE-1;
211    while (off >= 0 && buffer[off] == ERASE_VALUE) {
212        off -= 1;
213    }
214    memset(&buffer[off-3], BLOCK_SIZE, 3);
215    cfg->erase(cfg, block) => 0;
216    cfg->prog(cfg, block, 0, buffer, BLOCK_SIZE) => 0;
217    cfg->sync(cfg) => 0;
218
219    lfs_mount(&lfs, cfg) => 0;
220    lfs_dir_open(&lfs, &dir, "a") => 0;
221    lfs_dir_read(&lfs, &dir, &info) => 1;
222    assert(strcmp(info.name, ".") == 0);
223    assert(info.type == LFS_TYPE_DIR);
224    lfs_dir_read(&lfs, &dir, &info) => 1;
225    assert(strcmp(info.name, "..") == 0);
226    assert(info.type == LFS_TYPE_DIR);
227    lfs_dir_read(&lfs, &dir, &info) => 1;
228    assert(strcmp(info.name, "hello") == 0);
229    assert(info.type == LFS_TYPE_REG);
230    assert(info.size == 5+8+6);
231    lfs_dir_read(&lfs, &dir, &info) => 0;
232    lfs_dir_close(&lfs, &dir) => 0;
233    lfs_dir_open(&lfs, &dir, "c") => 0;
234    lfs_dir_read(&lfs, &dir, &info) => 1;
235    assert(strcmp(info.name, ".") == 0);
236    assert(info.type == LFS_TYPE_DIR);
237    lfs_dir_read(&lfs, &dir, &info) => 1;
238    assert(strcmp(info.name, "..") == 0);
239    assert(info.type == LFS_TYPE_DIR);
240    lfs_dir_read(&lfs, &dir, &info) => 0;
241    lfs_dir_close(&lfs, &dir) => 0;
242
243    lfs_file_open(&lfs, &file, "a/hello", LFS_O_RDONLY) => 0;
244    lfs_file_read(&lfs, &file, buffer, 5) => 5;
245    memcmp(buffer, "hola\n", 5) => 0;
246    lfs_file_read(&lfs, &file, buffer, 8) => 8;
247    memcmp(buffer, "bonjour\n", 8) => 0;
248    lfs_file_read(&lfs, &file, buffer, 6) => 6;
249    memcmp(buffer, "ohayo\n", 6) => 0;
250    lfs_file_close(&lfs, &file) => 0;
251    lfs_file_open(&lfs, &file, "b/hello", LFS_O_RDONLY) => LFS_ERR_NOENT;
252    lfs_file_open(&lfs, &file, "c/hello", LFS_O_RDONLY) => LFS_ERR_NOENT;
253    lfs_file_open(&lfs, &file, "d/hello", LFS_O_RDONLY) => LFS_ERR_NOENT;
254    lfs_unmount(&lfs) => 0;
255'''
256
257[cases.test_move_file_after_corrupt]
258in = "lfs.c"
259if = 'PROG_SIZE <= 0x3fe' # only works with one crc per commit
260code = '''
261    lfs_t lfs;
262    lfs_format(&lfs, cfg) => 0;
263    lfs_mount(&lfs, cfg) => 0;
264    lfs_mkdir(&lfs, "a") => 0;
265    lfs_mkdir(&lfs, "b") => 0;
266    lfs_mkdir(&lfs, "c") => 0;
267    lfs_mkdir(&lfs, "d") => 0;
268    lfs_file_t file;
269    lfs_file_open(&lfs, &file, "a/hello", LFS_O_CREAT | LFS_O_WRONLY) => 0;
270    lfs_file_write(&lfs, &file, "hola\n", 5) => 5;
271    lfs_file_write(&lfs, &file, "bonjour\n", 8) => 8;
272    lfs_file_write(&lfs, &file, "ohayo\n", 6) => 6;
273    lfs_file_close(&lfs, &file) => 0;
274    lfs_unmount(&lfs) => 0;
275
276    lfs_mount(&lfs, cfg) => 0;
277    lfs_rename(&lfs, "a/hello", "c/hello") => 0;
278    lfs_unmount(&lfs) => 0;
279
280    // corrupt the source
281    lfs_mount(&lfs, cfg) => 0;
282    lfs_dir_t dir;
283    struct lfs_info info;
284    lfs_dir_open(&lfs, &dir, "a") => 0;
285    lfs_block_t block = dir.m.pair[0];
286    lfs_dir_close(&lfs, &dir) => 0;
287    lfs_unmount(&lfs) => 0;
288    uint8_t buffer[BLOCK_SIZE];
289    cfg->read(cfg, block, 0, buffer, BLOCK_SIZE) => 0;
290    int off = BLOCK_SIZE-1;
291    while (off >= 0 && buffer[off] == ERASE_VALUE) {
292        off -= 1;
293    }
294    memset(&buffer[off-3], BLOCK_SIZE, 3);
295    cfg->erase(cfg, block) => 0;
296    cfg->prog(cfg, block, 0, buffer, BLOCK_SIZE) => 0;
297    cfg->sync(cfg) => 0;
298
299    // corrupt the destination
300    lfs_mount(&lfs, cfg) => 0;
301    lfs_dir_open(&lfs, &dir, "c") => 0;
302    block = dir.m.pair[0];
303    lfs_dir_close(&lfs, &dir) => 0;
304    lfs_unmount(&lfs) => 0;
305    cfg->read(cfg, block, 0, buffer, BLOCK_SIZE) => 0;
306    off = BLOCK_SIZE-1;
307    while (off >= 0 && buffer[off] == ERASE_VALUE) {
308        off -= 1;
309    }
310    memset(&buffer[off-3], BLOCK_SIZE, 3);
311    cfg->erase(cfg, block) => 0;
312    cfg->prog(cfg, block, 0, buffer, BLOCK_SIZE) => 0;
313    cfg->sync(cfg) => 0;
314
315    // continue move
316    lfs_mount(&lfs, cfg) => 0;
317    lfs_rename(&lfs, "a/hello", "c/hello") => 0;
318    lfs_unmount(&lfs) => 0;
319
320    lfs_mount(&lfs, cfg) => 0;
321    lfs_dir_open(&lfs, &dir, "a") => 0;
322    lfs_dir_read(&lfs, &dir, &info) => 1;
323    assert(strcmp(info.name, ".") == 0);
324    assert(info.type == LFS_TYPE_DIR);
325    lfs_dir_read(&lfs, &dir, &info) => 1;
326    assert(strcmp(info.name, "..") == 0);
327    assert(info.type == LFS_TYPE_DIR);
328    lfs_dir_read(&lfs, &dir, &info) => 0;
329    lfs_dir_close(&lfs, &dir) => 0;
330    lfs_dir_open(&lfs, &dir, "c") => 0;
331    lfs_dir_read(&lfs, &dir, &info) => 1;
332    assert(strcmp(info.name, ".") == 0);
333    assert(info.type == LFS_TYPE_DIR);
334    lfs_dir_read(&lfs, &dir, &info) => 1;
335    assert(strcmp(info.name, "..") == 0);
336    assert(info.type == LFS_TYPE_DIR);
337    lfs_dir_read(&lfs, &dir, &info) => 1;
338    assert(strcmp(info.name, "hello") == 0);
339    assert(info.type == LFS_TYPE_REG);
340    assert(info.size == 5+8+6);
341    lfs_dir_read(&lfs, &dir, &info) => 0;
342    lfs_dir_close(&lfs, &dir) => 0;
343
344    lfs_file_open(&lfs, &file, "a/hello", LFS_O_RDONLY) => LFS_ERR_NOENT;
345    lfs_file_open(&lfs, &file, "b/hello", LFS_O_RDONLY) => LFS_ERR_NOENT;
346    lfs_file_open(&lfs, &file, "c/hello", LFS_O_RDONLY) => 0;
347    lfs_file_read(&lfs, &file, buffer, 5) => 5;
348    memcmp(buffer, "hola\n", 5) => 0;
349    lfs_file_read(&lfs, &file, buffer, 8) => 8;
350    memcmp(buffer, "bonjour\n", 8) => 0;
351    lfs_file_read(&lfs, &file, buffer, 6) => 6;
352    memcmp(buffer, "ohayo\n", 6) => 0;
353    lfs_file_close(&lfs, &file) => 0;
354    lfs_file_open(&lfs, &file, "d/hello", LFS_O_RDONLY) => LFS_ERR_NOENT;
355    lfs_unmount(&lfs) => 0;
356'''
357
358[cases.test_move_reentrant_file]
359reentrant = true
360code = '''
361    lfs_t lfs;
362    int err = lfs_mount(&lfs, cfg);
363    if (err) {
364        lfs_format(&lfs, cfg) => 0;
365        lfs_mount(&lfs, cfg) => 0;
366    }
367    err = lfs_mkdir(&lfs, "a");
368    assert(!err || err == LFS_ERR_EXIST);
369    err = lfs_mkdir(&lfs, "b");
370    assert(!err || err == LFS_ERR_EXIST);
371    err = lfs_mkdir(&lfs, "c");
372    assert(!err || err == LFS_ERR_EXIST);
373    err = lfs_mkdir(&lfs, "d");
374    assert(!err || err == LFS_ERR_EXIST);
375    lfs_unmount(&lfs) => 0;
376
377    while (true) {
378        lfs_mount(&lfs, cfg) => 0;
379        // there should never exist _2_ hello files
380        int count = 0;
381        struct lfs_info info;
382        if (lfs_stat(&lfs, "a/hello", &info) == 0) {
383            assert(strcmp(info.name, "hello") == 0);
384            assert(info.type == LFS_TYPE_REG);
385            assert(info.size == 5+8+6 || info.size == 0);
386            count += 1;
387        }
388        if (lfs_stat(&lfs, "b/hello", &info) == 0) {
389            assert(strcmp(info.name, "hello") == 0);
390            assert(info.type == LFS_TYPE_REG);
391            assert(info.size == 5+8+6);
392            count += 1;
393        }
394        if (lfs_stat(&lfs, "c/hello", &info) == 0) {
395            assert(strcmp(info.name, "hello") == 0);
396            assert(info.type == LFS_TYPE_REG);
397            assert(info.size == 5+8+6);
398            count += 1;
399        }
400        if (lfs_stat(&lfs, "d/hello", &info) == 0) {
401            assert(strcmp(info.name, "hello") == 0);
402            assert(info.type == LFS_TYPE_REG);
403            assert(info.size == 5+8+6);
404            count += 1;
405        }
406        assert(count <= 1);
407        lfs_unmount(&lfs) => 0;
408
409        lfs_mount(&lfs, cfg) => 0;
410        if (lfs_stat(&lfs, "a/hello", &info) == 0 && info.size > 0) {
411            lfs_rename(&lfs, "a/hello", "b/hello") => 0;
412        } else if (lfs_stat(&lfs, "b/hello", &info) == 0) {
413            lfs_rename(&lfs, "b/hello", "c/hello") => 0;
414        } else if (lfs_stat(&lfs, "c/hello", &info) == 0) {
415            lfs_rename(&lfs, "c/hello", "d/hello") => 0;
416        } else if (lfs_stat(&lfs, "d/hello", &info) == 0) {
417            // success
418            lfs_unmount(&lfs) => 0;
419            break;
420        } else {
421            // create file
422            lfs_file_t file;
423            lfs_file_open(&lfs, &file, "a/hello",
424                    LFS_O_WRONLY | LFS_O_CREAT) => 0;
425            lfs_file_write(&lfs, &file, "hola\n", 5) => 5;
426            lfs_file_write(&lfs, &file, "bonjour\n", 8) => 8;
427            lfs_file_write(&lfs, &file, "ohayo\n", 6) => 6;
428            lfs_file_close(&lfs, &file) => 0;
429        }
430        lfs_unmount(&lfs) => 0;
431    }
432
433    lfs_mount(&lfs, cfg) => 0;
434    lfs_dir_t dir;
435    struct lfs_info info;
436    lfs_dir_open(&lfs, &dir, "a") => 0;
437    lfs_dir_read(&lfs, &dir, &info) => 1;
438    assert(strcmp(info.name, ".") == 0);
439    assert(info.type == LFS_TYPE_DIR);
440    lfs_dir_read(&lfs, &dir, &info) => 1;
441    assert(strcmp(info.name, "..") == 0);
442    assert(info.type == LFS_TYPE_DIR);
443    lfs_dir_read(&lfs, &dir, &info) => 0;
444    lfs_dir_close(&lfs, &dir) => 0;
445    lfs_dir_open(&lfs, &dir, "d") => 0;
446    lfs_dir_read(&lfs, &dir, &info) => 1;
447    assert(strcmp(info.name, ".") == 0);
448    assert(info.type == LFS_TYPE_DIR);
449    lfs_dir_read(&lfs, &dir, &info) => 1;
450    assert(strcmp(info.name, "..") == 0);
451    assert(info.type == LFS_TYPE_DIR);
452    lfs_dir_read(&lfs, &dir, &info) => 1;
453    assert(strcmp(info.name, "hello") == 0);
454    assert(info.type == LFS_TYPE_REG);
455    assert(info.size == 5+8+6);
456    lfs_dir_read(&lfs, &dir, &info) => 0;
457    lfs_dir_close(&lfs, &dir) => 0;
458
459    lfs_file_t file;
460    lfs_file_open(&lfs, &file, "a/hello", LFS_O_RDONLY) => LFS_ERR_NOENT;
461    lfs_file_open(&lfs, &file, "b/hello", LFS_O_RDONLY) => LFS_ERR_NOENT;
462    lfs_file_open(&lfs, &file, "c/hello", LFS_O_RDONLY) => LFS_ERR_NOENT;
463    lfs_file_open(&lfs, &file, "d/hello", LFS_O_RDONLY) => 0;
464    uint8_t buffer[1024];
465    lfs_file_read(&lfs, &file, buffer, 5) => 5;
466    memcmp(buffer, "hola\n", 5) => 0;
467    lfs_file_read(&lfs, &file, buffer, 8) => 8;
468    memcmp(buffer, "bonjour\n", 8) => 0;
469    lfs_file_read(&lfs, &file, buffer, 6) => 6;
470    memcmp(buffer, "ohayo\n", 6) => 0;
471    lfs_file_close(&lfs, &file) => 0;
472    lfs_unmount(&lfs) => 0;
473'''
474
475[cases.test_move_dir]
476code = '''
477    lfs_t lfs;
478    lfs_format(&lfs, cfg) => 0;
479    lfs_mount(&lfs, cfg) => 0;
480    lfs_mkdir(&lfs, "a") => 0;
481    lfs_mkdir(&lfs, "b") => 0;
482    lfs_mkdir(&lfs, "c") => 0;
483    lfs_mkdir(&lfs, "d") => 0;
484    lfs_mkdir(&lfs, "a/hi") => 0;
485    lfs_mkdir(&lfs, "a/hi/hola") => 0;
486    lfs_mkdir(&lfs, "a/hi/bonjour") => 0;
487    lfs_mkdir(&lfs, "a/hi/ohayo") => 0;
488    lfs_unmount(&lfs) => 0;
489
490    lfs_mount(&lfs, cfg) => 0;
491    lfs_rename(&lfs, "a/hi", "c/hi") => 0;
492    lfs_unmount(&lfs) => 0;
493
494    lfs_mount(&lfs, cfg) => 0;
495    lfs_dir_t dir;
496    struct lfs_info info;
497    lfs_dir_open(&lfs, &dir, "a") => 0;
498    lfs_dir_read(&lfs, &dir, &info) => 1;
499    assert(strcmp(info.name, ".") == 0);
500    assert(info.type == LFS_TYPE_DIR);
501    lfs_dir_read(&lfs, &dir, &info) => 1;
502    assert(strcmp(info.name, "..") == 0);
503    assert(info.type == LFS_TYPE_DIR);
504    lfs_dir_read(&lfs, &dir, &info) => 0;
505    lfs_dir_close(&lfs, &dir) => 0;
506    lfs_dir_open(&lfs, &dir, "c") => 0;
507    lfs_dir_read(&lfs, &dir, &info) => 1;
508    assert(strcmp(info.name, ".") == 0);
509    assert(info.type == LFS_TYPE_DIR);
510    lfs_dir_read(&lfs, &dir, &info) => 1;
511    assert(strcmp(info.name, "..") == 0);
512    assert(info.type == LFS_TYPE_DIR);
513    lfs_dir_read(&lfs, &dir, &info) => 1;
514    assert(strcmp(info.name, "hi") == 0);
515    assert(info.type == LFS_TYPE_DIR);
516    lfs_dir_read(&lfs, &dir, &info) => 0;
517    lfs_dir_close(&lfs, &dir) => 0;
518
519    lfs_dir_open(&lfs, &dir, "a/hi") => LFS_ERR_NOENT;
520    lfs_dir_open(&lfs, &dir, "b/hi") => LFS_ERR_NOENT;
521    lfs_dir_open(&lfs, &dir, "c/hi") => 0;
522    lfs_dir_read(&lfs, &dir, &info) => 1;
523    assert(strcmp(info.name, ".") == 0);
524    assert(info.type == LFS_TYPE_DIR);
525    lfs_dir_read(&lfs, &dir, &info) => 1;
526    assert(strcmp(info.name, "..") == 0);
527    assert(info.type == LFS_TYPE_DIR);
528    lfs_dir_read(&lfs, &dir, &info) => 1;
529    assert(strcmp(info.name, "bonjour") == 0);
530    assert(info.type == LFS_TYPE_DIR);
531    lfs_dir_read(&lfs, &dir, &info) => 1;
532    assert(strcmp(info.name, "hola") == 0);
533    assert(info.type == LFS_TYPE_DIR);
534    lfs_dir_read(&lfs, &dir, &info) => 1;
535    assert(strcmp(info.name, "ohayo") == 0);
536    assert(info.type == LFS_TYPE_DIR);
537    lfs_dir_read(&lfs, &dir, &info) => 0;
538    lfs_dir_close(&lfs, &dir) => 0;
539    lfs_dir_open(&lfs, &dir, "d/hi") => LFS_ERR_NOENT;
540    lfs_unmount(&lfs) => 0;
541'''
542
543[cases.test_move_dir_corrupt_source]
544in = "lfs.c"
545code = '''
546    lfs_t lfs;
547    lfs_format(&lfs, cfg) => 0;
548    lfs_mount(&lfs, cfg) => 0;
549    lfs_mkdir(&lfs, "a") => 0;
550    lfs_mkdir(&lfs, "b") => 0;
551    lfs_mkdir(&lfs, "c") => 0;
552    lfs_mkdir(&lfs, "d") => 0;
553    lfs_mkdir(&lfs, "a/hi") => 0;
554    lfs_mkdir(&lfs, "a/hi/hola") => 0;
555    lfs_mkdir(&lfs, "a/hi/bonjour") => 0;
556    lfs_mkdir(&lfs, "a/hi/ohayo") => 0;
557    lfs_unmount(&lfs) => 0;
558
559    lfs_mount(&lfs, cfg) => 0;
560    lfs_rename(&lfs, "a/hi", "c/hi") => 0;
561    lfs_unmount(&lfs) => 0;
562
563    // corrupt the source
564    lfs_mount(&lfs, cfg) => 0;
565    lfs_dir_t dir;
566    struct lfs_info info;
567    lfs_dir_open(&lfs, &dir, "a") => 0;
568    lfs_block_t block = dir.m.pair[0];
569    lfs_dir_close(&lfs, &dir) => 0;
570    lfs_unmount(&lfs) => 0;
571    uint8_t buffer[BLOCK_SIZE];
572    cfg->read(cfg, block, 0, buffer, BLOCK_SIZE) => 0;
573    int off = BLOCK_SIZE-1;
574    while (off >= 0 && buffer[off] == ERASE_VALUE) {
575        off -= 1;
576    }
577    memset(&buffer[off-3], BLOCK_SIZE, 3);
578    cfg->erase(cfg, block) => 0;
579    cfg->prog(cfg, block, 0, buffer, BLOCK_SIZE) => 0;
580    cfg->sync(cfg) => 0;
581
582    lfs_mount(&lfs, cfg) => 0;
583    lfs_dir_open(&lfs, &dir, "a") => 0;
584    lfs_dir_read(&lfs, &dir, &info) => 1;
585    assert(strcmp(info.name, ".") == 0);
586    assert(info.type == LFS_TYPE_DIR);
587    lfs_dir_read(&lfs, &dir, &info) => 1;
588    assert(strcmp(info.name, "..") == 0);
589    assert(info.type == LFS_TYPE_DIR);
590    lfs_dir_read(&lfs, &dir, &info) => 0;
591    lfs_dir_close(&lfs, &dir) => 0;
592    lfs_dir_open(&lfs, &dir, "c") => 0;
593    lfs_dir_read(&lfs, &dir, &info) => 1;
594    assert(strcmp(info.name, ".") == 0);
595    assert(info.type == LFS_TYPE_DIR);
596    lfs_dir_read(&lfs, &dir, &info) => 1;
597    assert(strcmp(info.name, "..") == 0);
598    assert(info.type == LFS_TYPE_DIR);
599    lfs_dir_read(&lfs, &dir, &info) => 1;
600    assert(strcmp(info.name, "hi") == 0);
601    assert(info.type == LFS_TYPE_DIR);
602    lfs_dir_read(&lfs, &dir, &info) => 0;
603    lfs_dir_close(&lfs, &dir) => 0;
604
605    lfs_dir_open(&lfs, &dir, "a/hi") => LFS_ERR_NOENT;
606    lfs_dir_open(&lfs, &dir, "b/hi") => LFS_ERR_NOENT;
607    lfs_dir_open(&lfs, &dir, "c/hi") => 0;
608    lfs_dir_read(&lfs, &dir, &info) => 1;
609    assert(strcmp(info.name, ".") == 0);
610    assert(info.type == LFS_TYPE_DIR);
611    lfs_dir_read(&lfs, &dir, &info) => 1;
612    assert(strcmp(info.name, "..") == 0);
613    assert(info.type == LFS_TYPE_DIR);
614    lfs_dir_read(&lfs, &dir, &info) => 1;
615    assert(strcmp(info.name, "bonjour") == 0);
616    assert(info.type == LFS_TYPE_DIR);
617    lfs_dir_read(&lfs, &dir, &info) => 1;
618    assert(strcmp(info.name, "hola") == 0);
619    assert(info.type == LFS_TYPE_DIR);
620    lfs_dir_read(&lfs, &dir, &info) => 1;
621    assert(strcmp(info.name, "ohayo") == 0);
622    assert(info.type == LFS_TYPE_DIR);
623    lfs_dir_read(&lfs, &dir, &info) => 0;
624    lfs_dir_close(&lfs, &dir) => 0;
625    lfs_dir_open(&lfs, &dir, "d/hi") => LFS_ERR_NOENT;
626    lfs_unmount(&lfs) => 0;
627'''
628
629[cases.test_move_dir_corrupt_source_dest]
630in = "lfs.c"
631if = 'PROG_SIZE <= 0x3fe' # only works with one crc per commit
632code = '''
633    lfs_t lfs;
634    lfs_format(&lfs, cfg) => 0;
635    lfs_mount(&lfs, cfg) => 0;
636    lfs_mkdir(&lfs, "a") => 0;
637    lfs_mkdir(&lfs, "b") => 0;
638    lfs_mkdir(&lfs, "c") => 0;
639    lfs_mkdir(&lfs, "d") => 0;
640    lfs_mkdir(&lfs, "a/hi") => 0;
641    lfs_mkdir(&lfs, "a/hi/hola") => 0;
642    lfs_mkdir(&lfs, "a/hi/bonjour") => 0;
643    lfs_mkdir(&lfs, "a/hi/ohayo") => 0;
644    lfs_unmount(&lfs) => 0;
645
646    lfs_mount(&lfs, cfg) => 0;
647    lfs_rename(&lfs, "a/hi", "c/hi") => 0;
648    lfs_unmount(&lfs) => 0;
649
650    // corrupt the source
651    lfs_mount(&lfs, cfg) => 0;
652    lfs_dir_t dir;
653    struct lfs_info info;
654    lfs_dir_open(&lfs, &dir, "a") => 0;
655    lfs_block_t block = dir.m.pair[0];
656    lfs_dir_close(&lfs, &dir) => 0;
657    lfs_unmount(&lfs) => 0;
658    uint8_t buffer[BLOCK_SIZE];
659    cfg->read(cfg, block, 0, buffer, BLOCK_SIZE) => 0;
660    int off = BLOCK_SIZE-1;
661    while (off >= 0 && buffer[off] == ERASE_VALUE) {
662        off -= 1;
663    }
664    memset(&buffer[off-3], BLOCK_SIZE, 3);
665    cfg->erase(cfg, block) => 0;
666    cfg->prog(cfg, block, 0, buffer, BLOCK_SIZE) => 0;
667    cfg->sync(cfg) => 0;
668
669    // corrupt the destination
670    lfs_mount(&lfs, cfg) => 0;
671    lfs_dir_open(&lfs, &dir, "c") => 0;
672    block = dir.m.pair[0];
673    lfs_dir_close(&lfs, &dir) => 0;
674    lfs_unmount(&lfs) => 0;
675    cfg->read(cfg, block, 0, buffer, BLOCK_SIZE) => 0;
676    off = BLOCK_SIZE-1;
677    while (off >= 0 && buffer[off] == ERASE_VALUE) {
678        off -= 1;
679    }
680    memset(&buffer[off-3], BLOCK_SIZE, 3);
681    cfg->erase(cfg, block) => 0;
682    cfg->prog(cfg, block, 0, buffer, BLOCK_SIZE) => 0;
683    cfg->sync(cfg) => 0;
684
685    lfs_mount(&lfs, cfg) => 0;
686    lfs_dir_open(&lfs, &dir, "a") => 0;
687    lfs_dir_read(&lfs, &dir, &info) => 1;
688    assert(strcmp(info.name, ".") == 0);
689    assert(info.type == LFS_TYPE_DIR);
690    lfs_dir_read(&lfs, &dir, &info) => 1;
691    assert(strcmp(info.name, "..") == 0);
692    assert(info.type == LFS_TYPE_DIR);
693    lfs_dir_read(&lfs, &dir, &info) => 1;
694    assert(strcmp(info.name, "hi") == 0);
695    assert(info.type == LFS_TYPE_DIR);
696    lfs_dir_read(&lfs, &dir, &info) => 0;
697    lfs_dir_close(&lfs, &dir) => 0;
698    lfs_dir_open(&lfs, &dir, "c") => 0;
699    lfs_dir_read(&lfs, &dir, &info) => 1;
700    assert(strcmp(info.name, ".") == 0);
701    assert(info.type == LFS_TYPE_DIR);
702    lfs_dir_read(&lfs, &dir, &info) => 1;
703    assert(strcmp(info.name, "..") == 0);
704    assert(info.type == LFS_TYPE_DIR);
705    lfs_dir_read(&lfs, &dir, &info) => 0;
706    lfs_dir_close(&lfs, &dir) => 0;
707
708    lfs_dir_open(&lfs, &dir, "a/hi") => 0;
709    lfs_dir_read(&lfs, &dir, &info) => 1;
710    assert(strcmp(info.name, ".") == 0);
711    assert(info.type == LFS_TYPE_DIR);
712    lfs_dir_read(&lfs, &dir, &info) => 1;
713    assert(strcmp(info.name, "..") == 0);
714    assert(info.type == LFS_TYPE_DIR);
715    lfs_dir_read(&lfs, &dir, &info) => 1;
716    assert(strcmp(info.name, "bonjour") == 0);
717    assert(info.type == LFS_TYPE_DIR);
718    lfs_dir_read(&lfs, &dir, &info) => 1;
719    assert(strcmp(info.name, "hola") == 0);
720    assert(info.type == LFS_TYPE_DIR);
721    lfs_dir_read(&lfs, &dir, &info) => 1;
722    assert(strcmp(info.name, "ohayo") == 0);
723    assert(info.type == LFS_TYPE_DIR);
724    lfs_dir_read(&lfs, &dir, &info) => 0;
725    lfs_dir_close(&lfs, &dir) => 0;
726    lfs_dir_open(&lfs, &dir, "b/hi") => LFS_ERR_NOENT;
727    lfs_dir_open(&lfs, &dir, "c/hi") => LFS_ERR_NOENT;
728    lfs_dir_open(&lfs, &dir, "d/hi") => LFS_ERR_NOENT;
729    lfs_unmount(&lfs) => 0;
730'''
731
732[cases.test_move_dir_after_corrupt]
733in = "lfs.c"
734if = 'PROG_SIZE <= 0x3fe' # only works with one crc per commit
735code = '''
736    lfs_t lfs;
737    lfs_format(&lfs, cfg) => 0;
738    lfs_mount(&lfs, cfg) => 0;
739    lfs_mkdir(&lfs, "a") => 0;
740    lfs_mkdir(&lfs, "b") => 0;
741    lfs_mkdir(&lfs, "c") => 0;
742    lfs_mkdir(&lfs, "d") => 0;
743    lfs_mkdir(&lfs, "a/hi") => 0;
744    lfs_mkdir(&lfs, "a/hi/hola") => 0;
745    lfs_mkdir(&lfs, "a/hi/bonjour") => 0;
746    lfs_mkdir(&lfs, "a/hi/ohayo") => 0;
747    lfs_unmount(&lfs) => 0;
748
749    lfs_mount(&lfs, cfg) => 0;
750    lfs_rename(&lfs, "a/hi", "c/hi") => 0;
751    lfs_unmount(&lfs) => 0;
752
753    // corrupt the source
754    lfs_mount(&lfs, cfg) => 0;
755    lfs_dir_t dir;
756    struct lfs_info info;
757    lfs_dir_open(&lfs, &dir, "a") => 0;
758    lfs_block_t block = dir.m.pair[0];
759    lfs_dir_close(&lfs, &dir) => 0;
760    lfs_unmount(&lfs) => 0;
761    uint8_t buffer[BLOCK_SIZE];
762    cfg->read(cfg, block, 0, buffer, BLOCK_SIZE) => 0;
763    int off = BLOCK_SIZE-1;
764    while (off >= 0 && buffer[off] == ERASE_VALUE) {
765        off -= 1;
766    }
767    memset(&buffer[off-3], BLOCK_SIZE, 3);
768    cfg->erase(cfg, block) => 0;
769    cfg->prog(cfg, block, 0, buffer, BLOCK_SIZE) => 0;
770    cfg->sync(cfg) => 0;
771
772    // corrupt the destination
773    lfs_mount(&lfs, cfg) => 0;
774    lfs_dir_open(&lfs, &dir, "c") => 0;
775    block = dir.m.pair[0];
776    lfs_dir_close(&lfs, &dir) => 0;
777    lfs_unmount(&lfs) => 0;
778    cfg->read(cfg, block, 0, buffer, BLOCK_SIZE) => 0;
779    off = BLOCK_SIZE-1;
780    while (off >= 0 && buffer[off] == ERASE_VALUE) {
781        off -= 1;
782    }
783    memset(&buffer[off-3], BLOCK_SIZE, 3);
784    cfg->erase(cfg, block) => 0;
785    cfg->prog(cfg, block, 0, buffer, BLOCK_SIZE) => 0;
786    cfg->sync(cfg) => 0;
787
788    // continue move
789    lfs_mount(&lfs, cfg) => 0;
790    lfs_rename(&lfs, "a/hi", "c/hi") => 0;
791    lfs_unmount(&lfs) => 0;
792
793    lfs_mount(&lfs, cfg) => 0;
794    lfs_dir_open(&lfs, &dir, "a") => 0;
795    lfs_dir_read(&lfs, &dir, &info) => 1;
796    assert(strcmp(info.name, ".") == 0);
797    assert(info.type == LFS_TYPE_DIR);
798    lfs_dir_read(&lfs, &dir, &info) => 1;
799    assert(strcmp(info.name, "..") == 0);
800    assert(info.type == LFS_TYPE_DIR);
801    lfs_dir_read(&lfs, &dir, &info) => 0;
802    lfs_dir_close(&lfs, &dir) => 0;
803    lfs_dir_open(&lfs, &dir, "c") => 0;
804    lfs_dir_read(&lfs, &dir, &info) => 1;
805    assert(strcmp(info.name, ".") == 0);
806    assert(info.type == LFS_TYPE_DIR);
807    lfs_dir_read(&lfs, &dir, &info) => 1;
808    assert(strcmp(info.name, "..") == 0);
809    assert(info.type == LFS_TYPE_DIR);
810    lfs_dir_read(&lfs, &dir, &info) => 1;
811    assert(strcmp(info.name, "hi") == 0);
812    assert(info.type == LFS_TYPE_DIR);
813    lfs_dir_read(&lfs, &dir, &info) => 0;
814    lfs_dir_close(&lfs, &dir) => 0;
815
816    lfs_dir_open(&lfs, &dir, "a/hi") => LFS_ERR_NOENT;
817    lfs_dir_open(&lfs, &dir, "b/hi") => LFS_ERR_NOENT;
818    lfs_dir_open(&lfs, &dir, "c/hi") => 0;
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    lfs_dir_read(&lfs, &dir, &info) => 1;
826    assert(strcmp(info.name, "bonjour") == 0);
827    assert(info.type == LFS_TYPE_DIR);
828    lfs_dir_read(&lfs, &dir, &info) => 1;
829    assert(strcmp(info.name, "hola") == 0);
830    assert(info.type == LFS_TYPE_DIR);
831    lfs_dir_read(&lfs, &dir, &info) => 1;
832    assert(strcmp(info.name, "ohayo") == 0);
833    assert(info.type == LFS_TYPE_DIR);
834    lfs_dir_read(&lfs, &dir, &info) => 0;
835    lfs_dir_close(&lfs, &dir) => 0;
836    lfs_dir_open(&lfs, &dir, "d/hi") => LFS_ERR_NOENT;
837    lfs_unmount(&lfs) => 0;
838'''
839
840[cases.test_reentrant_dir]
841reentrant = true
842code = '''
843    lfs_t lfs;
844    int err = lfs_mount(&lfs, cfg);
845    if (err) {
846        lfs_format(&lfs, cfg) => 0;
847        lfs_mount(&lfs, cfg) => 0;
848    }
849    err = lfs_mkdir(&lfs, "a");
850    assert(!err || err == LFS_ERR_EXIST);
851    err = lfs_mkdir(&lfs, "b");
852    assert(!err || err == LFS_ERR_EXIST);
853    err = lfs_mkdir(&lfs, "c");
854    assert(!err || err == LFS_ERR_EXIST);
855    err = lfs_mkdir(&lfs, "d");
856    assert(!err || err == LFS_ERR_EXIST);
857    lfs_unmount(&lfs) => 0;
858
859    while (true) {
860        lfs_mount(&lfs, cfg) => 0;
861        // there should never exist _2_ hi directories
862        int count = 0;
863        struct lfs_info info;
864        if (lfs_stat(&lfs, "a/hi", &info) == 0) {
865            assert(strcmp(info.name, "hi") == 0);
866            assert(info.type == LFS_TYPE_DIR);
867            count += 1;
868        }
869        if (lfs_stat(&lfs, "b/hi", &info) == 0) {
870            assert(strcmp(info.name, "hi") == 0);
871            assert(info.type == LFS_TYPE_DIR);
872            count += 1;
873        }
874        if (lfs_stat(&lfs, "c/hi", &info) == 0) {
875            assert(strcmp(info.name, "hi") == 0);
876            assert(info.type == LFS_TYPE_DIR);
877            count += 1;
878        }
879        if (lfs_stat(&lfs, "d/hi", &info) == 0) {
880            assert(strcmp(info.name, "hi") == 0);
881            assert(info.type == LFS_TYPE_DIR);
882            count += 1;
883        }
884        assert(count <= 1);
885        lfs_unmount(&lfs) => 0;
886
887        lfs_mount(&lfs, cfg) => 0;
888        if (lfs_stat(&lfs, "a/hi", &info) == 0) {
889            lfs_rename(&lfs, "a/hi", "b/hi") => 0;
890        } else if (lfs_stat(&lfs, "b/hi", &info) == 0) {
891            lfs_rename(&lfs, "b/hi", "c/hi") => 0;
892        } else if (lfs_stat(&lfs, "c/hi", &info) == 0) {
893            lfs_rename(&lfs, "c/hi", "d/hi") => 0;
894        } else if (lfs_stat(&lfs, "d/hi", &info) == 0) {
895            lfs_unmount(&lfs) => 0;
896            break; // success
897        } else {
898            // create dir and rename for atomicity
899            err = lfs_mkdir(&lfs, "temp");
900            assert(!err || err == LFS_ERR_EXIST);
901            err = lfs_mkdir(&lfs, "temp/hola");
902            assert(!err || err == LFS_ERR_EXIST);
903            err = lfs_mkdir(&lfs, "temp/bonjour");
904            assert(!err || err == LFS_ERR_EXIST);
905            err = lfs_mkdir(&lfs, "temp/ohayo");
906            assert(!err || err == LFS_ERR_EXIST);
907            lfs_rename(&lfs, "temp", "a/hi") => 0;
908        }
909        lfs_unmount(&lfs) => 0;
910    }
911
912    lfs_mount(&lfs, cfg) => 0;
913    lfs_dir_t dir;
914    struct lfs_info info;
915    lfs_dir_open(&lfs, &dir, "a") => 0;
916    lfs_dir_read(&lfs, &dir, &info) => 1;
917    assert(strcmp(info.name, ".") == 0);
918    assert(info.type == LFS_TYPE_DIR);
919    lfs_dir_read(&lfs, &dir, &info) => 1;
920    assert(strcmp(info.name, "..") == 0);
921    assert(info.type == LFS_TYPE_DIR);
922    lfs_dir_read(&lfs, &dir, &info) => 0;
923    lfs_dir_close(&lfs, &dir) => 0;
924    lfs_dir_open(&lfs, &dir, "d") => 0;
925    lfs_dir_read(&lfs, &dir, &info) => 1;
926    assert(strcmp(info.name, ".") == 0);
927    assert(info.type == LFS_TYPE_DIR);
928    lfs_dir_read(&lfs, &dir, &info) => 1;
929    assert(strcmp(info.name, "..") == 0);
930    assert(info.type == LFS_TYPE_DIR);
931    lfs_dir_read(&lfs, &dir, &info) => 1;
932    assert(strcmp(info.name, "hi") == 0);
933    assert(info.type == LFS_TYPE_DIR);
934    lfs_dir_read(&lfs, &dir, &info) => 0;
935    lfs_dir_close(&lfs, &dir) => 0;
936
937    lfs_dir_open(&lfs, &dir, "a/hi") => LFS_ERR_NOENT;
938    lfs_dir_open(&lfs, &dir, "b/hi") => LFS_ERR_NOENT;
939    lfs_dir_open(&lfs, &dir, "c/hi") => LFS_ERR_NOENT;
940    lfs_dir_open(&lfs, &dir, "d/hi") => 0;
941    lfs_dir_read(&lfs, &dir, &info) => 1;
942    assert(strcmp(info.name, ".") == 0);
943    assert(info.type == LFS_TYPE_DIR);
944    lfs_dir_read(&lfs, &dir, &info) => 1;
945    assert(strcmp(info.name, "..") == 0);
946    assert(info.type == LFS_TYPE_DIR);
947    lfs_dir_read(&lfs, &dir, &info) => 1;
948    assert(strcmp(info.name, "bonjour") == 0);
949    assert(info.type == LFS_TYPE_DIR);
950    lfs_dir_read(&lfs, &dir, &info) => 1;
951    assert(strcmp(info.name, "hola") == 0);
952    assert(info.type == LFS_TYPE_DIR);
953    lfs_dir_read(&lfs, &dir, &info) => 1;
954    assert(strcmp(info.name, "ohayo") == 0);
955    assert(info.type == LFS_TYPE_DIR);
956    lfs_dir_read(&lfs, &dir, &info) => 0;
957    lfs_dir_close(&lfs, &dir) => 0;
958    lfs_unmount(&lfs) => 0;
959'''
960
961[cases.test_move_state_stealing]
962code = '''
963    lfs_t lfs;
964    lfs_format(&lfs, cfg) => 0;
965    lfs_mount(&lfs, cfg) => 0;
966    lfs_mkdir(&lfs, "a") => 0;
967    lfs_mkdir(&lfs, "b") => 0;
968    lfs_mkdir(&lfs, "c") => 0;
969    lfs_mkdir(&lfs, "d") => 0;
970    lfs_file_t file;
971    lfs_file_open(&lfs, &file, "a/hello", LFS_O_CREAT | LFS_O_WRONLY) => 0;
972    lfs_file_write(&lfs, &file, "hola\n", 5) => 5;
973    lfs_file_write(&lfs, &file, "bonjour\n", 8) => 8;
974    lfs_file_write(&lfs, &file, "ohayo\n", 6) => 6;
975    lfs_file_close(&lfs, &file) => 0;
976    lfs_unmount(&lfs) => 0;
977
978    lfs_mount(&lfs, cfg) => 0;
979    lfs_rename(&lfs, "a/hello", "b/hello") => 0;
980    lfs_unmount(&lfs) => 0;
981    lfs_mount(&lfs, cfg) => 0;
982    lfs_rename(&lfs, "b/hello", "c/hello") => 0;
983    lfs_unmount(&lfs) => 0;
984    lfs_mount(&lfs, cfg) => 0;
985    lfs_rename(&lfs, "c/hello", "d/hello") => 0;
986    lfs_unmount(&lfs) => 0;
987
988    lfs_mount(&lfs, cfg) => 0;
989    lfs_file_open(&lfs, &file, "a/hello", LFS_O_RDONLY) => LFS_ERR_NOENT;
990    lfs_file_open(&lfs, &file, "b/hello", LFS_O_RDONLY) => LFS_ERR_NOENT;
991    lfs_file_open(&lfs, &file, "c/hello", LFS_O_RDONLY) => LFS_ERR_NOENT;
992    lfs_file_open(&lfs, &file, "d/hello", LFS_O_RDONLY) => 0;
993    uint8_t buffer[1024];
994    lfs_file_read(&lfs, &file, buffer, 5) => 5;
995    memcmp(buffer, "hola\n", 5) => 0;
996    lfs_file_read(&lfs, &file, buffer, 8) => 8;
997    memcmp(buffer, "bonjour\n", 8) => 0;
998    lfs_file_read(&lfs, &file, buffer, 6) => 6;
999    memcmp(buffer, "ohayo\n", 6) => 0;
1000    lfs_file_close(&lfs, &file) => 0;
1001    lfs_unmount(&lfs) => 0;
1002
1003    lfs_mount(&lfs, cfg) => 0;
1004    lfs_remove(&lfs, "b") => 0;
1005    lfs_remove(&lfs, "c") => 0;
1006    lfs_unmount(&lfs) => 0;
1007
1008    lfs_mount(&lfs, cfg) => 0;
1009    struct lfs_info info;
1010    lfs_stat(&lfs, "a", &info) => 0;
1011    lfs_stat(&lfs, "b", &info) => LFS_ERR_NOENT;
1012    lfs_stat(&lfs, "c", &info) => LFS_ERR_NOENT;
1013    lfs_stat(&lfs, "d", &info) => 0;
1014    lfs_file_open(&lfs, &file, "a/hello", LFS_O_RDONLY) => LFS_ERR_NOENT;
1015    lfs_file_open(&lfs, &file, "b/hello", LFS_O_RDONLY) => LFS_ERR_NOENT;
1016    lfs_file_open(&lfs, &file, "c/hello", LFS_O_RDONLY) => LFS_ERR_NOENT;
1017    lfs_file_open(&lfs, &file, "d/hello", LFS_O_RDONLY) => 0;
1018    lfs_file_read(&lfs, &file, buffer, 5) => 5;
1019    memcmp(buffer, "hola\n", 5) => 0;
1020    lfs_file_read(&lfs, &file, buffer, 8) => 8;
1021    memcmp(buffer, "bonjour\n", 8) => 0;
1022    lfs_file_read(&lfs, &file, buffer, 6) => 6;
1023    memcmp(buffer, "ohayo\n", 6) => 0;
1024    lfs_file_close(&lfs, &file) => 0;
1025    lfs_unmount(&lfs) => 0;
1026'''
1027
1028# Other specific corner cases
1029
1030# create + delete in same commit with neighbors
1031[cases.test_move_create_delete_same]
1032code = '''
1033    lfs_t lfs;
1034    lfs_format(&lfs, cfg) => 0;
1035    lfs_mount(&lfs, cfg) => 0;
1036
1037    // littlefs keeps files sorted, so we know the order these will be in
1038    lfs_file_t file;
1039    lfs_file_open(&lfs, &file, "/1.move_me",
1040            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1041    lfs_file_close(&lfs, &file) => 0;
1042
1043    lfs_file_open(&lfs, &file, "/0.before",
1044            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1045    lfs_file_write(&lfs, &file, "test.1", 7) => 7;
1046    lfs_file_close(&lfs, &file) => 0;
1047
1048    lfs_file_open(&lfs, &file, "/2.in_between",
1049            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1050    lfs_file_write(&lfs, &file, "test.2", 7) => 7;
1051    lfs_file_close(&lfs, &file) => 0;
1052
1053    lfs_file_open(&lfs, &file, "/4.after",
1054            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1055    lfs_file_write(&lfs, &file, "test.3", 7) => 7;
1056    lfs_file_close(&lfs, &file) => 0;
1057
1058    lfs_file_t files[3];
1059    lfs_file_open(&lfs, &files[0], "0.before",
1060            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1061    lfs_file_open(&lfs, &files[1], "2.in_between",
1062            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1063    lfs_file_open(&lfs, &files[2], "4.after",
1064            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1065    lfs_file_write(&lfs, &files[0], "test.4", 7) => 7;
1066    lfs_file_write(&lfs, &files[1], "test.5", 7) => 7;
1067    lfs_file_write(&lfs, &files[2], "test.6", 7) => 7;
1068
1069    // rename file while everything is open, this triggers both
1070    // a create and delete simultaneously
1071    lfs_rename(&lfs, "/1.move_me", "/3.move_me") => 0;
1072
1073    lfs_file_close(&lfs, &files[0]) => 0;
1074    lfs_file_close(&lfs, &files[1]) => 0;
1075    lfs_file_close(&lfs, &files[2]) => 0;
1076
1077    // check that nothing was corrupted
1078    lfs_dir_t dir;
1079    struct lfs_info info;
1080    lfs_dir_open(&lfs, &dir, "/") => 0;
1081    lfs_dir_read(&lfs, &dir, &info) => 1;
1082    assert(strcmp(info.name, ".") == 0);
1083    assert(info.type == LFS_TYPE_DIR);
1084    lfs_dir_read(&lfs, &dir, &info) => 1;
1085    assert(strcmp(info.name, "..") == 0);
1086    assert(info.type == LFS_TYPE_DIR);
1087    lfs_dir_read(&lfs, &dir, &info) => 1;
1088    assert(strcmp(info.name, "0.before") == 0);
1089    assert(info.type == LFS_TYPE_REG);
1090    assert(info.size == 7);
1091    lfs_dir_read(&lfs, &dir, &info) => 1;
1092    assert(strcmp(info.name, "2.in_between") == 0);
1093    assert(info.type == LFS_TYPE_REG);
1094    assert(info.size == 7);
1095    lfs_dir_read(&lfs, &dir, &info) => 1;
1096    assert(strcmp(info.name, "3.move_me") == 0);
1097    assert(info.type == LFS_TYPE_REG);
1098    assert(info.size == 0);
1099    lfs_dir_read(&lfs, &dir, &info) => 1;
1100    assert(strcmp(info.name, "4.after") == 0);
1101    assert(info.type == LFS_TYPE_REG);
1102    assert(info.size == 7);
1103    lfs_dir_read(&lfs, &dir, &info) => 0;
1104    lfs_dir_close(&lfs, &dir) => 0;
1105
1106    lfs_file_open(&lfs, &file, "/0.before", LFS_O_RDONLY) => 0;
1107    uint8_t buffer[1024];
1108    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1109    assert(strcmp((char*)buffer, "test.4") == 0);
1110    lfs_file_close(&lfs, &file) => 0;
1111    lfs_file_open(&lfs, &file, "/2.in_between", LFS_O_RDONLY) => 0;
1112    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1113    assert(strcmp((char*)buffer, "test.5") == 0);
1114    lfs_file_close(&lfs, &file) => 0;
1115    lfs_file_open(&lfs, &file, "/4.after", LFS_O_RDONLY) => 0;
1116    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1117    assert(strcmp((char*)buffer, "test.6") == 0);
1118    lfs_file_close(&lfs, &file) => 0;
1119
1120    // now move back
1121    lfs_file_open(&lfs, &files[0], "0.before",
1122            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1123    lfs_file_open(&lfs, &files[1], "2.in_between",
1124            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1125    lfs_file_open(&lfs, &files[2], "4.after",
1126            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1127    lfs_file_write(&lfs, &files[0], "test.7", 7) => 7;
1128    lfs_file_write(&lfs, &files[1], "test.8", 7) => 7;
1129    lfs_file_write(&lfs, &files[2], "test.9", 7) => 7;
1130
1131    // rename file while everything is open, this triggers both
1132    // a create and delete simultaneously
1133    lfs_rename(&lfs, "/3.move_me", "/1.move_me") => 0;
1134
1135    lfs_file_close(&lfs, &files[0]) => 0;
1136    lfs_file_close(&lfs, &files[1]) => 0;
1137    lfs_file_close(&lfs, &files[2]) => 0;
1138
1139    // and check that nothing was corrupted again
1140    lfs_dir_open(&lfs, &dir, "/") => 0;
1141    lfs_dir_read(&lfs, &dir, &info) => 1;
1142    assert(strcmp(info.name, ".") == 0);
1143    assert(info.type == LFS_TYPE_DIR);
1144    lfs_dir_read(&lfs, &dir, &info) => 1;
1145    assert(strcmp(info.name, "..") == 0);
1146    assert(info.type == LFS_TYPE_DIR);
1147    lfs_dir_read(&lfs, &dir, &info) => 1;
1148    assert(strcmp(info.name, "0.before") == 0);
1149    assert(info.type == LFS_TYPE_REG);
1150    assert(info.size == 7);
1151    lfs_dir_read(&lfs, &dir, &info) => 1;
1152    assert(strcmp(info.name, "1.move_me") == 0);
1153    assert(info.type == LFS_TYPE_REG);
1154    assert(info.size == 0);
1155    lfs_dir_read(&lfs, &dir, &info) => 1;
1156    assert(strcmp(info.name, "2.in_between") == 0);
1157    assert(info.type == LFS_TYPE_REG);
1158    assert(info.size == 7);
1159    lfs_dir_read(&lfs, &dir, &info) => 1;
1160    assert(strcmp(info.name, "4.after") == 0);
1161    assert(info.type == LFS_TYPE_REG);
1162    assert(info.size == 7);
1163    lfs_dir_read(&lfs, &dir, &info) => 0;
1164    lfs_dir_close(&lfs, &dir) => 0;
1165
1166    lfs_file_open(&lfs, &file, "/0.before", LFS_O_RDONLY) => 0;
1167    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1168    assert(strcmp((char*)buffer, "test.7") == 0);
1169    lfs_file_close(&lfs, &file) => 0;
1170    lfs_file_open(&lfs, &file, "/2.in_between", LFS_O_RDONLY) => 0;
1171    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1172    assert(strcmp((char*)buffer, "test.8") == 0);
1173    lfs_file_close(&lfs, &file) => 0;
1174    lfs_file_open(&lfs, &file, "/4.after", LFS_O_RDONLY) => 0;
1175    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1176    assert(strcmp((char*)buffer, "test.9") == 0);
1177    lfs_file_close(&lfs, &file) => 0;
1178    lfs_unmount(&lfs) => 0;
1179'''
1180
1181# create + delete + delete in same commit with neighbors
1182[cases.test_move_create_delete_delete_same]
1183code = '''
1184    lfs_t lfs;
1185    lfs_format(&lfs, cfg) => 0;
1186    lfs_mount(&lfs, cfg) => 0;
1187
1188    // littlefs keeps files sorted, so we know the order these will be in
1189    lfs_file_t file;
1190    lfs_file_open(&lfs, &file, "/1.move_me",
1191            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1192    lfs_file_close(&lfs, &file) => 0;
1193    lfs_file_open(&lfs, &file, "/3.move_me",
1194            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1195    lfs_file_write(&lfs, &file, "remove me",
1196            sizeof("remove me")) => sizeof("remove me");
1197    lfs_file_close(&lfs, &file) => 0;
1198
1199    lfs_file_open(&lfs, &file, "/0.before",
1200            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1201    lfs_file_write(&lfs, &file, "test.1", 7) => 7;
1202    lfs_file_close(&lfs, &file) => 0;
1203
1204    lfs_file_open(&lfs, &file, "/2.in_between",
1205            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1206    lfs_file_write(&lfs, &file, "test.2", 7) => 7;
1207    lfs_file_close(&lfs, &file) => 0;
1208
1209    lfs_file_open(&lfs, &file, "/4.after",
1210            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1211    lfs_file_write(&lfs, &file, "test.3", 7) => 7;
1212    lfs_file_close(&lfs, &file) => 0;
1213
1214    lfs_file_t files[3];
1215    lfs_file_open(&lfs, &files[0], "0.before",
1216            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1217    lfs_file_open(&lfs, &files[1], "2.in_between",
1218            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1219    lfs_file_open(&lfs, &files[2], "4.after",
1220            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1221    lfs_file_write(&lfs, &files[0], "test.4", 7) => 7;
1222    lfs_file_write(&lfs, &files[1], "test.5", 7) => 7;
1223    lfs_file_write(&lfs, &files[2], "test.6", 7) => 7;
1224
1225    // rename file while everything is open, this triggers both
1226    // a create and delete simultaneously
1227    lfs_rename(&lfs, "/1.move_me", "/3.move_me") => 0;
1228
1229    lfs_file_close(&lfs, &files[0]) => 0;
1230    lfs_file_close(&lfs, &files[1]) => 0;
1231    lfs_file_close(&lfs, &files[2]) => 0;
1232
1233    // check that nothing was corrupted
1234    lfs_dir_t dir;
1235    struct lfs_info info;
1236    lfs_dir_open(&lfs, &dir, "/") => 0;
1237    lfs_dir_read(&lfs, &dir, &info) => 1;
1238    assert(strcmp(info.name, ".") == 0);
1239    assert(info.type == LFS_TYPE_DIR);
1240    lfs_dir_read(&lfs, &dir, &info) => 1;
1241    assert(strcmp(info.name, "..") == 0);
1242    assert(info.type == LFS_TYPE_DIR);
1243    lfs_dir_read(&lfs, &dir, &info) => 1;
1244    assert(strcmp(info.name, "0.before") == 0);
1245    assert(info.type == LFS_TYPE_REG);
1246    assert(info.size == 7);
1247    lfs_dir_read(&lfs, &dir, &info) => 1;
1248    assert(strcmp(info.name, "2.in_between") == 0);
1249    assert(info.type == LFS_TYPE_REG);
1250    assert(info.size == 7);
1251    lfs_dir_read(&lfs, &dir, &info) => 1;
1252    assert(strcmp(info.name, "3.move_me") == 0);
1253    assert(info.type == LFS_TYPE_REG);
1254    assert(info.size == 0);
1255    lfs_dir_read(&lfs, &dir, &info) => 1;
1256    assert(strcmp(info.name, "4.after") == 0);
1257    assert(info.type == LFS_TYPE_REG);
1258    assert(info.size == 7);
1259    lfs_dir_read(&lfs, &dir, &info) => 0;
1260    lfs_dir_close(&lfs, &dir) => 0;
1261
1262    lfs_file_open(&lfs, &file, "/0.before", LFS_O_RDONLY) => 0;
1263    uint8_t buffer[1024];
1264    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1265    assert(strcmp((char*)buffer, "test.4") == 0);
1266    lfs_file_close(&lfs, &file) => 0;
1267    lfs_file_open(&lfs, &file, "/2.in_between", LFS_O_RDONLY) => 0;
1268    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1269    assert(strcmp((char*)buffer, "test.5") == 0);
1270    lfs_file_close(&lfs, &file) => 0;
1271    lfs_file_open(&lfs, &file, "/4.after", LFS_O_RDONLY) => 0;
1272    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1273    assert(strcmp((char*)buffer, "test.6") == 0);
1274    lfs_file_close(&lfs, &file) => 0;
1275
1276    // now move back
1277    lfs_file_open(&lfs, &file, "/1.move_me",
1278            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1279    lfs_file_write(&lfs, &file, "remove me",
1280            sizeof("remove me")) => sizeof("remove me");
1281    lfs_file_close(&lfs, &file) => 0;
1282
1283    lfs_file_open(&lfs, &files[0], "0.before",
1284            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1285    lfs_file_open(&lfs, &files[1], "2.in_between",
1286            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1287    lfs_file_open(&lfs, &files[2], "4.after",
1288            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1289    lfs_file_write(&lfs, &files[0], "test.7", 7) => 7;
1290    lfs_file_write(&lfs, &files[1], "test.8", 7) => 7;
1291    lfs_file_write(&lfs, &files[2], "test.9", 7) => 7;
1292
1293    // rename file while everything is open, this triggers both
1294    // a create and delete simultaneously
1295    lfs_rename(&lfs, "/3.move_me", "/1.move_me") => 0;
1296
1297    lfs_file_close(&lfs, &files[0]) => 0;
1298    lfs_file_close(&lfs, &files[1]) => 0;
1299    lfs_file_close(&lfs, &files[2]) => 0;
1300
1301    // and check that nothing was corrupted again
1302    lfs_dir_open(&lfs, &dir, "/") => 0;
1303    lfs_dir_read(&lfs, &dir, &info) => 1;
1304    assert(strcmp(info.name, ".") == 0);
1305    assert(info.type == LFS_TYPE_DIR);
1306    lfs_dir_read(&lfs, &dir, &info) => 1;
1307    assert(strcmp(info.name, "..") == 0);
1308    assert(info.type == LFS_TYPE_DIR);
1309    lfs_dir_read(&lfs, &dir, &info) => 1;
1310    assert(strcmp(info.name, "0.before") == 0);
1311    assert(info.type == LFS_TYPE_REG);
1312    assert(info.size == 7);
1313    lfs_dir_read(&lfs, &dir, &info) => 1;
1314    assert(strcmp(info.name, "1.move_me") == 0);
1315    assert(info.type == LFS_TYPE_REG);
1316    assert(info.size == 0);
1317    lfs_dir_read(&lfs, &dir, &info) => 1;
1318    assert(strcmp(info.name, "2.in_between") == 0);
1319    assert(info.type == LFS_TYPE_REG);
1320    assert(info.size == 7);
1321    lfs_dir_read(&lfs, &dir, &info) => 1;
1322    assert(strcmp(info.name, "4.after") == 0);
1323    assert(info.type == LFS_TYPE_REG);
1324    assert(info.size == 7);
1325    lfs_dir_read(&lfs, &dir, &info) => 0;
1326    lfs_dir_close(&lfs, &dir) => 0;
1327
1328    lfs_file_open(&lfs, &file, "/0.before", LFS_O_RDONLY) => 0;
1329    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1330    assert(strcmp((char*)buffer, "test.7") == 0);
1331    lfs_file_close(&lfs, &file) => 0;
1332    lfs_file_open(&lfs, &file, "/2.in_between", LFS_O_RDONLY) => 0;
1333    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1334    assert(strcmp((char*)buffer, "test.8") == 0);
1335    lfs_file_close(&lfs, &file) => 0;
1336    lfs_file_open(&lfs, &file, "/4.after", LFS_O_RDONLY) => 0;
1337    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1338    assert(strcmp((char*)buffer, "test.9") == 0);
1339    lfs_file_close(&lfs, &file) => 0;
1340    lfs_unmount(&lfs) => 0;
1341'''
1342
1343# create + delete in different dirs with neighbors
1344[cases.test_move_create_delete_different]
1345code = '''
1346    lfs_t lfs;
1347    lfs_format(&lfs, cfg) => 0;
1348    lfs_mount(&lfs, cfg) => 0;
1349
1350    // littlefs keeps files sorted, so we know the order these will be in
1351    lfs_mkdir(&lfs, "/dir.1") => 0;
1352    lfs_mkdir(&lfs, "/dir.2") => 0;
1353    lfs_file_t file;
1354    lfs_file_open(&lfs, &file, "/dir.1/1.move_me",
1355            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1356    lfs_file_close(&lfs, &file) => 0;
1357    lfs_file_open(&lfs, &file, "/dir.2/1.move_me",
1358            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1359    lfs_file_write(&lfs, &file, "remove me",
1360            sizeof("remove me")) => sizeof("remove me");
1361    lfs_file_close(&lfs, &file) => 0;
1362
1363    lfs_file_open(&lfs, &file, "/dir.1/0.before",
1364            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1365    lfs_file_write(&lfs, &file, "test.1", 7) => 7;
1366    lfs_file_close(&lfs, &file) => 0;
1367    lfs_file_open(&lfs, &file, "/dir.1/2.after",
1368            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1369    lfs_file_write(&lfs, &file, "test.2", 7) => 7;
1370    lfs_file_close(&lfs, &file) => 0;
1371
1372    lfs_file_open(&lfs, &file, "/dir.2/0.before",
1373            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1374    lfs_file_write(&lfs, &file, "test.3", 7) => 7;
1375    lfs_file_close(&lfs, &file) => 0;
1376    lfs_file_open(&lfs, &file, "/dir.2/2.after",
1377            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1378    lfs_file_write(&lfs, &file, "test.4", 7) => 7;
1379    lfs_file_close(&lfs, &file) => 0;
1380
1381    lfs_file_t files[4];
1382    lfs_file_open(&lfs, &files[0], "/dir.1/0.before",
1383            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1384    lfs_file_open(&lfs, &files[1], "/dir.1/2.after",
1385            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1386    lfs_file_open(&lfs, &files[2], "/dir.2/0.before",
1387            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1388    lfs_file_open(&lfs, &files[3], "/dir.2/2.after",
1389            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1390    lfs_file_write(&lfs, &files[0], "test.5", 7) => 7;
1391    lfs_file_write(&lfs, &files[1], "test.6", 7) => 7;
1392    lfs_file_write(&lfs, &files[2], "test.7", 7) => 7;
1393    lfs_file_write(&lfs, &files[3], "test.8", 7) => 7;
1394
1395    // rename file while everything is open, this triggers both
1396    // a create and delete as it overwrites the destination file
1397    lfs_rename(&lfs, "/dir.1/1.move_me", "/dir.2/1.move_me") => 0;
1398
1399    lfs_file_close(&lfs, &files[0]) => 0;
1400    lfs_file_close(&lfs, &files[1]) => 0;
1401    lfs_file_close(&lfs, &files[2]) => 0;
1402    lfs_file_close(&lfs, &files[3]) => 0;
1403
1404    // check that nothing was corrupted
1405    lfs_dir_t dir;
1406    struct lfs_info info;
1407    lfs_dir_open(&lfs, &dir, "/") => 0;
1408    lfs_dir_read(&lfs, &dir, &info) => 1;
1409    assert(strcmp(info.name, ".") == 0);
1410    assert(info.type == LFS_TYPE_DIR);
1411    lfs_dir_read(&lfs, &dir, &info) => 1;
1412    assert(strcmp(info.name, "..") == 0);
1413    assert(info.type == LFS_TYPE_DIR);
1414    lfs_dir_read(&lfs, &dir, &info) => 1;
1415    assert(strcmp(info.name, "dir.1") == 0);
1416    assert(info.type == LFS_TYPE_DIR);
1417    lfs_dir_read(&lfs, &dir, &info) => 1;
1418    assert(strcmp(info.name, "dir.2") == 0);
1419    assert(info.type == LFS_TYPE_DIR);
1420    lfs_dir_read(&lfs, &dir, &info) => 0;
1421    lfs_dir_close(&lfs, &dir) => 0;
1422
1423    lfs_dir_open(&lfs, &dir, "/dir.1") => 0;
1424    lfs_dir_read(&lfs, &dir, &info) => 1;
1425    assert(strcmp(info.name, ".") == 0);
1426    assert(info.type == LFS_TYPE_DIR);
1427    lfs_dir_read(&lfs, &dir, &info) => 1;
1428    assert(strcmp(info.name, "..") == 0);
1429    assert(info.type == LFS_TYPE_DIR);
1430    lfs_dir_read(&lfs, &dir, &info) => 1;
1431    assert(strcmp(info.name, "0.before") == 0);
1432    assert(info.type == LFS_TYPE_REG);
1433    assert(info.size == 7);
1434    lfs_dir_read(&lfs, &dir, &info) => 1;
1435    assert(strcmp(info.name, "2.after") == 0);
1436    assert(info.type == LFS_TYPE_REG);
1437    assert(info.size == 7);
1438    lfs_dir_read(&lfs, &dir, &info) => 0;
1439    lfs_dir_close(&lfs, &dir) => 0;
1440
1441    lfs_dir_open(&lfs, &dir, "/dir.2") => 0;
1442    lfs_dir_read(&lfs, &dir, &info) => 1;
1443    assert(strcmp(info.name, ".") == 0);
1444    assert(info.type == LFS_TYPE_DIR);
1445    lfs_dir_read(&lfs, &dir, &info) => 1;
1446    assert(strcmp(info.name, "..") == 0);
1447    assert(info.type == LFS_TYPE_DIR);
1448    lfs_dir_read(&lfs, &dir, &info) => 1;
1449    assert(strcmp(info.name, "0.before") == 0);
1450    assert(info.type == LFS_TYPE_REG);
1451    assert(info.size == 7);
1452    lfs_dir_read(&lfs, &dir, &info) => 1;
1453    assert(strcmp(info.name, "1.move_me") == 0);
1454    assert(info.type == LFS_TYPE_REG);
1455    assert(info.size == 0);
1456    lfs_dir_read(&lfs, &dir, &info) => 1;
1457    assert(strcmp(info.name, "2.after") == 0);
1458    assert(info.type == LFS_TYPE_REG);
1459    assert(info.size == 7);
1460    lfs_dir_read(&lfs, &dir, &info) => 0;
1461    lfs_dir_close(&lfs, &dir) => 0;
1462
1463    lfs_file_open(&lfs, &file, "/dir.1/0.before", LFS_O_RDONLY) => 0;
1464    uint8_t buffer[1024];
1465    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1466    assert(strcmp((char*)buffer, "test.5") == 0);
1467    lfs_file_close(&lfs, &file) => 0;
1468    lfs_file_open(&lfs, &file, "/dir.1/2.after", LFS_O_RDONLY) => 0;
1469    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1470    assert(strcmp((char*)buffer, "test.6") == 0);
1471    lfs_file_close(&lfs, &file) => 0;
1472    lfs_file_open(&lfs, &file, "/dir.2/0.before", LFS_O_RDONLY) => 0;
1473    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1474    assert(strcmp((char*)buffer, "test.7") == 0);
1475    lfs_file_close(&lfs, &file) => 0;
1476    lfs_file_open(&lfs, &file, "/dir.2/2.after", LFS_O_RDONLY) => 0;
1477    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1478    assert(strcmp((char*)buffer, "test.8") == 0);
1479    lfs_file_close(&lfs, &file) => 0;
1480
1481    // now move back
1482    lfs_file_open(&lfs, &file, "/dir.1/1.move_me",
1483            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1484    lfs_file_write(&lfs, &file, "remove me",
1485            sizeof("remove me")) => sizeof("remove me");
1486    lfs_file_close(&lfs, &file) => 0;
1487
1488    lfs_file_open(&lfs, &files[0], "/dir.1/0.before",
1489            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1490    lfs_file_open(&lfs, &files[1], "/dir.1/2.after",
1491            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1492    lfs_file_open(&lfs, &files[2], "/dir.2/0.before",
1493            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1494    lfs_file_open(&lfs, &files[3], "/dir.2/2.after",
1495            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1496    lfs_file_write(&lfs, &files[0], "test.9", 7) => 7;
1497    lfs_file_write(&lfs, &files[1], "test.a", 7) => 7;
1498    lfs_file_write(&lfs, &files[2], "test.b", 7) => 7;
1499    lfs_file_write(&lfs, &files[3], "test.c", 7) => 7;
1500
1501    // rename file while everything is open, this triggers both
1502    // a create and delete simultaneously
1503    lfs_rename(&lfs, "/dir.2/1.move_me", "/dir.1/1.move_me") => 0;
1504
1505    lfs_file_close(&lfs, &files[0]) => 0;
1506    lfs_file_close(&lfs, &files[1]) => 0;
1507    lfs_file_close(&lfs, &files[2]) => 0;
1508    lfs_file_close(&lfs, &files[3]) => 0;
1509
1510    // and check that nothing was corrupted again
1511    lfs_dir_open(&lfs, &dir, "/") => 0;
1512    lfs_dir_read(&lfs, &dir, &info) => 1;
1513    assert(strcmp(info.name, ".") == 0);
1514    assert(info.type == LFS_TYPE_DIR);
1515    lfs_dir_read(&lfs, &dir, &info) => 1;
1516    assert(strcmp(info.name, "..") == 0);
1517    assert(info.type == LFS_TYPE_DIR);
1518    lfs_dir_read(&lfs, &dir, &info) => 1;
1519    assert(strcmp(info.name, "dir.1") == 0);
1520    assert(info.type == LFS_TYPE_DIR);
1521    lfs_dir_read(&lfs, &dir, &info) => 1;
1522    assert(strcmp(info.name, "dir.2") == 0);
1523    assert(info.type == LFS_TYPE_DIR);
1524    lfs_dir_read(&lfs, &dir, &info) => 0;
1525    lfs_dir_close(&lfs, &dir) => 0;
1526
1527    lfs_dir_open(&lfs, &dir, "/dir.1") => 0;
1528    lfs_dir_read(&lfs, &dir, &info) => 1;
1529    assert(strcmp(info.name, ".") == 0);
1530    assert(info.type == LFS_TYPE_DIR);
1531    lfs_dir_read(&lfs, &dir, &info) => 1;
1532    assert(strcmp(info.name, "..") == 0);
1533    assert(info.type == LFS_TYPE_DIR);
1534    lfs_dir_read(&lfs, &dir, &info) => 1;
1535    assert(strcmp(info.name, "0.before") == 0);
1536    assert(info.type == LFS_TYPE_REG);
1537    assert(info.size == 7);
1538    lfs_dir_read(&lfs, &dir, &info) => 1;
1539    assert(strcmp(info.name, "1.move_me") == 0);
1540    assert(info.type == LFS_TYPE_REG);
1541    assert(info.size == 0);
1542    lfs_dir_read(&lfs, &dir, &info) => 1;
1543    assert(strcmp(info.name, "2.after") == 0);
1544    assert(info.type == LFS_TYPE_REG);
1545    assert(info.size == 7);
1546    lfs_dir_read(&lfs, &dir, &info) => 0;
1547    lfs_dir_close(&lfs, &dir) => 0;
1548
1549    lfs_dir_open(&lfs, &dir, "/dir.2") => 0;
1550    lfs_dir_read(&lfs, &dir, &info) => 1;
1551    assert(strcmp(info.name, ".") == 0);
1552    assert(info.type == LFS_TYPE_DIR);
1553    lfs_dir_read(&lfs, &dir, &info) => 1;
1554    assert(strcmp(info.name, "..") == 0);
1555    assert(info.type == LFS_TYPE_DIR);
1556    lfs_dir_read(&lfs, &dir, &info) => 1;
1557    assert(strcmp(info.name, "0.before") == 0);
1558    assert(info.type == LFS_TYPE_REG);
1559    assert(info.size == 7);
1560    lfs_dir_read(&lfs, &dir, &info) => 1;
1561    assert(strcmp(info.name, "2.after") == 0);
1562    assert(info.type == LFS_TYPE_REG);
1563    assert(info.size == 7);
1564    lfs_dir_read(&lfs, &dir, &info) => 0;
1565    lfs_dir_close(&lfs, &dir) => 0;
1566
1567    lfs_file_open(&lfs, &file, "/dir.1/0.before", LFS_O_RDONLY) => 0;
1568    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1569    assert(strcmp((char*)buffer, "test.9") == 0);
1570    lfs_file_close(&lfs, &file) => 0;
1571    lfs_file_open(&lfs, &file, "/dir.1/2.after", LFS_O_RDONLY) => 0;
1572    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1573    assert(strcmp((char*)buffer, "test.a") == 0);
1574    lfs_file_close(&lfs, &file) => 0;
1575    lfs_file_open(&lfs, &file, "/dir.2/0.before", LFS_O_RDONLY) => 0;
1576    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1577    assert(strcmp((char*)buffer, "test.b") == 0);
1578    lfs_file_close(&lfs, &file) => 0;
1579    lfs_file_open(&lfs, &file, "/dir.2/2.after", LFS_O_RDONLY) => 0;
1580    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1581    assert(strcmp((char*)buffer, "test.c") == 0);
1582    lfs_file_close(&lfs, &file) => 0;
1583    lfs_unmount(&lfs) => 0;
1584'''
1585
1586# move fix in relocation
1587[cases.test_move_fix_relocation]
1588in = "lfs.c"
1589defines.RELOCATIONS = 'range(4)'
1590defines.ERASE_CYCLES = 0xffffffff
1591code = '''
1592    lfs_t lfs;
1593    lfs_format(&lfs, cfg) => 0;
1594    lfs_mount(&lfs, cfg) => 0;
1595
1596    lfs_mkdir(&lfs, "/parent") => 0;
1597    lfs_mkdir(&lfs, "/parent/child") => 0;
1598
1599    lfs_file_t file;
1600    lfs_file_open(&lfs, &file, "/parent/1.move_me",
1601            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1602    lfs_file_write(&lfs, &file, "move me",
1603            sizeof("move me")) => sizeof("move me");
1604    lfs_file_close(&lfs, &file) => 0;
1605
1606    lfs_file_open(&lfs, &file, "/parent/0.before",
1607            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1608    lfs_file_write(&lfs, &file, "test.1", 7) => 7;
1609    lfs_file_close(&lfs, &file) => 0;
1610    lfs_file_open(&lfs, &file, "/parent/2.after",
1611            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1612    lfs_file_write(&lfs, &file, "test.2", 7) => 7;
1613    lfs_file_close(&lfs, &file) => 0;
1614    lfs_file_open(&lfs, &file, "/parent/child/0.before",
1615            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1616    lfs_file_write(&lfs, &file, "test.3", 7) => 7;
1617    lfs_file_close(&lfs, &file) => 0;
1618    lfs_file_open(&lfs, &file, "/parent/child/2.after",
1619            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1620    lfs_file_write(&lfs, &file, "test.4", 7) => 7;
1621    lfs_file_close(&lfs, &file) => 0;
1622
1623    lfs_file_t files[4];
1624    lfs_file_open(&lfs, &files[0], "/parent/0.before",
1625            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1626    lfs_file_open(&lfs, &files[1], "/parent/2.after",
1627            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1628    lfs_file_open(&lfs, &files[2], "/parent/child/0.before",
1629            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1630    lfs_file_open(&lfs, &files[3], "/parent/child/2.after",
1631            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1632    lfs_file_write(&lfs, &files[0], "test.5", 7) => 7;
1633    lfs_file_write(&lfs, &files[1], "test.6", 7) => 7;
1634    lfs_file_write(&lfs, &files[2], "test.7", 7) => 7;
1635    lfs_file_write(&lfs, &files[3], "test.8", 7) => 7;
1636
1637    // force specific directories to relocate
1638    if (RELOCATIONS & 0x1) {
1639        lfs_dir_t dir;
1640        lfs_dir_open(&lfs, &dir, "/parent");
1641        lfs_emubd_setwear(cfg, dir.m.pair[0], 0xffffffff) => 0;
1642        lfs_emubd_setwear(cfg, dir.m.pair[1], 0xffffffff) => 0;
1643        lfs_dir_close(&lfs, &dir) => 0;
1644    }
1645    if (RELOCATIONS & 0x2) {
1646        lfs_dir_t dir;
1647        lfs_dir_open(&lfs, &dir, "/parent/child");
1648        lfs_emubd_setwear(cfg, dir.m.pair[0], 0xffffffff) => 0;
1649        lfs_emubd_setwear(cfg, dir.m.pair[1], 0xffffffff) => 0;
1650        lfs_dir_close(&lfs, &dir) => 0;
1651    }
1652
1653    // ok, now we move the file, this creates a move that needs to be
1654    // fixed, possibly in a metadata-pair that needs to be relocated
1655    //
1656    // the worst case is if we need to relocate and we need to implicit
1657    // fix the move in our parent before it falls out of date
1658    lfs_rename(&lfs, "/parent/1.move_me", "/parent/child/1.move_me") => 0;
1659
1660    lfs_file_close(&lfs, &files[0]) => 0;
1661    lfs_file_close(&lfs, &files[1]) => 0;
1662    lfs_file_close(&lfs, &files[2]) => 0;
1663    lfs_file_close(&lfs, &files[3]) => 0;
1664
1665    // check that nothing was corrupted
1666    lfs_dir_t dir;
1667    struct lfs_info info;
1668    lfs_dir_open(&lfs, &dir, "/parent") => 0;
1669    lfs_dir_read(&lfs, &dir, &info) => 1;
1670    assert(strcmp(info.name, ".") == 0);
1671    assert(info.type == LFS_TYPE_DIR);
1672    lfs_dir_read(&lfs, &dir, &info) => 1;
1673    assert(strcmp(info.name, "..") == 0);
1674    assert(info.type == LFS_TYPE_DIR);
1675    lfs_dir_read(&lfs, &dir, &info) => 1;
1676    assert(strcmp(info.name, "0.before") == 0);
1677    assert(info.type == LFS_TYPE_REG);
1678    assert(info.size == 7);
1679    lfs_dir_read(&lfs, &dir, &info) => 1;
1680    assert(strcmp(info.name, "2.after") == 0);
1681    assert(info.type == LFS_TYPE_REG);
1682    assert(info.size == 7);
1683    lfs_dir_read(&lfs, &dir, &info) => 1;
1684    assert(strcmp(info.name, "child") == 0);
1685    assert(info.type == LFS_TYPE_DIR);
1686    lfs_dir_read(&lfs, &dir, &info) => 0;
1687    lfs_dir_close(&lfs, &dir) => 0;
1688
1689    lfs_dir_open(&lfs, &dir, "/parent/child") => 0;
1690    lfs_dir_read(&lfs, &dir, &info) => 1;
1691    assert(strcmp(info.name, ".") == 0);
1692    assert(info.type == LFS_TYPE_DIR);
1693    lfs_dir_read(&lfs, &dir, &info) => 1;
1694    assert(strcmp(info.name, "..") == 0);
1695    assert(info.type == LFS_TYPE_DIR);
1696    lfs_dir_read(&lfs, &dir, &info) => 1;
1697    assert(strcmp(info.name, "0.before") == 0);
1698    assert(info.type == LFS_TYPE_REG);
1699    assert(info.size == 7);
1700    lfs_dir_read(&lfs, &dir, &info) => 1;
1701    assert(strcmp(info.name, "1.move_me") == 0);
1702    assert(info.type == LFS_TYPE_REG);
1703    assert(info.size == sizeof("move me"));
1704    lfs_dir_read(&lfs, &dir, &info) => 1;
1705    assert(strcmp(info.name, "2.after") == 0);
1706    assert(info.type == LFS_TYPE_REG);
1707    assert(info.size == 7);
1708    lfs_dir_read(&lfs, &dir, &info) => 0;
1709    lfs_dir_close(&lfs, &dir) => 0;
1710
1711    lfs_file_open(&lfs, &file, "/parent/0.before", LFS_O_RDONLY) => 0;
1712    uint8_t buffer[1024];
1713    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1714    assert(strcmp((char*)buffer, "test.5") == 0);
1715    lfs_file_close(&lfs, &file) => 0;
1716    lfs_file_open(&lfs, &file, "/parent/2.after", LFS_O_RDONLY) => 0;
1717    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1718    assert(strcmp((char*)buffer, "test.6") == 0);
1719    lfs_file_close(&lfs, &file) => 0;
1720    lfs_file_open(&lfs, &file, "/parent/child/0.before", LFS_O_RDONLY) => 0;
1721    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1722    assert(strcmp((char*)buffer, "test.7") == 0);
1723    lfs_file_close(&lfs, &file) => 0;
1724    lfs_file_open(&lfs, &file, "/parent/child/2.after", LFS_O_RDONLY) => 0;
1725    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1726    assert(strcmp((char*)buffer, "test.8") == 0);
1727    lfs_file_close(&lfs, &file) => 0;
1728    lfs_unmount(&lfs) => 0;
1729'''
1730
1731# move fix in relocation with predecessor
1732[cases.test_move_fix_relocation_predecessor]
1733in = "lfs.c"
1734defines.RELOCATIONS = 'range(8)'
1735defines.ERASE_CYCLES = 0xffffffff
1736code = '''
1737    lfs_t lfs;
1738    lfs_format(&lfs, cfg) => 0;
1739    lfs_mount(&lfs, cfg) => 0;
1740
1741    lfs_mkdir(&lfs, "/parent") => 0;
1742    lfs_mkdir(&lfs, "/parent/child") => 0;
1743    lfs_mkdir(&lfs, "/parent/sibling") => 0;
1744
1745    lfs_file_t file;
1746    lfs_file_open(&lfs, &file, "/parent/sibling/1.move_me",
1747            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1748    lfs_file_write(&lfs, &file, "move me",
1749            sizeof("move me")) => sizeof("move me");
1750    lfs_file_close(&lfs, &file) => 0;
1751
1752    lfs_file_open(&lfs, &file, "/parent/sibling/0.before",
1753            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1754    lfs_file_write(&lfs, &file, "test.1", 7) => 7;
1755    lfs_file_close(&lfs, &file) => 0;
1756    lfs_file_open(&lfs, &file, "/parent/sibling/2.after",
1757            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1758    lfs_file_write(&lfs, &file, "test.2", 7) => 7;
1759    lfs_file_close(&lfs, &file) => 0;
1760    lfs_file_open(&lfs, &file, "/parent/child/0.before",
1761            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1762    lfs_file_write(&lfs, &file, "test.3", 7) => 7;
1763    lfs_file_close(&lfs, &file) => 0;
1764    lfs_file_open(&lfs, &file, "/parent/child/2.after",
1765            LFS_O_WRONLY | LFS_O_CREAT) => 0;
1766    lfs_file_write(&lfs, &file, "test.4", 7) => 7;
1767    lfs_file_close(&lfs, &file) => 0;
1768
1769    lfs_file_t files[4];
1770    lfs_file_open(&lfs, &files[0], "/parent/sibling/0.before",
1771            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1772    lfs_file_open(&lfs, &files[1], "/parent/sibling/2.after",
1773            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1774    lfs_file_open(&lfs, &files[2], "/parent/child/0.before",
1775            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1776    lfs_file_open(&lfs, &files[3], "/parent/child/2.after",
1777            LFS_O_WRONLY | LFS_O_TRUNC) => 0;
1778    lfs_file_write(&lfs, &files[0], "test.5", 7) => 7;
1779    lfs_file_write(&lfs, &files[1], "test.6", 7) => 7;
1780    lfs_file_write(&lfs, &files[2], "test.7", 7) => 7;
1781    lfs_file_write(&lfs, &files[3], "test.8", 7) => 7;
1782
1783    // force specific directories to relocate
1784    if (RELOCATIONS & 0x1) {
1785        lfs_dir_t dir;
1786        lfs_dir_open(&lfs, &dir, "/parent");
1787        lfs_emubd_setwear(cfg, dir.m.pair[0], 0xffffffff) => 0;
1788        lfs_emubd_setwear(cfg, dir.m.pair[1], 0xffffffff) => 0;
1789        lfs_dir_close(&lfs, &dir) => 0;
1790    }
1791    if (RELOCATIONS & 0x2) {
1792        lfs_dir_t dir;
1793        lfs_dir_open(&lfs, &dir, "/parent/sibling");
1794        lfs_emubd_setwear(cfg, dir.m.pair[0], 0xffffffff) => 0;
1795        lfs_emubd_setwear(cfg, dir.m.pair[1], 0xffffffff) => 0;
1796        lfs_dir_close(&lfs, &dir) => 0;
1797    }
1798    if (RELOCATIONS & 0x4) {
1799        lfs_dir_t dir;
1800        lfs_dir_open(&lfs, &dir, "/parent/child");
1801        lfs_emubd_setwear(cfg, dir.m.pair[0], 0xffffffff) => 0;
1802        lfs_emubd_setwear(cfg, dir.m.pair[1], 0xffffffff) => 0;
1803        lfs_dir_close(&lfs, &dir) => 0;
1804    }
1805
1806    // ok, now we move the file, this creates a move that needs to be
1807    // fixed, possibly in a metadata-pair that needs to be relocated
1808    //
1809    // and now relocations can force us to need to fix our move in either
1810    // the parent or child before things break
1811    lfs_rename(&lfs,
1812            "/parent/sibling/1.move_me",
1813            "/parent/child/1.move_me") => 0;
1814
1815    lfs_file_close(&lfs, &files[0]) => 0;
1816    lfs_file_close(&lfs, &files[1]) => 0;
1817    lfs_file_close(&lfs, &files[2]) => 0;
1818    lfs_file_close(&lfs, &files[3]) => 0;
1819
1820    // check that nothing was corrupted
1821    lfs_dir_t dir;
1822    struct lfs_info info;
1823    lfs_dir_open(&lfs, &dir, "/parent") => 0;
1824    lfs_dir_read(&lfs, &dir, &info) => 1;
1825    assert(strcmp(info.name, ".") == 0);
1826    assert(info.type == LFS_TYPE_DIR);
1827    lfs_dir_read(&lfs, &dir, &info) => 1;
1828    assert(strcmp(info.name, "..") == 0);
1829    assert(info.type == LFS_TYPE_DIR);
1830    lfs_dir_read(&lfs, &dir, &info) => 1;
1831    assert(strcmp(info.name, "child") == 0);
1832    assert(info.type == LFS_TYPE_DIR);
1833    lfs_dir_read(&lfs, &dir, &info) => 1;
1834    assert(strcmp(info.name, "sibling") == 0);
1835    assert(info.type == LFS_TYPE_DIR);
1836    lfs_dir_read(&lfs, &dir, &info) => 0;
1837    lfs_dir_close(&lfs, &dir) => 0;
1838
1839    lfs_dir_open(&lfs, &dir, "/parent/sibling") => 0;
1840    lfs_dir_read(&lfs, &dir, &info) => 1;
1841    assert(strcmp(info.name, ".") == 0);
1842    assert(info.type == LFS_TYPE_DIR);
1843    lfs_dir_read(&lfs, &dir, &info) => 1;
1844    assert(strcmp(info.name, "..") == 0);
1845    assert(info.type == LFS_TYPE_DIR);
1846    lfs_dir_read(&lfs, &dir, &info) => 1;
1847    assert(strcmp(info.name, "0.before") == 0);
1848    assert(info.type == LFS_TYPE_REG);
1849    assert(info.size == 7);
1850    lfs_dir_read(&lfs, &dir, &info) => 1;
1851    assert(strcmp(info.name, "2.after") == 0);
1852    assert(info.type == LFS_TYPE_REG);
1853    assert(info.size == 7);
1854    lfs_dir_read(&lfs, &dir, &info) => 0;
1855    lfs_dir_close(&lfs, &dir) => 0;
1856
1857    lfs_dir_open(&lfs, &dir, "/parent/child") => 0;
1858    lfs_dir_read(&lfs, &dir, &info) => 1;
1859    assert(strcmp(info.name, ".") == 0);
1860    assert(info.type == LFS_TYPE_DIR);
1861    lfs_dir_read(&lfs, &dir, &info) => 1;
1862    assert(strcmp(info.name, "..") == 0);
1863    assert(info.type == LFS_TYPE_DIR);
1864    lfs_dir_read(&lfs, &dir, &info) => 1;
1865    assert(strcmp(info.name, "0.before") == 0);
1866    assert(info.type == LFS_TYPE_REG);
1867    assert(info.size == 7);
1868    lfs_dir_read(&lfs, &dir, &info) => 1;
1869    assert(strcmp(info.name, "1.move_me") == 0);
1870    assert(info.type == LFS_TYPE_REG);
1871    assert(info.size == sizeof("move me"));
1872    lfs_dir_read(&lfs, &dir, &info) => 1;
1873    assert(strcmp(info.name, "2.after") == 0);
1874    assert(info.type == LFS_TYPE_REG);
1875    assert(info.size == 7);
1876    lfs_dir_read(&lfs, &dir, &info) => 0;
1877    lfs_dir_close(&lfs, &dir) => 0;
1878
1879    lfs_file_open(&lfs, &file, "/parent/sibling/0.before", LFS_O_RDONLY) => 0;
1880    uint8_t buffer[1024];
1881    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1882    assert(strcmp((char*)buffer, "test.5") == 0);
1883    lfs_file_close(&lfs, &file) => 0;
1884    lfs_file_open(&lfs, &file, "/parent/sibling/2.after", LFS_O_RDONLY) => 0;
1885    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1886    assert(strcmp((char*)buffer, "test.6") == 0);
1887    lfs_file_close(&lfs, &file) => 0;
1888    lfs_file_open(&lfs, &file, "/parent/child/0.before", LFS_O_RDONLY) => 0;
1889    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1890    assert(strcmp((char*)buffer, "test.7") == 0);
1891    lfs_file_close(&lfs, &file) => 0;
1892    lfs_file_open(&lfs, &file, "/parent/child/2.after", LFS_O_RDONLY) => 0;
1893    lfs_file_read(&lfs, &file, buffer, 7) => 7;
1894    assert(strcmp((char*)buffer, "test.8") == 0);
1895    lfs_file_close(&lfs, &file) => 0;
1896    lfs_unmount(&lfs) => 0;
1897'''
1898