1 /*
2  * Copyright (C) 2015-2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <assert.h>
18 #include <inttypes.h>
19 #include <limits.h>
20 #include <lk/macros.h>
21 #include <malloc.h>
22 #include <stdbool.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <string.h>
26 
27 #include <openssl/rand.h>
28 
29 #include "block_allocator.h"
30 #include "block_cache.h"
31 #include "block_map.h"
32 #include "block_set.h"
33 #include "checkpoint.h"
34 #include "crypt.h"
35 #include "debug_stats.h"
36 #include "error_reporting_mock.h"
37 #include "file.h"
38 #include "transaction.h"
39 
40 #include <time.h>
41 
gettime(uint32_t clock_id,uint32_t flags,int64_t * time)42 long gettime(uint32_t clock_id, uint32_t flags, int64_t* time) {
43     int ret;
44     struct timespec ts;
45     assert(!clock_id);
46     assert(!flags);
47 
48     ret = clock_gettime(CLOCK_MONOTONIC, &ts);
49     assert(!ret);
50     *time = ts.tv_sec * 1000000000LL + ts.tv_nsec;
51 
52     return 0;
53 }
54 
55 #define FILE_SYSTEM_TEST "block_test"
56 
57 #if 0 /* test tree order 3 */
58 /* not useful, b+tree for free set grows faster than the space that is added to it */
59 #define BLOCK_SIZE (64)
60 #define BLOCK_COUNT (256)
61 #elif 0 /* test tree order 4 */
62 #define BLOCK_SIZE (80)
63 #define BLOCK_COUNT (256)
64 #elif 0 /* test tree order 5 */
65 #define BLOCK_SIZE (96)
66 #define BLOCK_COUNT (256)
67 #elif 0 /* test tree order 6 */
68 #define BLOCK_SIZE (112)
69 #define BLOCK_COUNT (256)
70 #elif 0 /* test tree order 7 */
71 #define BLOCK_SIZE (128)
72 #define BLOCK_COUNT (256)
73 #elif 0 /* test tree order 8 */
74 #define BLOCK_SIZE (144)
75 #define BLOCK_COUNT (256)
76 #elif 0 /* test single rpmb block with 64-bit indexes */
77 #define BLOCK_SIZE (256)
78 #define BLOCK_COUNT (256)
79 #elif 1
80 #define BLOCK_SIZE (2048)
81 #define BLOCK_COUNT (256)
82 #elif 0
83 /* test single rpmb block with simulated 16-bit indexes, 128kb device */
84 #define BLOCK_SIZE (256 * 4)
85 #define BLOCK_COUNT (512)
86 #elif 0
87 /* test single rpmb block with simulated 16-bit indexes, 4MB device */
88 #define BLOCK_SIZE (256 * 4)
89 #define BLOCK_COUNT (16384)
90 #else
91 #define BLOCK_SIZE (256 * 4)
92 #define BLOCK_COUNT (0x10000)
93 #endif
94 
95 struct block {
96     char data[BLOCK_SIZE];
97     char data_copy[BLOCK_SIZE];
98     struct mac mac;
99     bool loaded;
100     bool dirty;
101     bool dirty_ref;
102     struct block* parent;
103     struct block_mac* block_mac_in_parent;
104     const char* used_by_str;
105     data_block_t used_by_block;
106     const char* checkpoint_used_by_str;
107     data_block_t checkpoint_used_by_block;
108 };
109 static struct block blocks[BLOCK_COUNT];
110 static struct block blocks_backup[BLOCK_COUNT];
111 static const struct key key;
112 
113 static bool allow_repaired = false;
114 
115 static bool print_test_verbose = false;
116 static bool print_block_tree_test_verbose = false;
117 
118 data_block_t block_test_fail_write_blocks;
119 
transaction_complete(struct transaction * tr)120 static inline void transaction_complete(struct transaction* tr) {
121     return transaction_complete_etc(tr, false);
122 }
123 
transaction_complete_update_checkpoint(struct transaction * tr)124 static inline void transaction_complete_update_checkpoint(
125         struct transaction* tr) {
126     return transaction_complete_etc(tr, true);
127 }
128 
block_test_clear_reinit_etc(struct transaction * tr,uint32_t flags,bool swap,bool clear,size_t start)129 static void block_test_clear_reinit_etc(struct transaction* tr,
130                                         uint32_t flags,
131                                         bool swap,
132                                         bool clear,
133                                         size_t start) {
134     struct fs* fs = tr->fs;
135     const struct key* key = fs->key;
136     struct block_device* dev = tr->fs->dev;
137     struct block_device* super_dev = tr->fs->super_dev;
138     int i;
139     struct block tmp;
140     int ret;
141 
142     transaction_free(tr);
143     fs_destroy(fs);
144     block_cache_dev_destroy(dev);
145 
146     if (swap) {
147         for (i = start; i < BLOCK_COUNT; ++i) {
148             tmp = blocks[i];
149             blocks[i] = blocks_backup[i];
150             blocks_backup[i] = tmp;
151         }
152     }
153 
154     if (clear) {
155         memset(&blocks[start], 0, (BLOCK_COUNT - start) * sizeof(struct block));
156     }
157 
158     ret = fs_init(fs, FILE_SYSTEM_TEST, key, dev, super_dev, flags);
159     assert(ret == 0);
160     fs->reserved_count = 18; /* HACK: override default reserved space */
161     transaction_init(tr, fs, true);
162 }
163 
block_test_swap_clear_reinit(struct transaction * tr,uint32_t flags)164 static void block_test_swap_clear_reinit(struct transaction* tr,
165                                          uint32_t flags) {
166     block_test_clear_reinit_etc(tr, flags, true, true, 2);
167 }
168 
block_test_swap_reinit(struct transaction * tr,uint32_t flags)169 static void block_test_swap_reinit(struct transaction* tr, uint32_t flags) {
170     block_test_clear_reinit_etc(tr, flags, true, false, 2);
171 }
172 
block_test_reinit(struct transaction * tr,uint32_t flags)173 static void block_test_reinit(struct transaction* tr, uint32_t flags) {
174     block_test_clear_reinit_etc(tr, flags, false, false, 2);
175 }
176 
block_test_clear_superblock_reinit(struct transaction * tr,uint32_t flags)177 static void block_test_clear_superblock_reinit(struct transaction* tr,
178                                                uint32_t flags) {
179     block_test_clear_reinit_etc(tr, flags, false, true, 0);
180 }
181 
block_test_start_read(struct block_device * dev,data_block_t block)182 static void block_test_start_read(struct block_device* dev,
183                                   data_block_t block) {
184     assert(dev->block_size <= BLOCK_SIZE);
185     assert(block < countof(blocks));
186     block_cache_complete_read(dev, block, blocks[block].data, dev->block_size,
187                               BLOCK_READ_SUCCESS);
188 }
189 
block_test_start_write(struct block_device * dev,data_block_t block,const void * data,size_t data_size,bool sync)190 static void block_test_start_write(struct block_device* dev,
191                                    data_block_t block,
192                                    const void* data,
193                                    size_t data_size,
194                                    bool sync) {
195     assert(block < countof(blocks));
196     assert(data_size <= sizeof(blocks[block].data));
197     memcpy(blocks[block].data, data, data_size);
198     block_cache_complete_write(dev, block,
199                                block < block_test_fail_write_blocks
200                                        ? BLOCK_WRITE_FAILED
201                                        : BLOCK_WRITE_SUCCESS);
202 }
203 
204 #if FULL_ASSERT
block_clear_used_by(void)205 static void block_clear_used_by(void) {
206     size_t block;
207     for (block = 0; block < countof(blocks); block++) {
208         blocks[block].used_by_str = NULL;
209         blocks[block].used_by_block = 0;
210         blocks[block].checkpoint_used_by_str = NULL;
211         blocks[block].checkpoint_used_by_block = 0;
212     }
213 }
214 
block_set_used_by_etc(data_block_t block,const char * used_by_str,data_block_t used_by_block,bool checkpoint,bool force)215 static void block_set_used_by_etc(data_block_t block,
216                                   const char* used_by_str,
217                                   data_block_t used_by_block,
218                                   bool checkpoint,
219                                   bool force) {
220     assert(block < countof(blocks));
221 
222     if (checkpoint) {
223         if (force || !blocks[block].checkpoint_used_by_str) {
224             blocks[block].checkpoint_used_by_str = used_by_str;
225             blocks[block].checkpoint_used_by_block = used_by_block;
226         }
227         assert(blocks[block].checkpoint_used_by_str == used_by_str);
228         assert(blocks[block].checkpoint_used_by_block == used_by_block);
229     } else {
230         if (force || !blocks[block].used_by_str) {
231             blocks[block].used_by_str = used_by_str;
232             blocks[block].used_by_block = used_by_block;
233         }
234         assert(blocks[block].used_by_str == used_by_str);
235         assert(blocks[block].used_by_block == used_by_block);
236     }
237 }
238 
block_set_replace_used_by(data_block_t block,const char * old_used_by_str,const char * new_used_by_str,data_block_t new_used_by_block,bool checkpoint)239 static bool block_set_replace_used_by(data_block_t block,
240                                       const char* old_used_by_str,
241                                       const char* new_used_by_str,
242                                       data_block_t new_used_by_block,
243                                       bool checkpoint) {
244     if (!blocks[block].used_by_str ||
245         strcmp(blocks[block].used_by_str, old_used_by_str) != 0) {
246         return false;
247     }
248 
249     block_set_used_by_etc(block, new_used_by_str, new_used_by_block, checkpoint,
250                           true);
251     return true;
252 }
253 
block_set_used_by(data_block_t block,const char * used_by_str,data_block_t used_by_block)254 static void block_set_used_by(data_block_t block,
255                               const char* used_by_str,
256                               data_block_t used_by_block) {
257     block_set_used_by_etc(block, used_by_str, used_by_block, false, false);
258 }
259 
mark_block_tree_in_use(struct transaction * tr,struct block_tree * block_tree,bool mark_data_used,const char * used_by_str,data_block_t used_by_block,bool checkpoint)260 static void mark_block_tree_in_use(struct transaction* tr,
261                                    struct block_tree* block_tree,
262                                    bool mark_data_used,
263                                    const char* used_by_str,
264                                    data_block_t used_by_block,
265                                    bool checkpoint) {
266     struct block_tree_path path;
267     unsigned int i;
268 
269     block_tree_walk(tr, block_tree, 0, true, &path);
270     if (path.count) {
271         /* mark root in use in case it is empty */
272         block_set_used_by_etc(block_mac_to_block(tr, &path.entry[0].block_mac),
273                               used_by_str, used_by_block, checkpoint, false);
274     }
275     while (block_tree_path_get_key(&path)) {
276         for (i = 0; i < path.count; i++) {
277             block_set_used_by_etc(
278                     block_mac_to_block(tr, &path.entry[i].block_mac),
279                     used_by_str, used_by_block, checkpoint, false);
280         }
281         if (mark_data_used) {
282             block_set_used_by_etc(block_tree_path_get_data(&path), used_by_str,
283                                   used_by_block, checkpoint, false);
284         }
285         block_tree_path_next(&path);
286     }
287 }
288 
mark_files_in_use(struct transaction * tr)289 static void mark_files_in_use(struct transaction* tr) {
290     void file_block_map_init(struct transaction * tr,
291                              struct block_map * block_map,
292                              const struct block_mac* file);
293 
294     struct block_tree_path path;
295     struct block_map block_map;
296 
297     block_tree_walk(tr, &tr->fs->files, 0, true, &path);
298     while (block_tree_path_get_key(&path)) {
299         struct block_mac block_mac = block_tree_path_get_data_block_mac(&path);
300         file_block_map_init(tr, &block_map, &block_mac);
301         mark_block_tree_in_use(tr, &block_map.tree, true, "file",
302                                block_mac_to_block(tr, &block_mac), false);
303         block_tree_path_next(&path);
304     }
305 }
306 
check_fs_prepare(struct transaction * tr)307 static void check_fs_prepare(struct transaction* tr) {
308     data_block_t block;
309     struct block_tree checkpoint_files =
310             BLOCK_TREE_INITIAL_VALUE(checkpoint_files);
311     size_t block_mac_size = tr->fs->block_num_size + tr->fs->mac_size;
312     block_tree_init(&checkpoint_files, tr->fs->dev->block_size,
313                     tr->fs->block_num_size, block_mac_size, block_mac_size);
314 
315     block_clear_used_by();
316 
317     for (block = tr->fs->dev->block_count; block < countof(blocks); block++) {
318         block_set_used_by(block, "out-of-range", 0);
319     }
320 
321     block_set_used_by(tr->fs->super_block[0], "superblock", 0);
322     block_set_used_by(tr->fs->super_block[1], "superblock", 1);
323 
324     block = 1;
325     while (true) {
326         block = block_set_find_next_block(tr, &tr->fs->free, block, true);
327         if (!block) {
328             break;
329         }
330         block_set_used_by(block, "free", 0);
331         block++;
332     }
333 
334     mark_block_tree_in_use(tr, &tr->fs->free.block_tree, false,
335                            "free_tree_node", 0, false);
336 
337     mark_block_tree_in_use(tr, &tr->fs->files, true, "files", 0, false);
338     mark_files_in_use(tr);
339 
340     if (block_mac_valid(tr, &tr->fs->checkpoint)) {
341         assert(checkpoint_read(tr, &tr->fs->checkpoint, &checkpoint_files,
342                                NULL));
343         block_set_used_by_etc(block_mac_to_block(tr, &tr->fs->checkpoint),
344                               "checkpoint", 0, true, false);
345         mark_block_tree_in_use(tr, &checkpoint_files, true, "checkpoint_files",
346                                0, true);
347         mark_block_tree_in_use(tr, &tr->fs->checkpoint_free.block_tree, false,
348                                "checkpoint_free", 0, true);
349     }
350 }
351 
check_fs_finish(struct transaction * tr)352 static bool check_fs_finish(struct transaction* tr) {
353     bool valid = true;
354     data_block_t block;
355 
356     for (block = 0; block < countof(blocks); block++) {
357         if (!blocks[block].used_by_str &&
358             !blocks[block].checkpoint_used_by_str) {
359             printf("block %" PRIu64 ", lost\n", block);
360             valid = false;
361         }
362     }
363 
364     if (!valid) {
365         printf("free:\n");
366         block_set_print(tr, &tr->fs->free);
367         files_print(tr);
368         printf("checkpoint free:\n");
369         block_set_print(tr, &tr->fs->checkpoint_free);
370     }
371 
372     return valid;
373 }
374 
get_fs_checkpoint_count(struct transaction * tr)375 static size_t get_fs_checkpoint_count(struct transaction* tr) {
376     data_block_t block;
377     size_t checkpoint_count = 0;
378     check_fs_prepare(tr);
379     for (block = 0; block < countof(blocks); block++) {
380         if (blocks[block].checkpoint_used_by_str &&
381             strncmp("checkpoint", blocks[block].checkpoint_used_by_str,
382                     strlen("checkpoint")) == 0) {
383             checkpoint_count++;
384         }
385     }
386     assert(check_fs_finish(tr));
387     return checkpoint_count;
388 }
389 
check_fs_allocated(struct transaction * tr,data_block_t * allocated,size_t allocated_count)390 static bool check_fs_allocated(struct transaction* tr,
391                                data_block_t* allocated,
392                                size_t allocated_count) {
393     size_t i;
394 
395     check_fs_prepare(tr);
396 
397     for (i = 0; i < allocated_count; i++) {
398         block_set_used_by(allocated[i], "allocated", 0);
399     }
400 
401     return check_fs_finish(tr);
402 }
403 
check_fs(struct transaction * tr)404 static bool check_fs(struct transaction* tr) {
405     return check_fs_allocated(tr, NULL, 0);
406 }
407 #endif
408 
empty_test(struct transaction * tr)409 static void empty_test(struct transaction* tr) {
410     struct block_range range;
411 
412     range.start = 4;
413     range.end = tr->fs->dev->block_count;
414     assert(block_set_range_in_set(tr, &tr->fs->free, range));
415 
416     if (print_test_verbose) {
417         printf("%s: initial free state:\n", __func__);
418         block_set_print(tr, &tr->fs->free);
419     }
420 }
421 
422 typedef uint16_t (*keyfunc_t)(unsigned int index,
423                               unsigned int rindex,
424                               unsigned int maxindex);
inc_inc_key(unsigned int index,unsigned int rindex,unsigned int maxindex)425 static uint16_t inc_inc_key(unsigned int index,
426                             unsigned int rindex,
427                             unsigned int maxindex) {
428     return rindex ?: index;
429 }
430 
inc_dec_key(unsigned int index,unsigned int rindex,unsigned int maxindex)431 static uint16_t inc_dec_key(unsigned int index,
432                             unsigned int rindex,
433                             unsigned int maxindex) {
434     return index;
435 }
436 
dec_inc_key(unsigned int index,unsigned int rindex,unsigned int maxindex)437 static uint16_t dec_inc_key(unsigned int index,
438                             unsigned int rindex,
439                             unsigned int maxindex) {
440     return maxindex + 1 - index;
441 }
442 
dec_dec_key(unsigned int index,unsigned int rindex,unsigned int maxindex)443 static uint16_t dec_dec_key(unsigned int index,
444                             unsigned int rindex,
445                             unsigned int maxindex) {
446     return maxindex + 1 - (rindex ?: index);
447 }
448 
same_key(unsigned int index,unsigned int rindex,unsigned int maxindex)449 static uint16_t same_key(unsigned int index,
450                          unsigned int rindex,
451                          unsigned int maxindex) {
452     return 1;
453 }
454 
rand_key(unsigned int index,unsigned int rindex,unsigned int maxindex)455 static uint16_t rand_key(unsigned int index,
456                          unsigned int rindex,
457                          unsigned int maxindex) {
458     uint16_t key;
459 
460     RAND_bytes((uint8_t*)&key, sizeof(key));
461 
462     return key ?: 1; /* 0 key is not currently supported */
463 }
464 
465 keyfunc_t keyfuncs[] = {
466         inc_inc_key, inc_dec_key, dec_inc_key, dec_dec_key, same_key, rand_key,
467 };
468 
block_tree_test_etc(struct transaction * tr,unsigned int order,unsigned int count,unsigned int commit_interval,keyfunc_t keyfunc)469 static void block_tree_test_etc(struct transaction* tr,
470                                 unsigned int order,
471                                 unsigned int count,
472                                 unsigned int commit_interval,
473                                 keyfunc_t keyfunc) {
474     unsigned int i;
475     unsigned int ri;
476     uint16_t key;
477     uint16_t tmpkey;
478     unsigned int commit_count = 0;
479     struct block_tree tree = BLOCK_TREE_INITIAL_VALUE(tree);
480     struct block_tree_path path;
481     const size_t key_size = sizeof(key);
482     const size_t header_size = sizeof(struct iv) + 8;
483     const size_t child_size = sizeof(struct block_mac);
484     size_t block_size =
485             header_size + key_size * (order - 1) + child_size * order;
486 
487     if (block_size > tr->fs->dev->block_size) {
488         printf("block tree order %d does not fit in block. block size %zd > %zd, skip test\n",
489                order, block_size, tr->fs->dev->block_size);
490         return;
491     }
492 
493     block_tree_init(&tree, block_size, key_size, child_size, child_size);
494     if (commit_interval) {
495         tree.copy_on_write = true;
496         tree.allow_copy_on_write = true;
497     }
498 
499     assert(tree.key_count[0] == order - 1);
500     assert(tree.key_count[1] == order - 1);
501 
502     for (i = 1; i <= count; key++, i++) {
503         key = keyfunc(i, 0, count);
504         if (print_block_tree_test_verbose) {
505             printf("block tree order %d, insert %d:\n", order, key);
506         }
507         block_tree_insert(tr, &tree, key, i);
508         if (tr->failed) {
509             return;
510         }
511         if (commit_interval && ++commit_count == commit_interval) {
512             commit_count = 0;
513             transaction_complete(tr);
514             assert(!tr->failed);
515             transaction_activate(tr);
516         }
517         if (print_block_tree_test_verbose) {
518             printf("block tree order %d after %d inserts, last %d:\n", order, i,
519                    key);
520             block_tree_print(tr, &tree);
521         }
522     }
523     for (ri = 1, i--; i >= 1; i--, ri++) {
524         tmpkey = keyfunc(i, ri, count);
525         assert(tmpkey);
526         block_tree_walk(tr, &tree, tmpkey, false, &path);
527         key = block_tree_path_get_key(&path);
528         if (!key) {
529             key = path.entry[path.count - 1].prev_key;
530             assert(key);
531         }
532         if (key != tmpkey) {
533             block_tree_walk(tr, &tree, key, false, &path);
534         }
535         assert(key = block_tree_path_get_key(&path));
536         assert(block_tree_path_get_data(&path));
537         if (print_block_tree_test_verbose) {
538             printf("block tree order %d, remove %d (%d):\n", order, key,
539                    tmpkey);
540         }
541         block_tree_remove(tr, &tree, key, block_tree_path_get_data(&path));
542         if (tr->failed) {
543             return;
544         }
545         if (commit_interval && ++commit_count == commit_interval) {
546             commit_count = 0;
547             transaction_complete(tr);
548             assert(!tr->failed);
549             transaction_activate(tr);
550         }
551         if (print_block_tree_test_verbose) {
552             printf("block tree order %d removed %d:\n", order, key);
553             block_tree_print(tr, &tree);
554         }
555     }
556 
557     if (commit_interval) {
558         block_discard_dirty_by_block(tr->fs->dev,
559                                      block_mac_to_block(tr, &tree.root));
560         block_free(tr, block_mac_to_block(tr, &tree.root));
561     }
562 }
563 
block_tree_keyfuncs_test(struct transaction * tr,unsigned int order,unsigned int count)564 static void block_tree_keyfuncs_test(struct transaction* tr,
565                                      unsigned int order,
566                                      unsigned int count) {
567     unsigned int commit_interval;
568     unsigned int i;
569 
570     for (commit_interval = 0; commit_interval < 2; commit_interval++) {
571         for (i = 0; i < countof(keyfuncs); i++) {
572             block_tree_test_etc(tr, order, count, commit_interval, keyfuncs[i]);
573         }
574     }
575 }
576 
block_tree_test(struct transaction * tr)577 static void block_tree_test(struct transaction* tr) {
578     unsigned int order;
579 
580     block_tree_keyfuncs_test(tr, 6, 5); /* test leaf node only */
581     block_tree_keyfuncs_test(tr, 6, 10);
582 
583     for (order = 3; order <= 5; order++) {
584         block_tree_keyfuncs_test(tr, order, order - 1);
585         block_tree_keyfuncs_test(tr, order, order);
586         block_tree_keyfuncs_test(tr, order, order * 2);
587         block_tree_keyfuncs_test(tr, order, order * order);
588         block_tree_keyfuncs_test(tr, order, order * order * order);
589     }
590 }
591 
block_set_test(struct transaction * tr)592 static void block_set_test(struct transaction* tr) {
593     struct block_set sets[3];
594     unsigned int si, i;
595 
596     for (si = 0; si < countof(sets); si++) {
597         block_set_init(tr->fs, &sets[si]);
598     }
599 
600     for (i = 0; i < 10; i++) {
601         for (si = 0; si < countof(sets); si++) {
602             block_set_add_block(tr, &sets[si], 2 + i * 3 + si);
603         }
604     }
605     for (si = 0; si < countof(sets); si++) {
606         assert(!block_set_overlap(tr, &sets[si],
607                                   &sets[(si + 1) % countof(sets)]));
608     }
609     for (si = 1; si < countof(sets); si++) {
610         block_set_add_block(tr, &sets[0], 2 + 5 * 3 + si);
611     }
612     for (si = 1; si < countof(sets); si++) {
613         assert(block_set_overlap(tr, &sets[si], &sets[0]));
614         assert(block_set_overlap(tr, &sets[0], &sets[si]));
615         assert(si < 2 || !block_set_overlap(tr, &sets[si], &sets[si - 1]));
616     }
617 }
618 
block_tree_allocate_all_test(struct transaction * tr)619 static void block_tree_allocate_all_test(struct transaction* tr) {
620     unsigned int i;
621 
622     for (i = 0; i < countof(keyfuncs); i++) {
623         assert(!tr->failed);
624         block_tree_test_etc(tr, 3, UINT_MAX, 0, keyfuncs[i]);
625         assert(tr->failed);
626         transaction_complete(tr);
627         transaction_activate(tr);
628     }
629 }
block_map_test(struct transaction * tr)630 static void block_map_test(struct transaction* tr) {
631     unsigned int i;
632     struct block_mac block_mac = BLOCK_MAC_INITIAL_VALUE(block_mac);
633     struct block_map block_map = BLOCK_MAP_INITIAL_VALUE(block_map);
634 
635     block_map_init(tr, &block_map, &block_mac, 128);
636 
637     for (i = 1; i <= 100; i++) {
638         block_mac_set_block(tr, &block_mac, block_allocate(tr));
639         block_map_set(tr, &block_map, i, &block_mac);
640     }
641     for (; i >= 2; i /= 2) {
642         block_map_truncate(tr, &block_map, i);
643         assert(!block_map_get(tr, &block_map, i, &block_mac));
644         assert(block_map_get(tr, &block_map, i - 1, &block_mac));
645     }
646     block_map_free(tr, &block_map);
647 }
648 
free_frag_etc_test(struct transaction * tr,int start,int end,int inc)649 static void free_frag_etc_test(struct transaction* tr,
650                                int start,
651                                int end,
652                                int inc) {
653     int i;
654 
655     for (i = start; i < end; i += inc) {
656         block_free(tr, i);
657         assert(!tr->failed);
658     }
659 }
660 
allocate_2_transactions_test_etc(struct transaction * tr,data_block_t blocks1[],size_t blocks1_count,data_block_t blocks2[],size_t blocks2_count)661 static void allocate_2_transactions_test_etc(struct transaction* tr,
662                                              data_block_t blocks1[],
663                                              size_t blocks1_count,
664                                              data_block_t blocks2[],
665                                              size_t blocks2_count) {
666     unsigned int i;
667     struct transaction tr1;
668     struct transaction tr2;
669     size_t blocks_max_count = MAX(blocks1_count, blocks2_count);
670 
671     transaction_init(&tr1, tr->fs, true);
672     transaction_init(&tr2, tr->fs, true);
673 
674     for (i = 0; i < blocks_max_count; i++) {
675         if (i < blocks1_count) {
676             blocks1[i] = block_allocate(&tr1);
677         }
678         if (i < blocks2_count) {
679             blocks2[i] = block_allocate(&tr2);
680         }
681     }
682     assert(!tr1.failed);
683     assert(!tr2.failed);
684 
685     transaction_complete(&tr1);
686 
687     assert(!tr1.failed);
688     assert(!tr2.failed);
689 
690     for (i = 0; i < blocks1_count; i++) {
691         assert(!block_set_block_in_set(tr, &tr->fs->free, blocks1[i]));
692     }
693     for (i = 0; i < blocks2_count; i++) {
694         assert(block_set_block_in_set(tr, &tr->fs->free, blocks2[i]));
695     }
696 
697     transaction_complete(&tr2);
698 
699     for (i = 0; i < blocks1_count; i++) {
700         assert(!block_set_block_in_set(tr, &tr->fs->free, blocks1[i]));
701     }
702     for (i = 0; i < blocks2_count; i++) {
703         assert(!block_set_block_in_set(tr, &tr->fs->free, blocks2[i]));
704     }
705 
706     assert(!block_cache_debug_get_ref_block_count());
707 #if FULL_ASSERT
708     check_fs_prepare(tr);
709     for (i = 0; i < blocks1_count; i++) {
710         block_set_used_by(blocks1[i], "allocated", 0);
711     }
712     for (i = 0; i < blocks2_count; i++) {
713         block_set_used_by(blocks2[i], "allocated2", 0);
714     }
715     assert(check_fs_finish(tr));
716 #endif
717 
718     transaction_free(&tr1);
719     transaction_free(&tr2);
720 }
721 
free_test_etc(struct transaction * tr,data_block_t blocks1[],size_t blocks1_count,data_block_t blocks2[],size_t blocks2_count)722 static void free_test_etc(struct transaction* tr,
723                           data_block_t blocks1[],
724                           size_t blocks1_count,
725                           data_block_t blocks2[],
726                           size_t blocks2_count) {
727     unsigned int i;
728     struct transaction tr1;
729     struct transaction tr2;
730     size_t blocks_max_count = MAX(blocks1_count, blocks2_count);
731 
732     transaction_init(&tr1, tr->fs, true);
733     transaction_init(&tr2, tr->fs, true);
734 
735     for (i = 0; i < blocks_max_count; i++) {
736         if (i < blocks1_count) {
737             block_free(&tr1, blocks1[i]);
738             if (print_test_verbose) {
739                 printf("tr1.freed after free %" PRIu64 ":\n", blocks1[i]);
740                 block_set_print(&tr1, &tr1.freed);
741             }
742         }
743         if (i < blocks2_count) {
744             block_free(&tr2, blocks1[i]);
745             if (print_test_verbose) {
746                 printf("tr2.freed after free %" PRIu64 ":\n", blocks2[i]);
747                 block_set_print(&tr2, &tr2.freed);
748             }
749         }
750     }
751     assert(!tr1.failed);
752     assert(!tr2.failed);
753 
754     transaction_complete(&tr1);
755 
756     assert(!tr1.failed);
757 
758     for (i = 0; i < blocks1_count; i++) {
759         assert(block_set_block_in_set(tr, &tr->fs->free, blocks1[i]));
760     }
761 
762     if (blocks1 == blocks2) {
763         /* free conflict test */
764         assert(tr2.failed);
765     }
766     transaction_complete(&tr2);
767 
768     if (blocks1 != blocks2) {
769         assert(!tr2.failed);
770         for (i = 0; i < blocks2_count; i++) {
771             assert(block_set_block_in_set(tr, &tr->fs->free, blocks2[i]));
772         }
773     }
774 
775     assert(!block_cache_debug_get_ref_block_count());
776 
777     transaction_free(&tr1);
778     transaction_free(&tr2);
779 }
780 
781 /* clang-format off */
782 enum {
783     test_free_start = BLOCK_SIZE > 112 ? 20 : 200,
784     test_free_split = test_free_start + (BLOCK_SIZE > 112 ? 60 : 20),
785     test_free_end = BLOCK_SIZE > 96 ? BLOCK_COUNT
786                                     : BLOCK_SIZE > 80 ? BLOCK_COUNT - 8
787                                                       : BLOCK_COUNT - 16,
788     test_free_increment = BLOCK_SIZE > 64 ? 2 : 20,
789 
790     allocated_size = BLOCK_SIZE > 128 ? 40 : 10,
791     allocated2_size = BLOCK_SIZE > 64 ? allocated_size : 4,
792 };
793 /* clang-format on */
794 static data_block_t allocated[allocated_size];
795 static data_block_t allocated2[allocated2_size];
796 
allocate_frag_test(struct transaction * tr)797 static void allocate_frag_test(struct transaction* tr) {
798     int i;
799     struct block_range range;
800 
801     range.start = test_free_start;
802     range.end = test_free_end;
803     block_set_add_range(tr, &tr->allocated, range);
804     assert(!block_cache_debug_get_ref_block_count());
805 
806     for (i = test_free_start; i + 1 < test_free_end; i += test_free_increment) {
807         if (print_test_verbose) {
808             printf("%s: remove block %d\n", __func__, i);
809         }
810         range.start = i + 1;
811         range.end = i + test_free_increment;
812         if (range.end > test_free_end) {
813             range.end = test_free_end;
814         }
815         block_set_remove_range(tr, &tr->allocated, range);
816         assert(!block_cache_debug_get_ref_block_count());
817         assert(!tr->failed);
818     }
819 
820     if (print_test_verbose) {
821         printf("%s: tr.tmp_allocated:\n", __func__);
822         block_set_print(tr, &tr->tmp_allocated);
823         printf("%s: tr.allocated:\n", __func__);
824         block_set_print(tr, &tr->allocated);
825     }
826 
827     assert(!tr->failed);
828     assert(!block_cache_debug_get_ref_block_count());
829     transaction_complete(tr);
830     assert(!tr->failed);
831     transaction_activate(tr);
832     if (print_test_verbose) {
833         printf("%s: free state after transaction complete:\n", __func__);
834         block_set_print(tr, &tr->fs->free);
835     }
836     assert(!block_cache_debug_get_ref_block_count());
837 #if FULL_ASSERT
838     check_fs_prepare(tr);
839     for (i = test_free_start; i < test_free_end; i += test_free_increment) {
840         block_set_used_by(i, "test_free_fragmentation", 0);
841     }
842     assert(check_fs_finish(tr));
843 #endif
844 }
845 
allocate_free_same_test(struct transaction * tr)846 static void allocate_free_same_test(struct transaction* tr) {
847     unsigned int i;
848     printf("%s: start allocate then free same test\n", __func__);
849     for (i = 0; i < countof(allocated); i++) {
850         allocated[i] = block_allocate(tr);
851         assert(!tr->failed);
852     }
853     if (print_test_verbose) {
854         printf("%s: tr.tmp_allocated:\n", __func__);
855         block_set_print(tr, &tr->tmp_allocated);
856         printf("%s: tr.allocated:\n", __func__);
857         block_set_print(tr, &tr->allocated);
858     }
859 
860     for (i = 0; i < countof(allocated); i++) {
861         block_free(tr, allocated[i]);
862     }
863     assert(!tr->failed);
864     transaction_complete(tr);
865     assert(!tr->failed);
866     transaction_activate(tr);
867     assert(block_set_check(tr, &tr->fs->free));
868     assert(!block_cache_debug_get_ref_block_count());
869 #if FULL_ASSERT
870     check_fs_prepare(tr);
871     for (i = test_free_start; i < test_free_end; i += test_free_increment) {
872         block_set_used_by(i, "test_free_fragmentation", 0);
873     }
874     assert(check_fs_finish(tr));
875 #endif
876     printf("%s: start allocate then free same test, done\n", __func__);
877 }
878 
allocate_free_other_test(struct transaction * tr)879 static void allocate_free_other_test(struct transaction* tr) {
880     unsigned int i;
881 
882     printf("%s: start allocate then free some other test\n", __func__);
883     for (i = 0; i < countof(allocated); i++) {
884         allocated[i] = block_allocate(tr);
885         if (print_test_verbose) {
886             printf("tr.tmp_allocated after allocate %d, %" PRIu64 ":\n", i,
887                    allocated[i]);
888             block_set_print(tr, &tr->tmp_allocated);
889             printf("tr.allocated after allocate %d, %" PRIu64 ":\n", i,
890                    allocated[i]);
891             block_set_print(tr, &tr->allocated);
892         }
893         assert(!tr->failed);
894     }
895     for (i = test_free_start; i < test_free_split; i += test_free_increment) {
896         block_free(tr, i);
897         if (print_test_verbose) {
898             printf("tr.freed after free %d:\n", i);
899             block_set_print(tr, &tr->freed);
900         }
901         assert(!tr->failed);
902     }
903     if (print_test_verbose) {
904         printf("fs.super->free:\n");
905         block_set_print(tr, &tr->fs->free);
906     }
907     assert(!tr->failed);
908     transaction_complete(tr);
909     assert(!tr->failed);
910     transaction_activate(tr);
911     assert(block_set_check(tr, &tr->fs->free));
912 
913     for (i = 0; i < countof(allocated); i++) {
914         assert(!block_set_block_in_set(tr, &tr->fs->free, allocated[i]));
915     }
916     for (i = test_free_start; i < test_free_split; i += test_free_increment) {
917         assert(block_set_block_in_set(tr, &tr->fs->free, i));
918     }
919     assert(!block_cache_debug_get_ref_block_count());
920 #if FULL_ASSERT
921     check_fs_prepare(tr);
922     for (i = test_free_split; i < test_free_end; i += test_free_increment) {
923         block_set_used_by(i, "test_free_fragmentation", 0);
924     }
925     for (i = 0; i < countof(allocated); i++) {
926         block_set_used_by(allocated[i], "allocated", 0);
927     }
928     assert(check_fs_finish(tr));
929 #endif
930     printf("%s: start allocate then free some other test, done\n", __func__);
931 }
932 
free_frag_rem_test(struct transaction * tr)933 static void free_frag_rem_test(struct transaction* tr) {
934     int i;
935     printf("%s: start free rem test\n", __func__);
936     free_frag_etc_test(tr, test_free_split, test_free_end, test_free_increment);
937     transaction_complete(tr);
938     assert(!tr->failed);
939     transaction_activate(tr);
940     assert(block_set_check(tr, &tr->fs->free));
941     for (i = test_free_split; i < test_free_end; i += test_free_increment) {
942         assert(block_set_block_in_set(tr, &tr->fs->free, i));
943     }
944     assert(!block_cache_debug_get_ref_block_count());
945     full_assert(check_fs_allocated(tr, allocated, countof(allocated)));
946     printf("%s: start free rem test, done\n", __func__);
947 }
948 
free_test(struct transaction * tr)949 static void free_test(struct transaction* tr) {
950     unsigned int i;
951 
952     free_test_etc(tr, allocated, countof(allocated), NULL, 0);
953 
954     i = 0;
955     do {
956         // TODO: use this version, currently does not work since ranges aer not
957         // merged accross nodes
958         // i = block_set_find_next_block(&fs.free, i, false);
959         while (block_set_find_next_block(tr, &tr->fs->free, i, true) == i) {
960             i++;
961         }
962         int free = block_set_find_next_block(tr, &tr->fs->free, i, true);
963         printf("not free: [%d-%d]\n", i, free - 1);
964         i = free;
965     } while (i);
966 }
967 
allocate_2_transactions_test(struct transaction * tr)968 static void allocate_2_transactions_test(struct transaction* tr) {
969     printf("%s: start allocate 2 transactions test\n", __func__);
970     allocate_2_transactions_test_etc(tr, allocated, countof(allocated),
971                                      allocated2, countof(allocated2));
972     printf("%s: allocate 2 transactions test done\n", __func__);
973 }
974 
free_2_transactions_same_test(struct transaction * tr)975 static void free_2_transactions_same_test(struct transaction* tr) {
976     printf("%s: start free 2 transactions same test\n", __func__);
977     free_test_etc(tr, allocated, countof(allocated), allocated,
978                   countof(allocated));
979     full_assert(check_fs_allocated(tr, allocated2, countof(allocated2)));
980     printf("%s: free 2 transactions same test done\n", __func__);
981 }
982 
free_2_transactions_same_test_2(struct transaction * tr)983 static void free_2_transactions_same_test_2(struct transaction* tr) {
984     printf("%s: start free 2 transactions same test 2\n", __func__);
985     free_test_etc(tr, allocated2, countof(allocated2), allocated2,
986                   countof(allocated2));
987     full_assert(check_fs(tr));
988     printf("%s: free 2 transactions same test 2 done\n", __func__);
989 }
990 
allocate_all_test(struct transaction * tr)991 static void allocate_all_test(struct transaction* tr) {
992     while (block_allocate(tr)) {
993         assert(!tr->failed);
994     }
995     assert(tr->failed);
996     transaction_complete(tr);
997     transaction_activate(tr);
998 }
999 
super_block_write_failure_test(struct transaction * tr)1000 static void super_block_write_failure_test(struct transaction* tr) {
1001     data_block_t block1 = block_allocate(tr);
1002     /* trigger a superblock write failure */
1003     block_test_fail_write_blocks = 2;
1004     transaction_complete(tr);
1005     block_test_fail_write_blocks = 0;
1006     assert(tr->failed);
1007     transaction_activate(tr);
1008     assert(block_allocate(tr) == block1);
1009     transaction_complete(tr);
1010     transaction_activate(tr);
1011     block_free(tr, block1);
1012 }
1013 
1014 /* Test that block_put_dirty_discard actually drops the reference */
block_put_dirty_discard_test(struct transaction * tr)1015 static void block_put_dirty_discard_test(struct transaction* tr) {
1016     struct obj_ref super_ref = OBJ_REF_INITIAL_VALUE(super_ref);
1017     struct fs* fs = tr->fs;
1018     const void* super_ro;
1019     uint32_t* super_rw;
1020     data_block_t block;
1021 
1022     block = tr->fs->super_block[fs->super_block_version & 1];
1023     super_ro = block_get_super(fs, block, &super_ref);
1024     assert(super_ro);
1025     super_rw = block_dirty(tr, super_ro, false);
1026     assert(super_rw);
1027     /*
1028      * As part of dropping the dirty block we need to clear it from the block
1029      * cache with block_cache_entry_discard_dirty, which requires that the block
1030      * not have any active references. Verify that block_put_dirty_discard drops
1031      * super_ref before trying to drop the block itself.
1032      */
1033     block_put_dirty_discard(super_rw, &super_ref);
1034 }
1035 
open_test_file_etc(struct transaction * tr,struct storage_file_handle * file,const char * path,enum file_create_mode create,enum file_op_result expected_result)1036 static void open_test_file_etc(struct transaction* tr,
1037                                struct storage_file_handle* file,
1038                                const char* path,
1039                                enum file_create_mode create,
1040                                enum file_op_result expected_result) {
1041     enum file_op_result result;
1042     result = file_open(tr, path, file, create, allow_repaired);
1043     if (print_test_verbose) {
1044         printf("%s: lookup file %s, create %d, got %" PRIu64 ":\n", __func__,
1045                path, create, block_mac_to_block(tr, &file->block_mac));
1046     }
1047 
1048     assert(result == expected_result);
1049     assert(result != FILE_OP_SUCCESS || block_mac_valid(tr, &file->block_mac));
1050 }
1051 
open_test_file(struct transaction * tr,struct storage_file_handle * file,const char * path,enum file_create_mode create)1052 static void open_test_file(struct transaction* tr,
1053                            struct storage_file_handle* file,
1054                            const char* path,
1055                            enum file_create_mode create) {
1056     open_test_file_etc(tr, file, path, create, FILE_OP_SUCCESS);
1057 }
1058 
file_allocate_all_test(struct transaction * master_tr,unsigned int tr_count,int success_count,int step_size,const char * path,enum file_create_mode create)1059 static void file_allocate_all_test(struct transaction* master_tr,
1060                                    unsigned int tr_count,
1061                                    int success_count,
1062                                    int step_size,
1063                                    const char* path,
1064                                    enum file_create_mode create) {
1065     unsigned int i;
1066     unsigned int j;
1067     unsigned int done;
1068     unsigned int count;
1069     struct storage_file_handle file[tr_count];
1070     data_block_t file_size[tr_count];
1071     struct transaction tr[tr_count];
1072     int written_count[tr_count];
1073     size_t file_block_size = master_tr->fs->dev->block_size - sizeof(struct iv);
1074     void* block_data_rw;
1075     struct obj_ref ref = OBJ_REF_INITIAL_VALUE(ref);
1076 
1077     for (i = 0; i < tr_count; i++) {
1078         transaction_init(&tr[i], master_tr->fs, true);
1079     }
1080 
1081     for (count = INT_MAX, done = 0; count > 0; count -= step_size) {
1082         for (i = 0; i < tr_count; i++) {
1083             open_test_file(&tr[i], &file[i], path, create);
1084             file_size[i] = file[i].size;
1085             written_count[i] = 0;
1086         }
1087 
1088         for (j = 0, done = 0; done != tr_count && j < count; j++) {
1089             for (i = 0, done = 0; i < tr_count; i++) {
1090                 if (tr[i].failed) {
1091                     done++;
1092                     assert(j);
1093                     continue;
1094                 }
1095                 block_data_rw =
1096                         file_get_block_write(&tr[i], &file[i], j, true, &ref);
1097                 if (!block_data_rw) {
1098                     done++;
1099                     continue;
1100                 }
1101                 assert(!tr[i].failed);
1102                 file_block_put_dirty(&tr[i], &file[i], j, block_data_rw, &ref);
1103                 written_count[i] = j + 1;
1104             }
1105         }
1106         for (i = 0, done = 0; i < tr_count; i++) {
1107             file_set_size(&tr[i], &file[i], written_count[i] * file_block_size);
1108             if (count == INT_MAX) {
1109                 assert(tr[i].failed);
1110             }
1111             transaction_complete(&tr[i]);
1112             if (!tr[i].failed) {
1113                 assert(file[i].size == written_count[i] * file_block_size);
1114                 done++;
1115             } else if (!done) {
1116                 assert(file_size[i] == file[i].size);
1117             }
1118             file_close(&file[i]);
1119             transaction_activate(&tr[i]);
1120         }
1121         if (count == INT_MAX) {
1122             count = j;
1123         }
1124         if (success_count && done) {
1125             success_count--;
1126         }
1127         if (!success_count) {
1128             break;
1129         }
1130         if (done) {
1131             file_delete(&tr[0], path, false);
1132             transaction_complete(&tr[0]);
1133             assert(!tr[0].failed);
1134             transaction_activate(&tr[0]);
1135         }
1136     }
1137     assert(!success_count);
1138     for (i = 0; i < tr_count; i++) {
1139         transaction_complete(&tr[i]);
1140         transaction_free(&tr[i]);
1141     }
1142 }
1143 
file_create_all_test(struct transaction * tr)1144 static void file_create_all_test(struct transaction* tr) {
1145     int i;
1146     char path[4 + 8 + 1];
1147     struct storage_file_handle file;
1148 
1149     enum file_op_result result;
1150     for (i = 0;; i++) {
1151         snprintf(path, sizeof(path), "test%08x", i);
1152         result = file_open(tr, path, &file, FILE_OPEN_CREATE_EXCLUSIVE, false);
1153         if (result != FILE_OP_SUCCESS) {
1154             break;
1155         }
1156         file_close(&file);
1157         assert(!tr->failed);
1158     }
1159 
1160     assert(tr->failed);
1161     transaction_complete(tr);
1162     transaction_activate(tr);
1163 }
1164 
1165 /* run tests on already open file */
file_test_open(struct transaction * tr,struct storage_file_handle * file,int allocate,int read,int free,int id)1166 static void file_test_open(struct transaction* tr,
1167                            struct storage_file_handle* file,
1168                            int allocate,
1169                            int read,
1170                            int free,
1171                            int id) {
1172     int i;
1173     int* block_data_rw;
1174     struct obj_ref ref = OBJ_REF_INITIAL_VALUE(ref);
1175     const int* block_data_ro;
1176     size_t file_block_size = tr->fs->dev->block_size - sizeof(struct iv);
1177 
1178     if (allocate) {
1179         for (i = 0; i < allocate; i++) {
1180             block_data_rw = file_get_block_write(tr, file, i, true, &ref);
1181             if (print_test_verbose) {
1182                 printf("%s: allocate file block %d, %" PRIu64 ":\n", __func__,
1183                        i, data_to_block_num(block_data_rw));
1184             }
1185             assert(block_data_rw);
1186             /* TODO: store iv in file block map */
1187             block_data_rw = (void*)block_data_rw + sizeof(struct iv);
1188             // block_data_rw = block_get_cleared(block)+ sizeof(struct iv);
1189             block_data_rw[0] = i;
1190             block_data_rw[1] = ~i;
1191             block_data_rw[2] = id;
1192             block_data_rw[3] = ~id;
1193             file_block_put_dirty(tr, file, i,
1194                                  (void*)block_data_rw - sizeof(struct iv),
1195                                  &ref);
1196         }
1197         if (file->size < i * file_block_size) {
1198             file_set_size(tr, file, i * file_block_size);
1199         }
1200         assert(file->size >= i * file_block_size);
1201         if (print_test_verbose) {
1202             printf("%s: allocated %d file blocks\n", __func__, i);
1203             file_print(tr, file);
1204         }
1205     }
1206 
1207     if (read) {
1208         for (i = 0;; i++) {
1209             block_data_ro = file_get_block(tr, file, i, &ref);
1210             if (!block_data_ro) {
1211                 break;
1212             }
1213             if (print_test_verbose) {
1214                 printf("%s: found file block %d, %" PRIu64 ":\n", __func__, i,
1215                        data_to_block_num(block_data_ro));
1216             }
1217             block_data_ro = (void*)block_data_ro + sizeof(struct iv);
1218             assert(block_data_ro[0] == i);
1219             assert(block_data_ro[1] == ~i);
1220             assert(block_data_ro[2] == id);
1221             assert(block_data_ro[3] == ~id);
1222             file_block_put((void*)block_data_ro - sizeof(struct iv), &ref);
1223         }
1224         assert(i == read);
1225         assert(file->size >= i * file_block_size);
1226     }
1227 
1228     if (free) {
1229         file_set_size(tr, file, 0);
1230         for (i = 0; i < free; i++) {
1231             block_data_ro = file_get_block(tr, file, i, &ref);
1232             if (block_data_ro) {
1233                 file_block_put(block_data_ro, &ref);
1234                 printf("%s: file block %d, %" PRIu64 " not deleted\n", __func__,
1235                        i, data_to_block_num(block_data_ro));
1236                 break;
1237             }
1238         }
1239         if (print_test_verbose) {
1240             printf("%s: deleted %d file blocks\n", __func__, i);
1241             file_print(tr, file);
1242         }
1243         assert(i == free);
1244     }
1245 }
1246 
file_test_commit(struct transaction * tr,bool commit)1247 static void file_test_commit(struct transaction* tr, bool commit) {
1248     if (commit) {
1249         transaction_complete(tr);
1250         assert(!tr->failed);
1251         transaction_activate(tr);
1252     }
1253 }
1254 
file_move_expect(struct transaction * tr,const char * src_path,enum file_create_mode src_create,const char * dest_path,enum file_create_mode dest_create,enum file_op_result expected_result)1255 static void file_move_expect(struct transaction* tr,
1256                              const char* src_path,
1257                              enum file_create_mode src_create,
1258                              const char* dest_path,
1259                              enum file_create_mode dest_create,
1260                              enum file_op_result expected_result) {
1261     struct storage_file_handle file;
1262     enum file_op_result res;
1263 
1264     assert(!tr->failed);
1265 
1266     open_test_file(tr, &file, src_path, src_create);
1267 
1268     res = file_move(tr, &file, dest_path, dest_create, false);
1269     assert(res == expected_result);
1270     assert(!tr->failed);
1271     file_close(&file);
1272 }
1273 
file_move_expect_success(struct transaction * tr,const char * src_path,enum file_create_mode src_create,const char * dest_path,enum file_create_mode dest_create)1274 static void file_move_expect_success(struct transaction* tr,
1275                                      const char* src_path,
1276                                      enum file_create_mode src_create,
1277                                      const char* dest_path,
1278                                      enum file_create_mode dest_create) {
1279     file_move_expect(tr, src_path, src_create, dest_path, dest_create,
1280                      FILE_OP_SUCCESS);
1281 }
1282 
file_test_etc(struct transaction * tr,bool commit,const char * path,enum file_create_mode create,const char * move_path,enum file_create_mode move_create,int allocate,int read,int free,bool delete,int id)1283 static void file_test_etc(struct transaction* tr,
1284                           bool commit,
1285                           const char* path,
1286                           enum file_create_mode create,
1287                           const char* move_path,
1288                           enum file_create_mode move_create,
1289                           int allocate,
1290                           int read,
1291                           int free,
1292                           bool delete,
1293                           int id) {
1294     enum file_op_result delete_res;
1295     struct storage_file_handle file;
1296 
1297     open_test_file(tr, &file, path, create);
1298 
1299     file_test_commit(tr, commit);
1300 
1301     if (move_path) {
1302         file_move(tr, &file, move_path, move_create, allow_repaired);
1303         file_test_commit(tr, commit);
1304         path = move_path;
1305     }
1306     file_test_open(tr, &file, allocate, read, free, id);
1307     file_test_commit(tr, commit);
1308 
1309     if (delete) {
1310         if (print_test_verbose) {
1311             printf("%s: delete file %s, at %" PRIu64 ":\n", __func__, path,
1312                    block_mac_to_block(tr, &file.block_mac));
1313         }
1314         delete_res = file_delete(tr, path, allow_repaired);
1315         file_test_commit(tr, commit);
1316         assert(delete_res == FILE_OP_SUCCESS);
1317     }
1318 
1319     file_close(&file);
1320 }
1321 
file_test(struct transaction * tr,const char * path,enum file_create_mode create,int allocate,int read,int free,bool delete,int id)1322 static void file_test(struct transaction* tr,
1323                       const char* path,
1324                       enum file_create_mode create,
1325                       int allocate,
1326                       int read,
1327                       int free,
1328                       bool delete,
1329                       int id) {
1330     file_test_etc(tr, false, path, create, NULL, FILE_OPEN_NO_CREATE, allocate,
1331                   read, free, delete, id);
1332 }
1333 
file_test_split_tr(struct transaction * tr,const char * path,enum file_create_mode create,int open_count,int allocate_file_index,int allocate_index,int allocate,int read,int free,bool delete,int id)1334 static void file_test_split_tr(struct transaction* tr,
1335                                const char* path,
1336                                enum file_create_mode create,
1337                                int open_count,
1338                                int allocate_file_index,
1339                                int allocate_index,
1340                                int allocate,
1341                                int read,
1342                                int free,
1343                                bool delete,
1344                                int id) {
1345     enum file_op_result delete_res;
1346     int i;
1347     struct storage_file_handle file[open_count];
1348 
1349     assert(allocate_file_index <= allocate_index);
1350     assert(allocate_index < open_count);
1351 
1352     for (i = 0; i < open_count; i++) {
1353         open_test_file(tr, &file[i], path,
1354                        (i == 0) ? create : FILE_OPEN_NO_CREATE);
1355         assert(file[i].size == 0 || i > allocate_index);
1356         if (i == allocate_index) {
1357             file_test_open(tr, &file[allocate_file_index], allocate, 0, 0, id);
1358         }
1359     }
1360 
1361     if (create == FILE_OPEN_NO_CREATE) {
1362         transaction_fail(tr);
1363         transaction_activate(tr);
1364         for (i = 0; i < open_count; i++) {
1365             assert(file[i].size == 0);
1366         }
1367 
1368         file_test_open(tr, &file[allocate_file_index], allocate, 0, 0, id);
1369     }
1370 
1371     assert(!tr->failed);
1372     transaction_complete(tr);
1373     assert(!tr->failed);
1374     transaction_activate(tr);
1375 
1376     for (i = 0; i < open_count; i++) {
1377         assert(file[i].size != 0);
1378     }
1379 
1380     for (i = 1; i < open_count; i++) {
1381         file_test_open(tr, &file[i], 0, read, 0, id);
1382         assert(!tr->failed);
1383         transaction_complete(tr);
1384         assert(!tr->failed);
1385         transaction_activate(tr);
1386     }
1387 
1388     file_test_open(tr, &file[0], 0, read, free, id);
1389 
1390     if (delete) {
1391         if (print_test_verbose) {
1392             printf("%s: delete file %s, at %" PRIu64 ":\n", __func__, path,
1393                    block_mac_to_block(tr, &file[i].block_mac));
1394         }
1395         delete_res = file_delete(tr, path, false);
1396         assert(delete_res == FILE_OP_SUCCESS);
1397     }
1398 
1399     for (i = 0; i < open_count; i++) {
1400         file_close(&file[i]);
1401     }
1402 }
1403 
file_read_after_delete_test(struct transaction * tr)1404 static void file_read_after_delete_test(struct transaction* tr) {
1405     const char* path = "test1s";
1406     struct storage_file_handle file;
1407     struct storage_file_handle file2;
1408     struct obj_ref ref = OBJ_REF_INITIAL_VALUE(ref);
1409     const void* block_data_ro;
1410     void* block_data_rw;
1411     struct transaction tr2;
1412 
1413     transaction_init(&tr2, tr->fs, true);
1414 
1415     /* create test file */
1416     open_test_file(tr, &file, path, FILE_OPEN_CREATE_EXCLUSIVE);
1417     block_data_rw = file_get_block_write(tr, &file, 0, false, &ref);
1418     file_block_put_dirty(tr, &file, 0, block_data_rw, &ref);
1419     transaction_complete(tr);
1420     assert(!tr->failed);
1421 
1422     /* open in second transaction */
1423     open_test_file(&tr2, &file2, path, FILE_OPEN_NO_CREATE);
1424 
1425     /* delete and try read in same transaction */
1426     transaction_activate(tr);
1427     file_delete(tr, path, false);
1428     block_data_ro = file_get_block(tr, &file, 0, &ref);
1429     assert(!block_data_ro);
1430     assert(tr->failed);
1431 
1432     /* read in second transaction */
1433     block_data_ro = file_get_block(&tr2, &file2, 0, &ref);
1434     assert(block_data_ro);
1435     file_block_put(block_data_ro, &ref);
1436 
1437     /* read file then delete file */
1438     transaction_activate(tr);
1439     block_data_ro = file_get_block(tr, &file, 0, &ref);
1440     assert(block_data_ro);
1441     file_block_put(block_data_ro, &ref);
1442 
1443     file_delete(tr, path, false);
1444     transaction_complete(tr);
1445     assert(!tr->failed);
1446 
1447     /* try to read in both transactions */
1448     transaction_activate(tr);
1449     block_data_ro = file_get_block(tr, &file, 0, &ref);
1450     assert(!block_data_ro);
1451     assert(tr->failed);
1452 
1453     block_data_ro = file_get_block(&tr2, &file2, 0, &ref);
1454     assert(!block_data_ro);
1455     assert(tr2.failed);
1456 
1457     file_close(&file);
1458     file_close(&file2);
1459 
1460     transaction_activate(tr);
1461     transaction_free(&tr2);
1462 }
1463 
1464 static const int file_test_block_count = BLOCK_SIZE > 64 ? 40 : 10;
1465 static const int file_test_many_file_count = BLOCK_SIZE > 80 ? 40 : 10;
1466 
file_create1_small_test(struct transaction * tr)1467 static void file_create1_small_test(struct transaction* tr) {
1468     file_test(tr, "test1s", FILE_OPEN_CREATE_EXCLUSIVE, 0, 0, 0, false, 1);
1469 }
1470 
file_write1_small_test(struct transaction * tr)1471 static void file_write1_small_test(struct transaction* tr) {
1472     file_test(tr, "test1s", FILE_OPEN_NO_CREATE, 1, 0, 0, false, 1);
1473 }
1474 
file_delete1_small_test(struct transaction * tr)1475 static void file_delete1_small_test(struct transaction* tr) {
1476     file_test(tr, "test1s", FILE_OPEN_NO_CREATE, 0, 1, 1, true, 1);
1477 }
1478 
file_create_write_delete1_small_test(struct transaction * tr)1479 static void file_create_write_delete1_small_test(struct transaction* tr) {
1480     file_test(tr, "test1s", FILE_OPEN_CREATE_EXCLUSIVE, 1, 1, 1, true, 1);
1481 }
1482 
file_splittr1_small_test(struct transaction * tr)1483 static void file_splittr1_small_test(struct transaction* tr) {
1484     file_test_split_tr(tr, "test1s", FILE_OPEN_NO_CREATE, 1, 0, 0, 1, 1, 0,
1485                        false, 1);
1486 }
1487 
file_splittr1o4_small_test(struct transaction * tr)1488 static void file_splittr1o4_small_test(struct transaction* tr) {
1489 #if 0
1490     /*
1491      * Disabled test: Current file code does not allow having the same
1492      * file open more than once per transaction.
1493      */
1494     file_test_split_tr(tr, "test1s", FILE_OPEN_NO_CREATE, 4, 1, 2, 1, 1, 0, false, 1);
1495 #else
1496     struct storage_file_handle file[2];
1497     open_test_file_etc(tr, &file[0], "test1s", FILE_OPEN_NO_CREATE,
1498                        FILE_OP_SUCCESS);
1499     open_test_file_etc(tr, &file[1], "test1s", FILE_OPEN_NO_CREATE,
1500                        FILE_OP_ERR_ALREADY_OPEN);
1501     file_close(&file[0]);
1502     file_splittr1_small_test(tr);
1503 #endif
1504 }
1505 
file_splittr1c_small_test(struct transaction * tr)1506 static void file_splittr1c_small_test(struct transaction* tr) {
1507     file_test_split_tr(tr, "test1s", FILE_OPEN_CREATE_EXCLUSIVE, 1, 0, 0, 1, 1,
1508                        1, true, 1);
1509 }
1510 
file_splittr1o4c_small_test(struct transaction * tr)1511 static void file_splittr1o4c_small_test(struct transaction* tr) {
1512 #if 0
1513     /*
1514      * Disabled test: Current file code does not allow having the same
1515      * file open more than once per transaction.
1516      */
1517     file_test_split_tr(tr, "test1s", FILE_OPEN_CREATE_EXCLUSIVE, 4, 1, 2, 1, 1, 1, true, 1);
1518 #endif
1519 }
1520 
file_splittr1o4cl_small_test(struct transaction * tr)1521 static void file_splittr1o4cl_small_test(struct transaction* tr) {
1522 #if 0
1523     /*
1524      * Disabled test: Current file code does not allow having the same
1525      * file open more than once per transaction.
1526      */
1527     file_test_split_tr(tr, "test1s", FILE_OPEN_CREATE_EXCLUSIVE, 4, 2, 3, 1, 1, 1, true, 1);
1528 #endif
1529 }
1530 
file_create1_test(struct transaction * tr)1531 static void file_create1_test(struct transaction* tr) {
1532     file_test(tr, "test1", FILE_OPEN_CREATE_EXCLUSIVE, 0, 0, 0, false, 1);
1533 }
1534 
file_write1h_test(struct transaction * tr)1535 static void file_write1h_test(struct transaction* tr) {
1536     file_test(tr, "test1", FILE_OPEN_NO_CREATE, file_test_block_count / 2, 0, 0,
1537               false, 1);
1538 }
1539 
file_write1_test(struct transaction * tr)1540 static void file_write1_test(struct transaction* tr) {
1541     file_test(tr, "test1", FILE_OPEN_NO_CREATE, file_test_block_count, 0, 0,
1542               false, 1);
1543 }
1544 
file_delete1_test(struct transaction * tr)1545 static void file_delete1_test(struct transaction* tr) {
1546     file_test(tr, "test1", FILE_OPEN_NO_CREATE, 0, file_test_block_count,
1547               file_test_block_count, true, 1);
1548 }
1549 
file_delete_create_write1_test(struct transaction * tr)1550 static void file_delete_create_write1_test(struct transaction* tr) {
1551     file_test(tr, "test1", FILE_OPEN_NO_CREATE, 0, file_test_block_count,
1552               file_test_block_count, true, 1);
1553     file_test(tr, "test1", FILE_OPEN_CREATE_EXCLUSIVE, file_test_block_count, 0,
1554               0, false, 1);
1555 }
1556 
file_delete1_no_free_test(struct transaction * tr)1557 static void file_delete1_no_free_test(struct transaction* tr) {
1558     file_test(tr, "test1", FILE_OPEN_NO_CREATE, 0, 0, 0, true, 1);
1559 }
1560 
file_move12_test(struct transaction * tr)1561 static void file_move12_test(struct transaction* tr) {
1562     file_test_etc(tr, false, "test1", FILE_OPEN_NO_CREATE, "test2",
1563                   FILE_OPEN_CREATE_EXCLUSIVE, 0, file_test_block_count, 0,
1564                   false, 1);
1565 }
1566 
file_move21_test(struct transaction * tr)1567 static void file_move21_test(struct transaction* tr) {
1568     file_test_etc(tr, true, "test2", FILE_OPEN_NO_CREATE, "test1",
1569                   FILE_OPEN_CREATE_EXCLUSIVE, 0, file_test_block_count, 0,
1570                   false, 1);
1571 }
1572 
file_create2_test(struct transaction * tr)1573 static void file_create2_test(struct transaction* tr) {
1574     file_test(tr, "test1", FILE_OPEN_CREATE_EXCLUSIVE, file_test_block_count, 0,
1575               0, false, 2);
1576     file_test(tr, "test2", FILE_OPEN_CREATE_EXCLUSIVE, file_test_block_count, 0,
1577               0, false, 3);
1578 }
1579 
file_move_test(struct transaction * tr)1580 static void file_move_test(struct transaction* tr) {
1581     file_test(tr, "test1", FILE_OPEN_CREATE_EXCLUSIVE, file_test_block_count, 0,
1582               0, false, 2);
1583     file_test_etc(tr, false, "test1", FILE_OPEN_NO_CREATE, "test2",
1584                   FILE_OPEN_CREATE_EXCLUSIVE, 0, file_test_block_count, 0,
1585                   false, 2);
1586     file_test(tr, "test2", FILE_OPEN_NO_CREATE, 0, file_test_block_count,
1587               file_test_block_count, true, 2);
1588 
1589     file_test(tr, "test1", FILE_OPEN_CREATE_EXCLUSIVE, file_test_block_count, 0,
1590               0, false, 2);
1591     file_test(tr, "test2", FILE_OPEN_CREATE_EXCLUSIVE, file_test_block_count, 0,
1592               0, false, 3);
1593 
1594     file_test(tr, "test1", FILE_OPEN_NO_CREATE, 0, file_test_block_count, false,
1595               false, 2);
1596     file_test(tr, "test2", FILE_OPEN_NO_CREATE, 0, file_test_block_count, false,
1597               false, 3);
1598 
1599     file_move_expect(tr, "test1", FILE_OPEN_NO_CREATE, "test3",
1600                      FILE_OPEN_NO_CREATE, FILE_OP_ERR_NOT_FOUND);
1601 
1602     file_move_expect(tr, "test1", FILE_OPEN_NO_CREATE, "test2",
1603                      FILE_OPEN_CREATE_EXCLUSIVE, FILE_OP_ERR_EXIST);
1604 
1605     file_move_expect(tr, "test1", FILE_OPEN_NO_CREATE, "test1",
1606                      FILE_OPEN_CREATE_EXCLUSIVE, FILE_OP_ERR_EXIST);
1607 
1608     file_move_expect_success(tr, "test1", FILE_OPEN_NO_CREATE, "test1",
1609                              FILE_OPEN_NO_CREATE);
1610 
1611     transaction_complete(tr);
1612     assert(!tr->failed);
1613     transaction_activate(tr);
1614 
1615     file_move_expect(tr, "test1", FILE_OPEN_NO_CREATE, "test3",
1616                      FILE_OPEN_NO_CREATE, FILE_OP_ERR_NOT_FOUND);
1617 
1618     file_move_expect(tr, "test1", FILE_OPEN_NO_CREATE, "test2",
1619                      FILE_OPEN_CREATE_EXCLUSIVE, FILE_OP_ERR_EXIST);
1620 
1621     file_move_expect(tr, "test1", FILE_OPEN_NO_CREATE, "test1",
1622                      FILE_OPEN_CREATE_EXCLUSIVE, FILE_OP_ERR_EXIST);
1623 
1624     file_move_expect_success(tr, "test1", FILE_OPEN_NO_CREATE, "test1",
1625                              FILE_OPEN_NO_CREATE);
1626 
1627     file_test_etc(tr, false, "test1", FILE_OPEN_NO_CREATE, "test2",
1628                   FILE_OPEN_NO_CREATE, 0, file_test_block_count, 0, false, 2);
1629 
1630     file_test(tr, "test2", FILE_OPEN_NO_CREATE, 0, file_test_block_count, false,
1631               false, 2);
1632 
1633     file_test(tr, "test2", FILE_OPEN_NO_CREATE, 0, file_test_block_count,
1634               file_test_block_count, true, 2);
1635 }
1636 
file_delete2_test(struct transaction * tr)1637 static void file_delete2_test(struct transaction* tr) {
1638     file_test(tr, "test1", FILE_OPEN_NO_CREATE, 0, file_test_block_count, false,
1639               false, 2);
1640     file_test(tr, "test2", FILE_OPEN_NO_CREATE, 0, file_test_block_count, false,
1641               false, 3);
1642     file_test(tr, "test1", FILE_OPEN_NO_CREATE, 0, file_test_block_count,
1643               file_test_block_count, file_test_block_count, 2);
1644     file_test(tr, "test2", FILE_OPEN_NO_CREATE, 0, file_test_block_count,
1645               file_test_block_count, file_test_block_count, 3);
1646 }
1647 
file_create2_read_after_commit_test(struct transaction * tr)1648 static void file_create2_read_after_commit_test(struct transaction* tr) {
1649     int i;
1650     struct storage_file_handle file[2];
1651 
1652     open_test_file(tr, &file[0], "test1", FILE_OPEN_CREATE_EXCLUSIVE);
1653     open_test_file(tr, &file[1], "test2", FILE_OPEN_CREATE_EXCLUSIVE);
1654 
1655     for (i = 0; i < 2; i++) {
1656         file_test_open(tr, &file[i], file_test_block_count, 0, 0, 2 + i);
1657     }
1658     transaction_complete(tr);
1659     assert(!tr->failed);
1660     transaction_activate(tr);
1661     for (i = 0; i < 2; i++) {
1662         file_test_open(tr, &file[i], 0, file_test_block_count, 0, 2 + i);
1663         file_close(&file[i]);
1664     }
1665 }
1666 
file_create3_conflict_test(struct transaction * tr)1667 static void file_create3_conflict_test(struct transaction* tr) {
1668     struct transaction tr1;
1669     struct transaction tr2;
1670     struct transaction tr3;
1671 
1672     transaction_init(&tr1, tr->fs, true);
1673     transaction_init(&tr2, tr->fs, true);
1674     transaction_init(&tr3, tr->fs, true);
1675 
1676     file_test(&tr1, "test1", FILE_OPEN_CREATE_EXCLUSIVE, 1, 0, 0, false, 4);
1677     file_test(&tr2, "test1", FILE_OPEN_CREATE_EXCLUSIVE, 1, 0, 0, false, 5);
1678     file_test(&tr3, "test2", FILE_OPEN_CREATE_EXCLUSIVE, 1, 0, 0, false, 6);
1679 
1680     assert(!tr1.failed);
1681     assert(!tr2.failed);
1682     assert(!tr3.failed);
1683     transaction_complete(&tr1);
1684     transaction_complete(&tr2);
1685     transaction_complete(&tr3);
1686     assert(!tr1.failed);
1687     assert(tr2.failed);
1688     assert(!tr3.failed);
1689     file_test(tr, "test1", FILE_OPEN_NO_CREATE, 0, 1, 1, true, 4);
1690     file_test(tr, "test2", FILE_OPEN_NO_CREATE, 0, 1, 1, true, 6);
1691 
1692     transaction_free(&tr1);
1693     transaction_free(&tr2);
1694     transaction_free(&tr3);
1695 }
1696 
file_create_delete_2_transaction_test(struct transaction * tr)1697 static void file_create_delete_2_transaction_test(struct transaction* tr) {
1698     struct transaction tr1;
1699     struct transaction tr2;
1700 
1701     transaction_init(&tr1, tr->fs, true);
1702     transaction_init(&tr2, tr->fs, true);
1703 
1704     file_test(&tr1, "test1", FILE_OPEN_CREATE_EXCLUSIVE, 1, 0, 0, false, 7);
1705     file_test(&tr2, "test2", FILE_OPEN_CREATE_EXCLUSIVE, 1, 0, 0, false, 8);
1706 
1707     assert(!tr1.failed);
1708     assert(!tr2.failed);
1709     transaction_complete(&tr1);
1710     transaction_complete(&tr2);
1711     assert(!tr1.failed);
1712     assert(!tr2.failed);
1713 
1714     transaction_activate(&tr1);
1715     transaction_activate(&tr2);
1716     file_test(&tr1, "test1", FILE_OPEN_NO_CREATE, 0, 1, 1, true, 7);
1717     file_test(&tr2, "test2", FILE_OPEN_NO_CREATE, 0, 1, 1, true, 8);
1718 
1719     assert(!tr1.failed);
1720     assert(!tr2.failed);
1721     transaction_complete(&tr1);
1722     transaction_complete(&tr2);
1723     assert(!tr1.failed);
1724     assert(!tr2.failed);
1725 
1726     transaction_free(&tr1);
1727     transaction_free(&tr2);
1728 }
1729 
file_create_many_test(struct transaction * tr)1730 static void file_create_many_test(struct transaction* tr) {
1731     char path[10];
1732     int i;
1733     for (i = 0; i < file_test_many_file_count; i++) {
1734         snprintf(path, sizeof(path), "test%d", i);
1735         file_test(tr, path, FILE_OPEN_CREATE_EXCLUSIVE, 1, 0, 0, false, 7 + i);
1736     }
1737 }
1738 
file_delete_many_test(struct transaction * tr)1739 static void file_delete_many_test(struct transaction* tr) {
1740     char path[10];
1741     int i;
1742 
1743     files_print(tr);
1744 
1745     for (i = 0; i < file_test_many_file_count; i++) {
1746         snprintf(path, sizeof(path), "test%d", i);
1747         file_test(tr, path, false, 0, 1, 1, true, 7 + i);
1748     }
1749 }
1750 
1751 struct file_iterate_many_state {
1752     struct file_iterate_state iter;
1753     uint64_t found;
1754     bool stop;
1755     char last_path[10];
1756 };
1757 
file_iterate_many_iter(struct file_iterate_state * iter,struct transaction * tr,const struct block_mac * block_mac,bool added,bool removed)1758 static bool file_iterate_many_iter(struct file_iterate_state* iter,
1759                                    struct transaction* tr,
1760                                    const struct block_mac* block_mac,
1761                                    bool added,
1762                                    bool removed) {
1763     struct file_iterate_many_state* miter =
1764             containerof(iter, struct file_iterate_many_state, iter);
1765     const struct file_info* file_info;
1766     struct obj_ref ref = OBJ_REF_INITIAL_VALUE(ref);
1767     int i;
1768     int ret;
1769     uint64_t mask;
1770 
1771     file_info = file_get_info(tr, block_mac, &ref);
1772 
1773     ret = sscanf(file_info->path, "test%d", &i);
1774 
1775     assert(strlen(file_info->path) < sizeof(miter->last_path));
1776     strcpy(miter->last_path, file_info->path);
1777 
1778     file_info_put(file_info, &ref);
1779 
1780     assert(ret == 1);
1781     mask = (1ULL << i);
1782     assert(!(miter->found & mask));
1783     miter->found |= mask;
1784 
1785     return miter->stop;
1786 }
1787 
file_iterate_many_test(struct transaction * tr)1788 static void file_iterate_many_test(struct transaction* tr) {
1789     struct file_iterate_many_state state = {
1790             .iter.file = file_iterate_many_iter,
1791             .found = 0,
1792             .stop = false,
1793     };
1794     uint64_t last_found = 0;
1795     enum file_op_result res;
1796 
1797     /* iterate over all files in one pass */
1798     res = file_iterate(tr, NULL, false, &state.iter, false);
1799     assert(state.found = (1ull << file_test_many_file_count) - 1);
1800     assert(res == FILE_OP_SUCCESS);
1801     res = file_iterate(tr, NULL, true, &state.iter, false);
1802     assert(res == FILE_OP_SUCCESS);
1803 
1804     /* lookup one file at a time */
1805     state.found = 0;
1806     state.stop = true;
1807     res = file_iterate(tr, NULL, false, &state.iter, false);
1808     assert(res == FILE_OP_SUCCESS);
1809     while (state.found != last_found) {
1810         last_found = state.found;
1811         res = file_iterate(tr, state.last_path, false, &state.iter, false);
1812         assert(res == FILE_OP_SUCCESS);
1813     }
1814     assert(state.found = (1ull << file_test_many_file_count) - 1);
1815     res = file_iterate(tr, NULL, true, &state.iter, false);
1816     assert(res == FILE_OP_SUCCESS);
1817 }
1818 
file_allocate_all1_test(struct transaction * tr)1819 static void file_allocate_all1_test(struct transaction* tr) {
1820     file_allocate_all_test(tr, 1, 0, 1, "test1", FILE_OPEN_CREATE);
1821 }
1822 
file_allocate_all_2tr_1_test(struct transaction * tr)1823 static void file_allocate_all_2tr_1_test(struct transaction* tr) {
1824     file_allocate_all_test(tr, 2, 0, 1, "test1", FILE_OPEN_CREATE);
1825 }
1826 
file_allocate_all_8tr_1_test(struct transaction * tr)1827 static void file_allocate_all_8tr_1_test(struct transaction* tr) {
1828     file_allocate_all_test(tr, 8, 0, 1, "test1", FILE_OPEN_CREATE);
1829 }
1830 
file_allocate_all_complete1_test(struct transaction * tr)1831 static void file_allocate_all_complete1_test(struct transaction* tr) {
1832     file_allocate_all_test(tr, 1, 1, 1, "test1", FILE_OPEN_CREATE);
1833 }
1834 
file_allocate_all_complete_multi1_test(struct transaction * tr)1835 static void file_allocate_all_complete_multi1_test(struct transaction* tr) {
1836     file_allocate_all_test(tr, 2, 8, 10, "test1", FILE_OPEN_CREATE);
1837 }
1838 
file_allocate_leave_10_test(struct transaction * tr)1839 static void file_allocate_leave_10_test(struct transaction* tr) {
1840     file_allocate_all_test(tr, 1, 1, 10, "test1", FILE_OPEN_CREATE);
1841 }
1842 
fs_create_checkpoint(struct transaction * tr)1843 static void fs_create_checkpoint(struct transaction* tr) {
1844     file_test(tr, "test_checkpoint", FILE_OPEN_CREATE_EXCLUSIVE,
1845               file_test_block_count, 0, 0, false, 1);
1846     transaction_complete_update_checkpoint(tr);
1847     assert(!tr->failed);
1848 
1849     transaction_activate(tr);
1850     assert(get_fs_checkpoint_count(tr) > 3);
1851     file_delete(tr, "test_checkpoint", false);
1852     transaction_complete(tr);
1853     assert(!tr->failed);
1854 
1855     transaction_activate(tr);
1856     assert(get_fs_checkpoint_count(tr) > 3);
1857 }
1858 
fs_modify_with_checkpoint(struct transaction * tr)1859 static void fs_modify_with_checkpoint(struct transaction* tr) {
1860     file_test(tr, "test_checkpoint", FILE_OPEN_CREATE_EXCLUSIVE,
1861               file_test_block_count, 0, 0, false, 1);
1862     transaction_complete_update_checkpoint(tr);
1863     assert(!tr->failed);
1864 
1865     /* modify the active filesystem with an active checkpoint */
1866     transaction_activate(tr);
1867     file_test(tr, "test_checkpoint", FILE_OPEN_NO_CREATE, file_test_block_count,
1868               0, 0, false, 2);
1869     transaction_complete(tr);
1870     assert(!tr->failed);
1871 
1872     transaction_activate(tr);
1873     assert(get_fs_checkpoint_count(tr) > 3);
1874     file_delete(tr, "test_checkpoint", false);
1875     transaction_complete(tr);
1876     assert(!tr->failed);
1877 
1878     transaction_activate(tr);
1879     assert(get_fs_checkpoint_count(tr) > 3);
1880 }
1881 
fs_clear_checkpoint(struct transaction * tr)1882 static void fs_clear_checkpoint(struct transaction* tr) {
1883     assert(get_fs_checkpoint_count(tr) > 3);
1884 
1885     file_delete(tr, "test_checkpoint", false);
1886     /* at this point the file-system should be empty, all files are deleted */
1887     transaction_complete_update_checkpoint(tr);
1888 
1889     /*
1890      * one block each for the checkpoint metadata block, checkpoint file tree
1891      * root, and checkpoint free set, no file blocks should exist now
1892      */
1893     assert(get_fs_checkpoint_count(tr) == 3);
1894 
1895     transaction_activate(tr);
1896 }
1897 
fs_rebuild_free_set(struct transaction * tr)1898 static void fs_rebuild_free_set(struct transaction* tr) {
1899     data_block_t block;
1900     ssize_t initial_free_count, free_count;
1901     bool pending_modifications = false;
1902 
1903     check_fs_prepare(tr);
1904 
1905     initial_free_count = 0;
1906     block = block_set_find_next_block(tr, &tr->fs->free, 1, true);
1907     while (block) {
1908         initial_free_count++;
1909         block = block_set_find_next_block(tr, &tr->fs->free, block + 1, true);
1910     }
1911     block = block_set_find_next_block(tr, &tr->allocated, 1, true);
1912     while (block) {
1913         /* we allocated blocks already in this transaction */
1914         pending_modifications = true;
1915         initial_free_count--;
1916         block = block_set_find_next_block(tr, &tr->allocated, block + 1, true);
1917     }
1918     assert(initial_free_count > 0);
1919 
1920     if (print_test_verbose) {
1921         printf("files before rebuild:\n");
1922         block_tree_print(tr, &tr->fs->files);
1923         printf("free set before rebuild:\n");
1924         block_set_print(tr, &tr->fs->free);
1925     }
1926 
1927     tr->rebuild_free_set = true;
1928     transaction_complete(tr);
1929     assert(!tr->failed);
1930     transaction_activate(tr);
1931 
1932     if (print_test_verbose) {
1933         printf("files after rebuild:\n");
1934         block_tree_print(tr, &tr->fs->files);
1935         printf("free set after rebuild:\n");
1936         block_set_print(tr, &tr->fs->free);
1937     }
1938 
1939     free_count = 0;
1940     block = block_set_find_next_block(tr, &tr->fs->free, 1, true);
1941     while (block) {
1942         free_count++;
1943         /*
1944          * free the old free set nodes, because we should have replaced them
1945          * with the rebuilt free set tree
1946          */
1947         if (!block_set_replace_used_by(block, "free_tree_node", "free", 0,
1948                                        false)) {
1949             /*
1950              * Ensure that all blocks in the new free set were in the old free
1951              * set. We can only do this if there were no pending modifications
1952              * in the transaction; if there were the files tree has been
1953              * re-written and some blocks in the new free set will have been in
1954              * the previous file tree.
1955              */
1956             if (!pending_modifications) {
1957                 block_set_used_by(block, "free", 0);
1958             }
1959         }
1960         block = block_set_find_next_block(tr, &tr->fs->free, block + 1, true);
1961     }
1962 
1963     assert(free_count == initial_free_count);
1964 }
1965 
fs_rebuild_fragmented_free_set(struct transaction * tr)1966 static void fs_rebuild_fragmented_free_set(struct transaction* tr) {
1967     char path[10];
1968     int i;
1969     for (i = 0; i < file_test_many_file_count; i++) {
1970         snprintf(path, sizeof(path), "test%d", i);
1971         file_test(tr, path, FILE_OPEN_CREATE_EXCLUSIVE, 1, 0, 0, false, 7 + i);
1972     }
1973 
1974     /*
1975      * Delete half the files, leaving the free set fragmented so we have a free
1976      * set tree with depth > 1.
1977      */
1978     for (i = 0; i < file_test_many_file_count; i += 2) {
1979         snprintf(path, sizeof(path), "test%d", i);
1980         file_test(tr, path, false, 0, 1, 1, true, 7 + i);
1981     }
1982 
1983     transaction_complete(tr);
1984     assert(!tr->failed);
1985     transaction_activate(tr);
1986 
1987     fs_rebuild_free_set(tr);
1988 
1989     /* clean up */
1990     for (i = 0; i < file_test_many_file_count; i++) {
1991         snprintf(path, sizeof(path), "test%d", i);
1992         file_delete(tr, path, false);
1993     }
1994 }
1995 
fs_rebuild_with_pending_file(struct transaction * tr)1996 static void fs_rebuild_with_pending_file(struct transaction* tr) {
1997     file_test(tr, "test_rebuild", FILE_OPEN_CREATE_EXCLUSIVE,
1998               file_test_block_count, 0, 0, false, 1);
1999     fs_rebuild_free_set(tr);
2000     file_delete(tr, "test_rebuild", false);
2001 }
2002 
fs_rebuild_with_pending_transaction(struct transaction * tr)2003 static void fs_rebuild_with_pending_transaction(struct transaction* tr) {
2004     struct transaction other_tr;
2005     transaction_init(&other_tr, tr->fs, true);
2006     fs_rebuild_free_set(tr);
2007     assert(other_tr.failed);
2008     transaction_free(&other_tr);
2009 }
2010 
fs_repair_flag(struct transaction * tr)2011 static void fs_repair_flag(struct transaction* tr) {
2012     enum file_op_result result;
2013     struct storage_file_handle file;
2014 
2015     /* clear FS to reset repair flag */
2016     transaction_fail(tr);
2017     block_test_reinit(tr, FS_INIT_FLAGS_DO_CLEAR);
2018 
2019     /* a non-existent file should not return FS_REPAIRED */
2020     result = file_open(tr, "test_simulated_repair_nonexistent", &file,
2021                        FILE_OPEN_NO_CREATE, false);
2022     assert(result == FILE_OP_ERR_NOT_FOUND);
2023 
2024     /* simulate an operation that requires setting the repair flag */
2025     file_test(tr, "test_simulated_repair", FILE_OPEN_CREATE_EXCLUSIVE,
2026               file_test_block_count, 0, 0, false, 1);
2027     tr->repaired = true;
2028     transaction_complete(tr);
2029     assert(!tr->failed);
2030     assert(fs_is_repaired(tr->fs));
2031     transaction_activate(tr);
2032 
2033     /* globally acknowledge repair in test helpers */
2034     allow_repaired = true;
2035 
2036     /* and again */
2037     file_test(tr, "test_simulated_repair", FILE_OPEN_NO_CREATE,
2038               file_test_block_count, 0, 0, false, 2);
2039     tr->repaired = true;
2040     transaction_complete(tr);
2041     assert(!tr->failed);
2042     assert(fs_is_repaired(tr->fs));
2043     transaction_activate(tr);
2044 
2045     /* Opening existing files must ack repair, because we reset the whole FS */
2046     result = file_open(tr, "test_simulated_repair", &file, FILE_OPEN_NO_CREATE,
2047                        false);
2048     assert(result == FILE_OP_ERR_FS_REPAIRED);
2049 
2050     /* a non-existent file should now report FS_REPAIRED */
2051     result = file_open(tr, "test_simulated_repair_nonexistent", &file,
2052                        FILE_OPEN_NO_CREATE, false);
2053     assert(result == FILE_OP_ERR_FS_REPAIRED);
2054 
2055     /* ...unless we allow a repaired FS */
2056     result = file_open(tr, "test_simulated_repair_nonexistent", &file,
2057                        FILE_OPEN_NO_CREATE, true);
2058     assert(result == FILE_OP_ERR_NOT_FOUND);
2059 
2060     result = file_open(tr, "test_simulated_repair", &file, FILE_OPEN_NO_CREATE,
2061                        true);
2062     assert(result == FILE_OP_SUCCESS);
2063     file_close(&file);
2064 
2065     result = file_open(tr, "test_simulated_repair_nonexistent", &file,
2066                        FILE_OPEN_CREATE, true);
2067     assert(result == FILE_OP_SUCCESS);
2068     file_close(&file);
2069 
2070     /*
2071      * re-initialize the fs to make sure we propagate the repaired state through
2072      * the super block
2073      */
2074     transaction_fail(tr);
2075     block_test_reinit(tr, FS_INIT_FLAGS_NONE);
2076     assert(fs_is_repaired(tr->fs));
2077     transaction_complete(tr);
2078 
2079     /* disallow repair globally in test helpers */
2080     allow_repaired = false;
2081 
2082     block_test_reinit(tr, FS_INIT_FLAGS_DO_CLEAR);
2083     assert(!fs_is_repaired(tr->fs));
2084     /* force the cleared superblock to be written */
2085     file_test(tr, "test_simulated_repair", FILE_OPEN_CREATE_EXCLUSIVE,
2086               file_test_block_count, 0, 0, false, 1);
2087     transaction_complete(tr);
2088 
2089     /* did the cleared repair flag persist? */
2090     block_test_reinit(tr, FS_INIT_FLAGS_NONE);
2091     assert(!fs_is_repaired(tr->fs));
2092 
2093     file_delete(tr, "test_simulated_repair", false);
2094 }
2095 
2096 /*
2097  * We don't allow repairs of the alternate FS, but we must persist it from the
2098  * main FS across usage of the alternate.
2099  */
fs_repair_with_alternate(struct transaction * tr)2100 static void fs_repair_with_alternate(struct transaction* tr) {
2101     enum file_op_result result;
2102     struct storage_file_handle file;
2103 
2104     /* simulate an operation that requires setting the repair flag */
2105     file_test(tr, "test_repair_with_alternate", FILE_OPEN_CREATE_EXCLUSIVE,
2106               file_test_block_count, 0, 0, false, 1);
2107     tr->repaired = true;
2108     transaction_complete(tr);
2109     assert(!tr->failed);
2110     assert(fs_is_repaired(tr->fs));
2111     transaction_activate(tr);
2112 
2113     /*
2114      * re-initialize the fs to make sure we propagate the repaired state through
2115      * the super block
2116      */
2117     transaction_fail(tr);
2118     block_test_swap_clear_reinit(
2119             tr, FS_INIT_FLAGS_DO_CLEAR | FS_INIT_FLAGS_ALTERNATE_DATA);
2120     assert(tr->fs->main_repaired);
2121     assert(!fs_is_repaired(tr->fs));
2122 
2123     /* Opening a non-existent file does not return FILE_OP_ERR_FS_REPAIRED */
2124     result = file_open(tr, "test_alternate_nonexistent", &file,
2125                        FILE_OPEN_NO_CREATE, true);
2126     assert(result == FILE_OP_ERR_NOT_FOUND);
2127 
2128     /* Make sure we rewrite the alternate superblock */
2129     file_test(tr, "test_alternate_create", FILE_OPEN_CREATE_EXCLUSIVE,
2130               file_test_block_count, 0, 0, true, 1);
2131     transaction_complete(tr);
2132     assert(!tr->failed);
2133     transaction_activate(tr);
2134 
2135     /* But we can't do a repair on the alternate FS */
2136     file_test(tr, "test_alternate_repair", FILE_OPEN_CREATE_EXCLUSIVE,
2137               file_test_block_count, 0, 0, false, 1);
2138     tr->repaired = true;
2139     transaction_complete(tr);
2140     assert(tr->failed);
2141     assert(!fs_is_repaired(tr->fs));
2142 
2143     /* Back to the main FS, which must still be repaired */
2144     block_test_swap_reinit(tr, FS_INIT_FLAGS_NONE);
2145     assert(fs_is_repaired(tr->fs));
2146     transaction_complete(tr);
2147 
2148     /* Clear the repair flag */
2149     block_test_reinit(tr, FS_INIT_FLAGS_DO_CLEAR);
2150     assert(!fs_is_repaired(tr->fs));
2151 }
2152 
future_fs_version_test(struct transaction * tr)2153 static void future_fs_version_test(struct transaction* tr) {
2154     struct obj_ref super_ref = OBJ_REF_INITIAL_VALUE(super_ref);
2155     struct fs* fs = tr->fs;
2156     const struct key* key = fs->key;
2157     struct block_device* dev = fs->dev;
2158     struct block_device* super_dev = fs->super_dev;
2159     const void* super_ro;
2160     uint16_t* super_rw;
2161     data_block_t block;
2162     int ret;
2163     struct storage_file_handle file;
2164     enum file_op_result open_result;
2165 
2166     /* offset of fs_version field in uint16_t words */
2167     size_t fs_version_offset = 28 / 2;
2168 
2169     file_test(tr, "future_fs_version_file", FILE_OPEN_CREATE_EXCLUSIVE, 0, 0, 0,
2170               false, 1);
2171 
2172     transaction_complete(tr);
2173 
2174     block = tr->fs->super_block[fs->super_block_version & 1];
2175     super_ro = block_get_super(fs, block, &super_ref);
2176     assert(super_ro);
2177     super_rw = block_dirty(tr, super_ro, false);
2178     assert(super_rw);
2179     super_rw[fs_version_offset]++;
2180     block_put_dirty_no_mac(super_rw, &super_ref, false);
2181     block_cache_clean_transaction(tr);
2182 
2183     transaction_free(tr);
2184     fs_destroy(fs);
2185     block_cache_dev_destroy(dev);
2186 
2187     ret = fs_init(fs, FILE_SYSTEM_TEST, key, dev, super_dev,
2188                   FS_INIT_FLAGS_NONE);
2189     assert(ret == 0);
2190     assert(!fs_is_readable(fs));
2191     expect_errors(TRUSTY_STORAGE_ERROR_SUPERBLOCK_INVALID, 1);
2192 
2193     transaction_init(tr, fs, true);
2194     open_result = file_open(tr, "future_fs_version_file", &file,
2195                             FILE_OPEN_NO_CREATE, false);
2196     assert(open_result == FILE_OP_ERR_FAILED);
2197     transaction_fail(tr);
2198     transaction_free(tr);
2199     fs_destroy(fs);
2200     block_cache_dev_destroy(dev);
2201 
2202     ret = fs_init(fs, FILE_SYSTEM_TEST, key, dev, super_dev,
2203                   FS_INIT_FLAGS_DO_CLEAR);
2204     assert(ret == 0);
2205     assert(!fs_is_readable(fs));
2206     expect_errors(TRUSTY_STORAGE_ERROR_SUPERBLOCK_INVALID, 1);
2207 
2208     transaction_init(tr, fs, true);
2209     open_result = file_open(tr, "future_fs_version_file", &file,
2210                             FILE_OPEN_NO_CREATE, false);
2211     assert(open_result == FILE_OP_ERR_FAILED);
2212     transaction_fail(tr);
2213     transaction_free(tr);
2214     fs_destroy(fs);
2215     block_cache_dev_destroy(dev);
2216 
2217     /*
2218      * fs is not mountable, but we want to rewrite a block. Set up the bare
2219      * minimum required fs so we can rewrite the superblock manually.
2220      */
2221     fs->dev = dev;
2222     fs->super_dev = super_dev;
2223     fs->readable = true;
2224     fs->writable = true;
2225 
2226     transaction_init(tr, fs, false);
2227     super_ro = block_get_super(fs, block, &super_ref);
2228     assert(super_ro);
2229     super_rw = block_dirty(tr, super_ro, false);
2230     assert(super_rw);
2231     super_rw[fs_version_offset]--;
2232     block_put_dirty_no_mac(super_rw, &super_ref, false);
2233     block_cache_clean_transaction(tr);
2234     transaction_free(tr);
2235 
2236     ret = fs_init(fs, FILE_SYSTEM_TEST, key, dev, super_dev,
2237                   FS_INIT_FLAGS_NONE);
2238     assert(ret == 0);
2239 
2240     transaction_init(tr, fs, true);
2241 
2242     file_test(tr, "future_fs_version_file", FILE_OPEN_NO_CREATE, 0, 0, 0,
2243               true /* delete */, 1);
2244 }
2245 
2246 /**
2247  * set_required_flags - helper to modify the required_flags super block field
2248  * @fs:             File system object.
2249  * @required_flags: Flags to write into the required_flags field.
2250  *
2251  * Write @required_flags into the required_flags field of the active super
2252  * block. @fs may have been destroyed with fs_destroy() but @fs->dev and
2253  * @fs->super_dev must be reset correctly after it was destroyed.
2254  *
2255  * Returns: the previous required_flags value of the active super block.
2256  */
set_required_flags(struct fs * fs,uint16_t required_flags)2257 static uint16_t set_required_flags(struct fs* fs, uint16_t required_flags) {
2258     /* offset of required_flags field in uint16_t words */
2259     size_t required_flags_offset = 30 / 2;
2260 
2261     struct obj_ref super_ref = OBJ_REF_INITIAL_VALUE(super_ref);
2262     struct transaction tr;
2263     struct block_device* dev = fs->dev;
2264     const void* super_ro;
2265     uint16_t* super_rw;
2266     data_block_t block;
2267     uint16_t old_required_flags;
2268 
2269     /*
2270      * If the fs was mounted read-only due to an error, we need to override this
2271      * state. We want to manually rewrite the superblock, so we have to override
2272      * the read-only state for block_dirty() to be allowed.
2273      */
2274     transaction_init(&tr, fs, false);
2275     block = fs->super_block[fs->super_block_version & 1];
2276     super_ro = block_get_super(fs, block, &super_ref);
2277     assert(super_ro);
2278     super_rw = block_dirty(&tr, super_ro, false);
2279     assert(super_rw);
2280     old_required_flags = super_rw[required_flags_offset];
2281     super_rw[required_flags_offset] = required_flags;
2282     block_put_dirty_no_mac(super_rw, &super_ref, false);
2283     block_cache_clean_transaction(&tr);
2284     transaction_free(&tr);
2285     block_cache_dev_destroy(dev);
2286     return old_required_flags;
2287 }
2288 
unknown_required_flags_test(struct transaction * tr)2289 static void unknown_required_flags_test(struct transaction* tr) {
2290     struct fs* fs = tr->fs;
2291     const struct key* key = fs->key;
2292     struct block_device* dev = fs->dev;
2293     struct block_device* super_dev = fs->super_dev;
2294     int ret;
2295     uint16_t initial_required_flags;
2296     struct storage_file_handle file;
2297     enum file_op_result open_result;
2298 
2299     /* update when SUPER_BLOCK_REQUIRED_FLAGS_MASK changes in super.c */
2300     uint16_t first_unsupported_required_flag = 0x2U;
2301 
2302     file_test(tr, "unknown_flags_file", FILE_OPEN_CREATE_EXCLUSIVE, 0, 0, 0,
2303               false, 1);
2304 
2305     transaction_complete(tr);
2306     transaction_free(tr);
2307 
2308     initial_required_flags =
2309             set_required_flags(fs, first_unsupported_required_flag);
2310 
2311     fs_destroy(fs);
2312 
2313     ret = fs_init(fs, FILE_SYSTEM_TEST, key, dev, super_dev,
2314                   FS_INIT_FLAGS_NONE);
2315     assert(ret == 0);
2316     assert(!fs_is_readable(fs));
2317     expect_errors(TRUSTY_STORAGE_ERROR_SUPERBLOCK_INVALID, 1);
2318 
2319     transaction_init(tr, fs, true);
2320     open_result = file_open(tr, "unknown_flags_file", &file,
2321                             FILE_OPEN_NO_CREATE, false);
2322     assert(open_result == FILE_OP_ERR_FAILED);
2323     transaction_fail(tr);
2324     transaction_free(tr);
2325     fs_destroy(fs);
2326     block_cache_dev_destroy(dev);
2327 
2328     ret = fs_init(fs, FILE_SYSTEM_TEST, key, dev, super_dev,
2329                   FS_INIT_FLAGS_DO_CLEAR);
2330     assert(ret == 0);
2331     assert(!fs_is_readable(fs));
2332     expect_errors(TRUSTY_STORAGE_ERROR_SUPERBLOCK_INVALID, 1);
2333 
2334     transaction_init(tr, fs, true);
2335     open_result = file_open(tr, "unknown_flags_file", &file,
2336                             FILE_OPEN_NO_CREATE, false);
2337     assert(open_result == FILE_OP_ERR_FAILED);
2338     transaction_fail(tr);
2339     transaction_free(tr);
2340     fs_destroy(fs);
2341     block_cache_dev_destroy(dev);
2342 
2343     /*
2344      * fs is not mountable, but we want to rewrite a block. Set up the bare
2345      * minimum required fs so we can rewrite the superblock manually.
2346      */
2347     fs->dev = dev;
2348     fs->super_dev = super_dev;
2349     fs->readable = true;
2350     fs->writable = true;
2351 
2352     /* set all flag bits, this should fail unless we support 16 flags */
2353     set_required_flags(fs, UINT16_MAX);
2354 
2355     ret = fs_init(fs, FILE_SYSTEM_TEST, key, dev, super_dev,
2356                   FS_INIT_FLAGS_NONE);
2357     assert(ret == 0);
2358     assert(!fs_is_readable(fs));
2359     expect_errors(TRUSTY_STORAGE_ERROR_SUPERBLOCK_INVALID, 1);
2360 
2361     transaction_init(tr, fs, true);
2362     open_result = file_open(tr, "unknown_flags_file", &file,
2363                             FILE_OPEN_NO_CREATE, false);
2364     assert(open_result == FILE_OP_ERR_FAILED);
2365     transaction_fail(tr);
2366     transaction_free(tr);
2367     fs_destroy(fs);
2368     block_cache_dev_destroy(dev);
2369 
2370     ret = fs_init(fs, FILE_SYSTEM_TEST, key, dev, super_dev,
2371                   FS_INIT_FLAGS_DO_CLEAR);
2372     assert(ret == 0);
2373     assert(!fs_is_readable(fs));
2374     expect_errors(TRUSTY_STORAGE_ERROR_SUPERBLOCK_INVALID, 1);
2375 
2376     transaction_init(tr, fs, true);
2377     open_result = file_open(tr, "unknown_flags_file", &file,
2378                             FILE_OPEN_NO_CREATE, false);
2379     assert(open_result == FILE_OP_ERR_FAILED);
2380     transaction_fail(tr);
2381     transaction_free(tr);
2382     fs_destroy(fs);
2383     block_cache_dev_destroy(dev);
2384 
2385     fs->dev = dev;
2386     fs->super_dev = super_dev;
2387     fs->readable = true;
2388     fs->writable = true;
2389 
2390     /* set highest flag bit, this should fail unless we support 16 flags */
2391     set_required_flags(fs, 0x1U << 15);
2392 
2393     ret = fs_init(fs, FILE_SYSTEM_TEST, key, dev, super_dev,
2394                   FS_INIT_FLAGS_NONE);
2395     assert(ret == 0);
2396     assert(!fs_is_readable(fs));
2397     expect_errors(TRUSTY_STORAGE_ERROR_SUPERBLOCK_INVALID, 1);
2398 
2399     transaction_init(tr, fs, true);
2400     open_result = file_open(tr, "unknown_flags_file", &file,
2401                             FILE_OPEN_NO_CREATE, false);
2402     assert(open_result == FILE_OP_ERR_FAILED);
2403     transaction_fail(tr);
2404     transaction_free(tr);
2405     fs_destroy(fs);
2406     block_cache_dev_destroy(dev);
2407 
2408     ret = fs_init(fs, FILE_SYSTEM_TEST, key, dev, super_dev,
2409                   FS_INIT_FLAGS_DO_CLEAR);
2410     assert(ret == 0);
2411     assert(!fs_is_readable(fs));
2412     expect_errors(TRUSTY_STORAGE_ERROR_SUPERBLOCK_INVALID, 1);
2413 
2414     transaction_init(tr, fs, true);
2415     open_result = file_open(tr, "unknown_flags_file", &file,
2416                             FILE_OPEN_NO_CREATE, false);
2417     assert(open_result == FILE_OP_ERR_FAILED);
2418     transaction_fail(tr);
2419     transaction_free(tr);
2420     fs_destroy(fs);
2421     block_cache_dev_destroy(dev);
2422 
2423     fs->dev = dev;
2424     fs->super_dev = super_dev;
2425     fs->readable = true;
2426     fs->writable = true;
2427 
2428     set_required_flags(fs, initial_required_flags);
2429 
2430     ret = fs_init(fs, FILE_SYSTEM_TEST, key, dev, super_dev,
2431                   FS_INIT_FLAGS_NONE);
2432     assert(ret == 0);
2433 
2434     transaction_init(tr, fs, true);
2435 
2436     file_test(tr, "unknown_flags_file", FILE_OPEN_NO_CREATE, 0, 0, 0,
2437               true /* delete */, 1);
2438 }
2439 
2440 typedef data_block_t (*block_selector)(struct transaction* tr,
2441                                        unsigned int arg);
2442 
fs_corruption_helper(struct transaction * tr,block_selector callback,unsigned int arg,bool expect_missing_file)2443 static void fs_corruption_helper(struct transaction* tr,
2444                                  block_selector callback,
2445                                  unsigned int arg,
2446                                  bool expect_missing_file) {
2447     struct storage_file_handle file;
2448     enum file_op_result result;
2449     struct fs* fs = tr->fs;
2450 
2451     file_test(tr, "recovery", FILE_OPEN_CREATE_EXCLUSIVE, file_test_block_count,
2452               0, 0, false, 1);
2453     transaction_complete(tr);
2454     assert(!tr->failed);
2455     transaction_activate(tr);
2456 
2457     open_test_file_etc(tr, &file, "recovery", FILE_OPEN_NO_CREATE,
2458                        FILE_OP_SUCCESS);
2459     file_close(&file);
2460     transaction_complete(tr);
2461     assert(!tr->failed);
2462     transaction_activate(tr);
2463 
2464     /* Corrupt the provided block */
2465     memset(&blocks[callback(tr, arg)], 0, sizeof(struct block));
2466     block_cache_dev_destroy(fs->dev);
2467 
2468     result = file_open(tr, "recovery", &file, FILE_OPEN_NO_CREATE, false);
2469     if (expect_missing_file) {
2470         assert(result == FILE_OP_ERR_FAILED);
2471     } else {
2472         assert(result == FILE_OP_SUCCESS);
2473         file_close(&file);
2474     }
2475     transaction_complete(tr);
2476     assert(!expect_missing_file || tr->failed);
2477 
2478     /* re-initialize the filesystem without recovery enabled */
2479     block_test_reinit(tr, FS_INIT_FLAGS_NONE);
2480 
2481     open_test_file_etc(
2482             tr, &file, "recovery", FILE_OPEN_NO_CREATE,
2483             expect_missing_file ? FILE_OP_ERR_FAILED : FILE_OP_SUCCESS);
2484     if (!expect_missing_file) {
2485         file_close(&file);
2486     }
2487     transaction_complete(tr);
2488 }
2489 
select_files_block(struct transaction * tr,unsigned int depth)2490 static data_block_t select_files_block(struct transaction* tr,
2491                                        unsigned int depth) {
2492     struct block_tree_path path;
2493     block_tree_walk(tr, &tr->fs->files, 0, true, &path);
2494     assert(path.count > depth);
2495     return block_mac_to_block(tr, &path.entry[depth].block_mac);
2496 }
2497 
select_free_block(struct transaction * tr,unsigned int depth)2498 static data_block_t select_free_block(struct transaction* tr,
2499                                       unsigned int depth) {
2500     struct block_tree_path path;
2501     block_tree_walk(tr, &tr->fs->free.block_tree, 0, true, &path);
2502     assert(path.count > depth);
2503     return block_mac_to_block(tr, &path.entry[depth].block_mac);
2504 }
2505 
select_data_block(struct transaction * tr,unsigned int block)2506 static data_block_t select_data_block(struct transaction* tr,
2507                                       unsigned int block) {
2508     struct storage_file_handle file;
2509     const void* block_data_ro = NULL;
2510     struct obj_ref ref = OBJ_REF_INITIAL_VALUE(ref);
2511     data_block_t data_block_num;
2512 
2513     open_test_file_etc(tr, &file, "recovery", FILE_OPEN_NO_CREATE,
2514                        FILE_OP_SUCCESS);
2515 
2516     block_data_ro = file_get_block(tr, &file, block, &ref);
2517     assert(block_data_ro);
2518     data_block_num = data_to_block_num(block_data_ro - sizeof(struct iv));
2519     file_block_put(block_data_ro, &ref);
2520     file_close(&file);
2521     return data_block_num;
2522 }
2523 
create_and_delete(struct transaction * tr,const char * filename)2524 static void create_and_delete(struct transaction* tr, const char* filename) {
2525     struct storage_file_handle file;
2526     open_test_file_etc(tr, &file, filename, FILE_OPEN_CREATE_EXCLUSIVE,
2527                        FILE_OP_SUCCESS);
2528     transaction_complete(tr);
2529     assert(!tr->failed);
2530     file_close(&file);
2531     transaction_activate(tr);
2532     file_delete(tr, filename, false);
2533     transaction_complete(tr);
2534     assert(!tr->failed);
2535     transaction_activate(tr);
2536 }
2537 
fs_recovery_clear_roots_test(struct transaction * tr)2538 static void fs_recovery_clear_roots_test(struct transaction* tr) {
2539     /*
2540      * Create and delete a file to ensure that we have a root files block and
2541      * not just an empty super block.
2542      */
2543     create_and_delete(tr, "ensure_roots");
2544     assert(!tr->fs->needs_full_scan);
2545 
2546     /* Corrupt the root files block */
2547     assert(select_files_block(tr, 0) ==
2548            block_mac_to_block(tr, &tr->fs->files.root));
2549     fs_corruption_helper(tr, select_files_block, 0, true);
2550     assert(tr->failed);
2551     /*
2552      * fs_corruption_helper hits the corrupted block while checking the file,
2553      * again while re-initializing the FS, and then again checking the file.
2554      */
2555     expect_errors(TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH, 3);
2556 
2557     assert(fs_check_full(tr->fs) == FS_CHECK_INVALID_BLOCK);
2558     assert(tr->fs->needs_full_scan);
2559     expect_errors(TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH, 2);
2560 
2561     /* re-initialize the filesystem with recovery enabled */
2562     block_test_reinit(tr, FS_INIT_FLAGS_RECOVERY_CLEAR_ALLOWED);
2563     expect_errors(TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH, 1);
2564 
2565     /* Did we recover correctly? */
2566     create_and_delete(tr, "recovery");
2567     assert(!tr->fs->needs_full_scan);
2568 
2569     /* Corrupt the root of the free list */
2570     assert(select_free_block(tr, 0) ==
2571            block_mac_to_block(tr, &tr->fs->free.block_tree.root));
2572     fs_corruption_helper(tr, select_free_block, 0, false);
2573     assert(tr->failed);
2574     assert(tr->invalid_block_found);
2575     expect_errors(TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH, 3);
2576 
2577     assert(fs_check_full(tr->fs) == FS_CHECK_INVALID_FREE_SET);
2578     expect_errors(TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH, 1);
2579 
2580     /* re-initialize the filesystem with recovery enabled */
2581     block_test_reinit(tr, FS_INIT_FLAGS_RECOVERY_CLEAR_ALLOWED);
2582     expect_errors(TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH, 1);
2583 
2584     /* Did we recover correctly? */
2585     create_and_delete(tr, "recovery");
2586     assert(!tr->invalid_block_found);
2587     assert(!tr->fs->needs_full_scan);
2588 }
2589 
fs_check_file_child_test(struct transaction * tr)2590 static void fs_check_file_child_test(struct transaction* tr) {
2591     /* Create lots of files */
2592     file_create_many_test(tr);
2593     transaction_complete(tr);
2594     assert(!tr->failed);
2595     transaction_activate(tr);
2596     assert(!tr->fs->needs_full_scan);
2597 
2598     /* Corrupt a child in the files list */
2599     fs_corruption_helper(tr, select_files_block, 1, false);
2600     /*
2601      * We corrupt a file tree block, but the file that fs_corruption_helper
2602      * probes is linked in a different file tree leaf and is unaffected by this,
2603      * so the transaction succeeds.
2604      */
2605 
2606     /* Ensure that we detect this corruption */
2607     assert(fs_check_full(tr->fs) == FS_CHECK_INVALID_BLOCK);
2608     assert(tr->fs->needs_full_scan);
2609     expect_errors(TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH, 2);
2610 
2611     /* re-initialize the filesystem with recovery enabled */
2612     block_test_reinit(tr, FS_INIT_FLAGS_RECOVERY_CLEAR_ALLOWED);
2613 
2614     /* recovery doesn't fix this error */
2615     assert(fs_check_full(tr->fs) == FS_CHECK_INVALID_BLOCK);
2616     assert(tr->fs->needs_full_scan);
2617     expect_errors(TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH, 2);
2618 
2619     transaction_fail(tr);
2620     block_test_reinit(tr, FS_INIT_FLAGS_DO_CLEAR);
2621 
2622     assert(fs_check_full(tr->fs) == FS_CHECK_NO_ERROR);
2623     assert(!tr->fs->needs_full_scan);
2624 }
2625 
fs_check_free_child_test(struct transaction * tr)2626 static void fs_check_free_child_test(struct transaction* tr) {
2627     /* Fragment the free list */
2628     allocate_frag_test(tr);
2629     transaction_complete(tr);
2630     assert(!tr->failed);
2631     transaction_activate(tr);
2632     assert(!tr->fs->needs_full_scan);
2633 
2634     /* Corrupt a child in the free list */
2635     fs_corruption_helper(tr, select_free_block, 1, false);
2636     assert(tr->failed);
2637     assert(tr->invalid_block_found);
2638     expect_errors(TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH, 4);
2639 
2640     /* Ensure that we detect this corruption */
2641     assert(fs_check_full(tr->fs) == FS_CHECK_INVALID_FREE_SET);
2642     assert(tr->fs->needs_full_scan);
2643     expect_errors(TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH, 2);
2644 
2645     /* re-initialize the filesystem with recovery enabled */
2646     block_test_reinit(tr, FS_INIT_FLAGS_RECOVERY_CLEAR_ALLOWED);
2647 
2648     /* recovery clear doesn't fix this error */
2649     assert(fs_check_full(tr->fs) == FS_CHECK_INVALID_FREE_SET);
2650     assert(tr->fs->needs_full_scan);
2651     expect_errors(TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH, 2);
2652 
2653     transaction_fail(tr);
2654     block_test_reinit(tr, FS_INIT_FLAGS_DO_CLEAR);
2655 
2656     assert(fs_check_full(tr->fs) == FS_CHECK_NO_ERROR);
2657     assert(!tr->fs->needs_full_scan);
2658 }
2659 
fs_check_sparse_file_test(struct transaction * tr)2660 static void fs_check_sparse_file_test(struct transaction* tr) {
2661     struct storage_file_handle file;
2662     int i;
2663     int* block_data_rw;
2664     struct obj_ref ref = OBJ_REF_INITIAL_VALUE(ref);
2665     size_t file_block_size = tr->fs->dev->block_size - sizeof(struct iv);
2666 
2667     open_test_file(tr, &file, "sparse_file", FILE_OPEN_CREATE);
2668     for (i = 0; i < 20; i += 5) {
2669         block_data_rw = file_get_block_write(tr, &file, i, true, &ref);
2670         assert(block_data_rw);
2671 
2672         block_data_rw[0] = i;
2673         block_data_rw[1] = ~i;
2674         file_block_put_dirty(tr, &file, i, block_data_rw, &ref);
2675     }
2676     file_set_size(tr, &file, i * file_block_size);
2677 
2678     transaction_complete(tr);
2679     assert(!tr->failed);
2680     transaction_activate(tr);
2681 
2682     assert(fs_check_full(tr->fs) == FS_CHECK_NO_ERROR);
2683     assert(!tr->fs->needs_full_scan);
2684 
2685     file_close(&file);
2686     file_delete(tr, "sparse_file", false);
2687 }
2688 
fs_corrupt_data_blocks_test(struct transaction * tr)2689 static void fs_corrupt_data_blocks_test(struct transaction* tr) {
2690     fs_corruption_helper(tr, select_data_block, 0, false);
2691     assert(!tr->failed);
2692     transaction_activate(tr);
2693 
2694     assert(fs_check_full(tr->fs) == FS_CHECK_NO_ERROR);
2695     assert(!tr->fs->needs_full_scan);
2696 
2697     /* file should still exist because we don't scan data blocks */
2698     file_test(tr, "recovery", FILE_OPEN_NO_CREATE, 0, 0, 0, true, 1);
2699     transaction_complete(tr);
2700     assert(!tr->failed);
2701     assert(!tr->invalid_block_found);
2702     transaction_activate(tr);
2703 
2704     /* we can delete files with corrupted data blocks */
2705     file_delete(tr, "recovery", false);
2706     transaction_complete(tr);
2707     assert(!tr->failed);
2708     transaction_activate(tr);
2709 
2710     create_and_delete(tr, "recovery");
2711     assert(!tr->invalid_block_found);
2712     assert(!tr->fs->needs_full_scan);
2713 }
2714 
fs_persist_needs_full_scan_test(struct transaction * tr)2715 static void fs_persist_needs_full_scan_test(struct transaction* tr) {
2716     const int* block_data_ro;
2717     struct storage_file_handle file;
2718     struct obj_ref ref = OBJ_REF_INITIAL_VALUE(ref);
2719 
2720     assert(!tr->fs->needs_full_scan);
2721     transaction_complete(tr);
2722 
2723     /* clear the FS to get a known empty starting point */
2724     block_test_reinit(tr, FS_INIT_FLAGS_DO_CLEAR);
2725 
2726     /* Corrupt a data block */
2727     fs_corruption_helper(tr, select_data_block, 0, false);
2728     assert(!tr->failed);
2729     assert(!tr->fs->needs_full_scan);
2730 
2731     /* Try to read back the corrupted block */
2732     open_test_file(tr, &file, "recovery", FILE_OPEN_NO_CREATE);
2733     block_data_ro = file_get_block(tr, &file, 0, &ref);
2734     assert(!block_data_ro);
2735     assert(tr->failed);
2736     assert(tr->fs->needs_full_scan);
2737     file_close(&file);
2738 
2739     /* Does the flag survive remount? */
2740     block_test_reinit(tr, FS_INIT_FLAGS_NONE);
2741     assert(tr->fs->needs_full_scan);
2742     transaction_complete(tr);
2743 
2744     /* And a switch back and forth to the alternate FS? */
2745     block_test_reinit(tr, FS_INIT_FLAGS_ALTERNATE_DATA);
2746     assert(tr->fs->needs_full_scan);
2747     transaction_complete(tr);
2748 
2749     block_test_reinit(tr, FS_INIT_FLAGS_NONE);
2750     assert(tr->fs->needs_full_scan);
2751     transaction_complete(tr);
2752 
2753     /* Clear the FS, clearing the scan flag. */
2754     block_test_reinit(tr, FS_INIT_FLAGS_DO_CLEAR);
2755     assert(!tr->fs->needs_full_scan);
2756 }
2757 
fs_recovery_clear_test(struct transaction * tr)2758 static void fs_recovery_clear_test(struct transaction* tr) {
2759     struct storage_file_handle file;
2760     file_test(tr, "recovery", FILE_OPEN_CREATE_EXCLUSIVE, file_test_block_count,
2761               0, 0, false, 1);
2762     transaction_complete(tr);
2763     assert(!tr->failed);
2764 
2765     /*
2766      * Backup, then clear and re-initialize the filesystem with only clear
2767      * recovery enabled.
2768      */
2769     block_test_swap_clear_reinit(tr, FS_INIT_FLAGS_RECOVERY_CLEAR_ALLOWED);
2770     expect_errors(TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH, 1);
2771 
2772     /* test file should be missing */
2773     open_test_file_etc(tr, &file, "recovery", FILE_OPEN_NO_CREATE,
2774                        FILE_OP_ERR_NOT_FOUND);
2775     transaction_complete(tr);
2776 
2777     block_test_swap_reinit(tr, FS_INIT_FLAGS_RECOVERY_CLEAR_ALLOWED);
2778 
2779     /* test file should NOT be back */
2780     open_test_file_etc(tr, &file, "recovery", FILE_OPEN_NO_CREATE,
2781                        FILE_OP_ERR_NOT_FOUND);
2782 }
2783 
fs_restore_nonexistent_checkpoint_test(struct transaction * tr)2784 static void fs_restore_nonexistent_checkpoint_test(struct transaction* tr) {
2785     /* ensure that there is no existing checkpoint */
2786     transaction_fail(tr);
2787     block_test_clear_superblock_reinit(tr, FS_INIT_FLAGS_NONE);
2788 
2789     file_test(tr, "nonexistent_checkpoint", FILE_OPEN_CREATE_EXCLUSIVE,
2790               file_test_block_count, 0, 0, false, 1);
2791     transaction_complete(tr);
2792     assert(!tr->failed);
2793 
2794     /* attempt to restore a non-existent checkpoint */
2795     block_test_reinit(tr, FS_INIT_FLAGS_RESTORE_CHECKPOINT);
2796 
2797     /* reads should work */
2798     file_test(tr, "nonexistent_checkpoint", FILE_OPEN_NO_CREATE, 0,
2799               file_test_block_count, 0, false, 1);
2800 
2801     /* but writes should fail */
2802     file_delete(tr, "nonexistent_checkpoint", allow_repaired);
2803     transaction_complete(tr);
2804     assert(tr->failed);
2805 
2806     block_test_reinit(tr, FS_INIT_FLAGS_NONE);
2807     file_delete(tr, "nonexistent_checkpoint", allow_repaired);
2808 }
2809 
fs_auto_checkpoint_test(struct transaction * tr)2810 static void fs_auto_checkpoint_test(struct transaction* tr) {
2811     /* ensure that there is no existing checkpoint */
2812     transaction_fail(tr);
2813     block_test_clear_superblock_reinit(tr, FS_INIT_FLAGS_NONE);
2814     assert(!block_mac_valid(tr, &tr->fs->checkpoint));
2815 
2816     /* Create lots of files */
2817     file_create_many_test(tr);
2818     transaction_complete(tr);
2819     assert(!tr->failed);
2820     transaction_activate(tr);
2821 
2822     fs_corruption_helper(tr, select_files_block, 1, false);
2823 
2824     assert(fs_check_full(tr->fs) == FS_CHECK_INVALID_BLOCK);
2825     expect_errors(TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH, 2);
2826 
2827     /* test that we don't checkpoint a corrupt FS */
2828     block_test_reinit(tr, FS_INIT_FLAGS_AUTO_CHECKPOINT);
2829     expect_errors(TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH, 2);
2830     assert(!block_mac_valid(tr, &tr->fs->checkpoint));
2831 
2832     transaction_fail(tr);
2833     block_test_clear_superblock_reinit(tr, FS_INIT_FLAGS_NONE);
2834     assert(!block_mac_valid(tr, &tr->fs->checkpoint));
2835 
2836     file_test(tr, "auto_checkpoint", FILE_OPEN_CREATE_EXCLUSIVE,
2837               file_test_block_count, 0, 0, false, 1);
2838     transaction_complete(tr);
2839     assert(!tr->failed);
2840 
2841     block_test_reinit(tr, FS_INIT_FLAGS_AUTO_CHECKPOINT);
2842     transaction_complete(tr);
2843     assert(!tr->failed);
2844     transaction_activate(tr);
2845     assert(block_mac_valid(tr, &tr->fs->checkpoint));
2846 
2847     file_delete(tr, "auto_checkpoint", allow_repaired);
2848     transaction_complete(tr);
2849     assert(!tr->failed);
2850 
2851     block_test_reinit(tr, FS_INIT_FLAGS_RESTORE_CHECKPOINT);
2852     /* globally acknowledge repair in test helpers */
2853     allow_repaired = true;
2854 
2855     file_test(tr, "auto_checkpoint", FILE_OPEN_NO_CREATE, 0,
2856               file_test_block_count, 0, true, 1);
2857     transaction_complete(tr);
2858     assert(!tr->failed);
2859 
2860     transaction_activate(tr);
2861 }
2862 
fs_recovery_restore_test(struct transaction * tr)2863 static void fs_recovery_restore_test(struct transaction* tr) {
2864     struct storage_file_handle file;
2865     enum file_op_result result;
2866 
2867     /* ensure that there is no existing checkpoint */
2868     transaction_fail(tr);
2869     block_test_clear_superblock_reinit(tr, FS_INIT_FLAGS_NONE);
2870 
2871     /* create empty checkpoint block */
2872     transaction_complete_update_checkpoint(tr);
2873 
2874     /* restore the empty checkpoint, do we still end up with a usable fs */
2875     full_assert(check_fs(tr));
2876     transaction_fail(tr);
2877     block_test_reinit(tr, FS_INIT_FLAGS_RESTORE_CHECKPOINT);
2878     full_assert(check_fs(tr));
2879 
2880     /* globally acknowledge repair in test helpers */
2881     allow_repaired = true;
2882 
2883     file_test(tr, "recovery_restore", FILE_OPEN_CREATE_EXCLUSIVE,
2884               file_test_block_count, 0, 0, false, 1);
2885     transaction_complete_update_checkpoint(tr);
2886     assert(!tr->failed);
2887     transaction_activate(tr);
2888 
2889     /* check and delete the file */
2890     file_test(tr, "recovery_restore", FILE_OPEN_NO_CREATE, 0,
2891               file_test_block_count, 0, true, 1);
2892     transaction_complete(tr);
2893     assert(!tr->failed);
2894     transaction_activate(tr);
2895 
2896     /* make sure it's gone */
2897     open_test_file_etc(tr, &file, "recovery_restore", FILE_OPEN_NO_CREATE,
2898                        FILE_OP_ERR_NOT_FOUND);
2899     file_test(tr, "recovery_not_in_checkpoint", FILE_OPEN_CREATE_EXCLUSIVE,
2900               file_test_block_count, 0, 0, false, 1);
2901     transaction_complete(tr);
2902     assert(!tr->failed);
2903 
2904     full_assert(check_fs(tr));
2905     block_test_reinit(tr, FS_INIT_FLAGS_RESTORE_CHECKPOINT);
2906     full_assert(check_fs(tr));
2907 
2908     /* globally acknowledge repair in test helpers */
2909     allow_repaired = true;
2910 
2911     /*
2912      * check and delete the file again (note this writes a new superblock,
2913      * persisting the repair flag)
2914      */
2915     file_test(tr, "recovery_restore", FILE_OPEN_NO_CREATE, 0,
2916               file_test_block_count, 0, true, 1);
2917 
2918     result = file_open(tr, "recovery_not_in_checkpoint", &file,
2919                        FILE_OPEN_NO_CREATE, false);
2920     assert(result == FILE_OP_ERR_FS_REPAIRED);
2921     result = file_open(tr, "recovery_not_in_checkpoint", &file,
2922                        FILE_OPEN_NO_CREATE, true);
2923     assert(result == FILE_OP_ERR_NOT_FOUND);
2924 }
2925 
2926 /* Attempt to restore the checkpoint again */
fs_recovery_restore_test2(struct transaction * tr)2927 static void fs_recovery_restore_test2(struct transaction* tr) {
2928     struct storage_file_handle file;
2929     enum file_op_result result;
2930 
2931     /* this file should only be in the checkpoint */
2932     result =
2933             file_open(tr, "recovery_restore", &file, FILE_OPEN_NO_CREATE, true);
2934     assert(result == FILE_OP_ERR_NOT_FOUND);
2935     transaction_complete(tr);
2936     assert(!tr->failed);
2937 
2938     full_assert(check_fs(tr));
2939     block_test_reinit(tr, FS_INIT_FLAGS_RESTORE_CHECKPOINT);
2940     full_assert(check_fs(tr));
2941 
2942     /* check that the file is back again (and delete it) */
2943     file_test(tr, "recovery_restore", FILE_OPEN_NO_CREATE, 0,
2944               file_test_block_count, 0, true, 1);
2945 
2946     /* but this one isn't */
2947     result = file_open(tr, "recovery_not_in_checkpoint", &file,
2948                        FILE_OPEN_NO_CREATE, false);
2949     assert(result == FILE_OP_ERR_FS_REPAIRED);
2950     result = file_open(tr, "recovery_not_in_checkpoint", &file,
2951                        FILE_OPEN_NO_CREATE, true);
2952     assert(result == FILE_OP_ERR_NOT_FOUND);
2953 }
2954 
fs_recovery_restore_cleanup(struct transaction * tr)2955 static void fs_recovery_restore_cleanup(struct transaction* tr) {
2956     transaction_complete(tr);
2957     /* clear the FS to reset the repair flag */
2958     block_test_reinit(tr, FS_INIT_FLAGS_DO_CLEAR);
2959     transaction_complete_update_checkpoint(tr);
2960     transaction_activate(tr);
2961     allow_repaired = false;
2962 }
2963 
2964 /*
2965  * Main and Alternate data states:
2966  * a) empty superblock, empty backing file
2967  * b) empty superblock, uncommitted backing file
2968  * c) non-empty superblock, non-empty backing file
2969  * d) non-empty superblock, empty backing file
2970  *
2971  * Transitions:
2972  * Main a-d -> Alternate a-c
2973  * Alternate a-c -> Alternate a-c
2974  * Alternate a-c -> Main a-c
2975  *
2976  * (d is not listed as a transition target as it should be replaced by an empty
2977  * superblock on init)
2978  */
2979 
fs_alternate_negative_test(struct transaction * tr)2980 static void fs_alternate_negative_test(struct transaction* tr) {
2981     struct storage_file_handle file;
2982 
2983     /* Initialize and commit a file to the FS */
2984     file_test(tr, "main", FILE_OPEN_CREATE_EXCLUSIVE, file_test_block_count, 0,
2985               0, false, 1);
2986     transaction_complete(tr);
2987     assert(!tr->failed);
2988     transaction_activate(tr);
2989 
2990     open_test_file_etc(tr, &file, "main", FILE_OPEN_NO_CREATE, FILE_OP_SUCCESS);
2991     file_close(&file);
2992     transaction_complete(tr);
2993     assert(!tr->failed);
2994 
2995     /* Swap and clear backing file without using alternate superblock */
2996     block_test_swap_clear_reinit(tr, FS_INIT_FLAGS_DO_CLEAR);
2997 
2998     /* Ensure that the file is missing */
2999     open_test_file_etc(tr, &file, "main", FILE_OPEN_NO_CREATE,
3000                        FILE_OP_ERR_NOT_FOUND);
3001 
3002     /* Flush the cleared superblock here */
3003     transaction_complete(tr);
3004 
3005     block_test_swap_reinit(tr, FS_INIT_FLAGS_NONE);
3006 
3007     /*
3008      * Ensure that the file is still missing (i.e. we did not create and restore
3009      * a backup)
3010      */
3011     open_test_file_etc(tr, &file, "main", FILE_OPEN_NO_CREATE,
3012                        FILE_OP_ERR_NOT_FOUND);
3013     transaction_fail(tr);
3014 
3015     block_test_swap_clear_reinit(tr, FS_INIT_FLAGS_DO_CLEAR);
3016 }
3017 
3018 /*
3019  * Test that we can correctly alternate between a non-empty FS and an alternate
3020  * FS.
3021  * Tests the sequence:
3022  *     Main c -> Alternate a -> Alternate a -> Alternate b -> Main c ->
3023  *     Alternate b -> Alternate c -> Main c -> Alternate c
3024  */
fs_alternate_test(struct transaction * tr)3025 static void fs_alternate_test(struct transaction* tr) {
3026     struct storage_file_handle file;
3027 
3028     /* Initialize and commit a file to the FS */
3029     file_test(tr, "main", FILE_OPEN_CREATE_EXCLUSIVE, file_test_block_count, 0,
3030               0, false, 1);
3031     transaction_complete(tr);
3032     assert(!tr->failed);
3033     transaction_activate(tr);
3034 
3035     open_test_file_etc(tr, &file, "main", FILE_OPEN_NO_CREATE, FILE_OP_SUCCESS);
3036     file_close(&file);
3037     transaction_complete(tr);
3038     assert(!tr->failed);
3039 
3040     /* reboot into alternate and clear */
3041     block_test_swap_clear_reinit(
3042             tr, FS_INIT_FLAGS_DO_CLEAR | FS_INIT_FLAGS_ALTERNATE_DATA);
3043 
3044     /* test file should be missing */
3045     open_test_file_etc(tr, &file, "main", FILE_OPEN_NO_CREATE,
3046                        FILE_OP_ERR_NOT_FOUND);
3047     transaction_fail(tr);
3048 
3049     /*
3050      * simulate a reboot with an empty backing file, staying in alternate
3051      * mode
3052      */
3053     block_test_reinit(tr,
3054                       FS_INIT_FLAGS_DO_CLEAR | FS_INIT_FLAGS_ALTERNATE_DATA);
3055 
3056     /* test file should still be missing */
3057     open_test_file_etc(tr, &file, "main", FILE_OPEN_NO_CREATE,
3058                        FILE_OP_ERR_NOT_FOUND);
3059     transaction_fail(tr);
3060     transaction_activate(tr);
3061 
3062     /* flush blocks to disk so the backing store is non-empty */
3063     file_test_etc(tr, false, "alternate", FILE_OPEN_CREATE_EXCLUSIVE, "",
3064                   FILE_OPEN_NO_CREATE, 80, 0, 0, false, 1);
3065     transaction_fail(tr);
3066 
3067     /* simulate a reboot with a cleared superblock but non-empty backing file */
3068     block_test_reinit(tr, FS_INIT_FLAGS_ALTERNATE_DATA);
3069 
3070     open_test_file_etc(tr, &file, "alternate", FILE_OPEN_NO_CREATE,
3071                        FILE_OP_ERR_NOT_FOUND);
3072     transaction_fail(tr);
3073 
3074     /* simulate a reboot, switching to main mode */
3075     block_test_swap_reinit(tr, FS_INIT_FLAGS_NONE);
3076 
3077     /* test file should be available */
3078     open_test_file_etc(tr, &file, "main", FILE_OPEN_NO_CREATE, FILE_OP_SUCCESS);
3079     file_close(&file);
3080     transaction_complete(tr);
3081     assert(!tr->failed);
3082 
3083     /* and alternate test file should not be */
3084     open_test_file_etc(tr, &file, "alternate", FILE_OPEN_NO_CREATE,
3085                        FILE_OP_ERR_NOT_FOUND);
3086     transaction_fail(tr);
3087 
3088     /* simulate a reboot, switching to alternate mode */
3089     block_test_swap_reinit(tr, FS_INIT_FLAGS_ALTERNATE_DATA);
3090 
3091     /* main test file should not exist */
3092     open_test_file_etc(tr, &file, "main", FILE_OPEN_NO_CREATE,
3093                        FILE_OP_ERR_NOT_FOUND);
3094     transaction_fail(tr);
3095     transaction_activate(tr);
3096 
3097     /* write a file */
3098     file_test(tr, "alternate", FILE_OPEN_CREATE_EXCLUSIVE,
3099               file_test_block_count, 0, 0, false, 1);
3100     transaction_complete(tr);
3101     assert(!tr->failed);
3102     transaction_activate(tr);
3103 
3104     open_test_file_etc(tr, &file, "alternate", FILE_OPEN_NO_CREATE,
3105                        FILE_OP_SUCCESS);
3106     file_close(&file);
3107     transaction_complete(tr);
3108     assert(!tr->failed);
3109 
3110     /* simulate reboot, still in alternate mode */
3111     block_test_reinit(tr, FS_INIT_FLAGS_ALTERNATE_DATA);
3112 
3113     open_test_file_etc(tr, &file, "alternate", FILE_OPEN_NO_CREATE,
3114                        FILE_OP_SUCCESS);
3115     file_close(&file);
3116     transaction_complete(tr);
3117     assert(!tr->failed);
3118 
3119     /* simulate reboot back into main mode */
3120     block_test_swap_reinit(tr, FS_INIT_FLAGS_NONE);
3121 
3122     /* test file should be back */
3123     open_test_file_etc(tr, &file, "main", FILE_OPEN_NO_CREATE, FILE_OP_SUCCESS);
3124     file_close(&file);
3125     transaction_complete(tr);
3126     assert(!tr->failed);
3127     transaction_activate(tr);
3128 
3129     /* and alternate file should be gone */
3130     open_test_file_etc(tr, &file, "alternate", FILE_OPEN_NO_CREATE,
3131                        FILE_OP_ERR_NOT_FOUND);
3132     transaction_fail(tr);
3133 
3134     /* simulate reboot back into alternate */
3135     block_test_swap_reinit(tr, FS_INIT_FLAGS_ALTERNATE_DATA);
3136 
3137     /* alternate test file should be back */
3138     open_test_file_etc(tr, &file, "alternate", FILE_OPEN_NO_CREATE,
3139                        FILE_OP_SUCCESS);
3140     file_close(&file);
3141     transaction_complete(tr);
3142     assert(!tr->failed);
3143     transaction_activate(tr);
3144 
3145     /* and regular file should be gone */
3146     open_test_file_etc(tr, &file, "main", FILE_OPEN_NO_CREATE,
3147                        FILE_OP_ERR_NOT_FOUND);
3148     transaction_fail(tr);
3149 
3150     block_test_swap_clear_reinit(tr, FS_INIT_FLAGS_DO_CLEAR);
3151 }
3152 
3153 /*
3154  * Test that we can correctly backup from and recover a empty FS state.
3155  * Tests the sequence:
3156  *     Main a -> Alternate a -> Alternate c -> Main a -> Alternate c -> Main a
3157  *     -> Main b -> Alternate a -> Alternate c -> Main b
3158  */
fs_alternate_empty_test(struct transaction * tr)3159 static void fs_alternate_empty_test(struct transaction* tr) {
3160     struct storage_file_handle file;
3161     transaction_fail(tr);
3162 
3163     /* clear main fs */
3164     block_test_swap_clear_reinit(tr, FS_INIT_FLAGS_DO_CLEAR);
3165 
3166     /* Ensure that the file is missing */
3167     open_test_file_etc(tr, &file, "main", FILE_OPEN_NO_CREATE,
3168                        FILE_OP_ERR_NOT_FOUND);
3169     transaction_fail(tr);
3170 
3171     /* swap to alternate and clear */
3172     block_test_swap_clear_reinit(
3173             tr, FS_INIT_FLAGS_DO_CLEAR | FS_INIT_FLAGS_ALTERNATE_DATA);
3174 
3175     /* Ensure that the file is still missing */
3176     open_test_file_etc(tr, &file, "main", FILE_OPEN_NO_CREATE,
3177                        FILE_OP_ERR_NOT_FOUND);
3178     transaction_fail(tr);
3179     transaction_activate(tr);
3180 
3181     /* write a file */
3182     file_test(tr, "alternate", FILE_OPEN_CREATE_EXCLUSIVE,
3183               file_test_block_count, 0, 0, false, 1);
3184     transaction_complete(tr);
3185     assert(!tr->failed);
3186     transaction_activate(tr);
3187 
3188     open_test_file_etc(tr, &file, "alternate", FILE_OPEN_NO_CREATE,
3189                        FILE_OP_SUCCESS);
3190     file_close(&file);
3191     transaction_complete(tr);
3192     assert(!tr->failed);
3193 
3194     /* reboot back to alternate with data */
3195     block_test_reinit(tr, FS_INIT_FLAGS_ALTERNATE_DATA);
3196 
3197     open_test_file_etc(tr, &file, "alternate", FILE_OPEN_NO_CREATE,
3198                        FILE_OP_SUCCESS);
3199     file_close(&file);
3200     transaction_complete(tr);
3201     assert(!tr->failed);
3202 
3203     /* reboot to cleared main */
3204     block_test_swap_reinit(tr, FS_INIT_FLAGS_NONE);
3205 
3206     /* Ensure that the file is missing */
3207     open_test_file_etc(tr, &file, "alternate", FILE_OPEN_NO_CREATE,
3208                        FILE_OP_ERR_NOT_FOUND);
3209     transaction_fail(tr);
3210 
3211     /* reboot to alternate to check that our data is still there */
3212     block_test_swap_reinit(tr, FS_INIT_FLAGS_ALTERNATE_DATA);
3213 
3214     open_test_file_etc(tr, &file, "alternate", FILE_OPEN_NO_CREATE,
3215                        FILE_OP_SUCCESS);
3216     file_close(&file);
3217     transaction_complete(tr);
3218     assert(!tr->failed);
3219 
3220     /* reboot back to cleared main */
3221     block_test_swap_reinit(tr, FS_INIT_FLAGS_DO_CLEAR);
3222 
3223     /* flush blocks to disk so we have a non-empty backing file */
3224     file_test_etc(tr, false, "main", FILE_OPEN_CREATE_EXCLUSIVE, "",
3225                   FILE_OPEN_NO_CREATE, 80, 0, 0, false, 1);
3226     transaction_fail(tr);
3227 
3228     /* reboot with a non-empty backing file and cleared main superblock */
3229     block_test_reinit(tr, FS_INIT_FLAGS_NONE);
3230 
3231     /* Ensure that the file is still missing */
3232     open_test_file_etc(tr, &file, "main", FILE_OPEN_NO_CREATE,
3233                        FILE_OP_ERR_NOT_FOUND);
3234     transaction_fail(tr);
3235 
3236     /* reboot to alternate, clearing */
3237     block_test_swap_reinit(
3238             tr, FS_INIT_FLAGS_DO_CLEAR | FS_INIT_FLAGS_ALTERNATE_DATA);
3239 
3240     /* write a file */
3241     file_test(tr, "alternate", FILE_OPEN_CREATE_EXCLUSIVE,
3242               file_test_block_count, 0, 0, false, 1);
3243     transaction_complete(tr);
3244     assert(!tr->failed);
3245     transaction_activate(tr);
3246 
3247     open_test_file_etc(tr, &file, "alternate", FILE_OPEN_NO_CREATE,
3248                        FILE_OP_SUCCESS);
3249     file_close(&file);
3250     transaction_complete(tr);
3251     assert(!tr->failed);
3252 
3253     /* reboot to non-empty alternate */
3254     block_test_reinit(tr, FS_INIT_FLAGS_ALTERNATE_DATA);
3255 
3256     transaction_fail(tr);
3257 
3258     /* reboot to empty main with non-empty backing file */
3259     block_test_swap_reinit(tr, FS_INIT_FLAGS_NONE);
3260 
3261     /* write a file */
3262     file_test(tr, "main", FILE_OPEN_CREATE_EXCLUSIVE, file_test_block_count, 0,
3263               0, false, 1);
3264     transaction_complete(tr);
3265     assert(!tr->failed);
3266     transaction_activate(tr);
3267 
3268     open_test_file_etc(tr, &file, "main", FILE_OPEN_NO_CREATE, FILE_OP_SUCCESS);
3269     file_close(&file);
3270     transaction_complete(tr);
3271     assert(!tr->failed);
3272 
3273     block_test_swap_clear_reinit(tr, FS_INIT_FLAGS_DO_CLEAR);
3274 }
3275 
3276 /*
3277  * Tests the interaction between alternate data and corruption recovery.
3278  * Tests the sequence:
3279  *     Main c -> Alternate a -> Alternate c -> Recover from corrupt alternate ->
3280  *     Main c -> Recover from corrupt main -> Alternate c
3281  */
fs_alternate_recovery_test(struct transaction * tr)3282 static void fs_alternate_recovery_test(struct transaction* tr) {
3283     data_block_t block;
3284     struct storage_file_handle file;
3285     struct fs* fs = tr->fs;
3286 
3287     file_test(tr, "recovery_main", FILE_OPEN_CREATE_EXCLUSIVE,
3288               file_test_block_count, 0, 0, false, 1);
3289     transaction_complete(tr);
3290     assert(!tr->failed);
3291     transaction_activate(tr);
3292 
3293     open_test_file_etc(tr, &file, "recovery_main", FILE_OPEN_NO_CREATE,
3294                        FILE_OP_SUCCESS);
3295     file_close(&file);
3296     transaction_complete(tr);
3297     assert(!tr->failed);
3298 
3299     block_test_swap_clear_reinit(
3300             tr, FS_INIT_FLAGS_DO_CLEAR | FS_INIT_FLAGS_ALTERNATE_DATA);
3301 
3302     file_test(tr, "recovery_alternate", FILE_OPEN_CREATE_EXCLUSIVE,
3303               file_test_block_count, 0, 0, false, 1);
3304     transaction_complete(tr);
3305     assert(!tr->failed);
3306     transaction_activate(tr);
3307 
3308     open_test_file_etc(tr, &file, "recovery_alternate", FILE_OPEN_NO_CREATE,
3309                        FILE_OP_SUCCESS);
3310     file_close(&file);
3311     transaction_complete(tr);
3312     assert(!tr->failed);
3313 
3314     /* Corrupt the files root block */
3315     block = block_mac_to_block(tr, &fs->files.root);
3316     memset(&blocks[block], 0, sizeof(struct block));
3317     block_cache_dev_destroy(fs->dev);
3318 
3319     transaction_activate(tr);
3320     open_test_file_etc(tr, &file, "recovery_alternate", FILE_OPEN_NO_CREATE,
3321                        FILE_OP_ERR_FAILED);
3322     transaction_complete(tr);
3323     assert(tr->failed);
3324     expect_errors(TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH, 1);
3325 
3326     /* re-initialize the filesystem without recovery enabled */
3327     block_test_reinit(tr, FS_INIT_FLAGS_ALTERNATE_DATA);
3328     expect_errors(TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH, 1);
3329 
3330     open_test_file_etc(tr, &file, "recovery_alternate",
3331                        FILE_OPEN_CREATE_EXCLUSIVE, FILE_OP_ERR_FAILED);
3332     transaction_complete(tr);
3333     assert(tr->failed);
3334     expect_errors(TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH, 1);
3335 
3336     /* re-initialize the filesystem with recovery enabled */
3337     block_test_reinit(tr, FS_INIT_FLAGS_RECOVERY_CLEAR_ALLOWED |
3338                                   FS_INIT_FLAGS_ALTERNATE_DATA);
3339     expect_errors(TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH, 1);
3340 
3341     file_test(tr, "recovery_alternate", FILE_OPEN_CREATE_EXCLUSIVE,
3342               file_test_block_count, 0, 0, false, 1);
3343     transaction_complete(tr);
3344     assert(!tr->failed);
3345 
3346     /* Swap to main and verify that our file still exists */
3347     block_test_swap_reinit(tr, FS_INIT_FLAGS_NONE);
3348 
3349     /* alternate test file should be missing */
3350     open_test_file_etc(tr, &file, "recovery_alternate", FILE_OPEN_NO_CREATE,
3351                        FILE_OP_ERR_NOT_FOUND);
3352     transaction_complete(tr);
3353     transaction_activate(tr);
3354 
3355     /*
3356      * Main test file should exist. We write to it to force the pending
3357      * superblock to get written before we do the corruption to avoid tripping a
3358      * dirty transaction assert
3359      */
3360     file_test(tr, "recovery_main", FILE_OPEN_NO_CREATE, file_test_block_count,
3361               0, 0, false, 2);
3362     transaction_complete(tr);
3363     assert(!tr->failed);
3364 
3365     /* Corrupt the files root block */
3366     block = block_mac_to_block(tr, &fs->files.root);
3367     memset(&blocks[block], 0, sizeof(struct block));
3368     block_cache_dev_destroy(fs->dev);
3369 
3370     transaction_activate(tr);
3371     open_test_file_etc(tr, &file, "recovery_main", FILE_OPEN_NO_CREATE,
3372                        FILE_OP_ERR_FAILED);
3373     transaction_complete(tr);
3374     assert(tr->failed);
3375     expect_errors(TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH, 1);
3376 
3377     /* re-initialize the filesystem without recovery enabled */
3378     block_test_reinit(tr, FS_INIT_FLAGS_NONE);
3379     expect_errors(TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH, 1);
3380 
3381     open_test_file_etc(tr, &file, "recovery_main", FILE_OPEN_CREATE_EXCLUSIVE,
3382                        FILE_OP_ERR_FAILED);
3383     transaction_complete(tr);
3384     assert(tr->failed);
3385     expect_errors(TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH, 1);
3386 
3387     /* re-initialize the filesystem with recovery enabled */
3388     block_test_reinit(tr, FS_INIT_FLAGS_RECOVERY_CLEAR_ALLOWED);
3389     expect_errors(TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH, 1);
3390 
3391     file_test(tr, "recovery_main", FILE_OPEN_CREATE_EXCLUSIVE,
3392               file_test_block_count, 0, 0, false, 1);
3393     transaction_complete(tr);
3394     assert(!tr->failed);
3395 
3396     /* Swap to alternate and verify that our file still exists */
3397     block_test_swap_reinit(tr, FS_INIT_FLAGS_ALTERNATE_DATA);
3398 
3399     /* alternate test file should exist */
3400     open_test_file_etc(tr, &file, "recovery_alternate", FILE_OPEN_NO_CREATE,
3401                        FILE_OP_SUCCESS);
3402     file_close(&file);
3403     transaction_complete(tr);
3404     assert(!tr->failed);
3405 
3406     /*
3407      * Swap data back to main to finish off the test so we don't end up with a
3408      * mismatch during cleanup
3409      */
3410     block_test_swap_reinit(tr, FS_INIT_FLAGS_NONE);
3411 }
3412 
fs_alternate_init_test(struct transaction * tr)3413 static void fs_alternate_init_test(struct transaction* tr) {
3414     struct storage_file_handle file;
3415     transaction_fail(tr);
3416 
3417     block_test_clear_superblock_reinit(tr, FS_INIT_FLAGS_NONE);
3418 
3419     /* Initialize and commit a file to the FS */
3420     file_test(tr, "main", FILE_OPEN_CREATE_EXCLUSIVE, file_test_block_count, 0,
3421               0, false, 1);
3422     transaction_complete(tr);
3423     assert(!tr->failed);
3424     transaction_activate(tr);
3425 
3426     open_test_file_etc(tr, &file, "main", FILE_OPEN_NO_CREATE, FILE_OP_SUCCESS);
3427     file_close(&file);
3428     transaction_complete(tr);
3429     assert(!tr->failed);
3430 
3431     /* reboot into alternate but do not pass clear flag */
3432     block_test_swap_clear_reinit(tr, FS_INIT_FLAGS_ALTERNATE_DATA);
3433 
3434     /* test file should be missing */
3435     open_test_file_etc(tr, &file, "main", FILE_OPEN_NO_CREATE,
3436                        FILE_OP_ERR_NOT_FOUND);
3437     transaction_fail(tr);
3438     transaction_activate(tr);
3439 
3440     /* Initialize and commit a file to the FS */
3441     file_test(tr, "alternate", FILE_OPEN_CREATE_EXCLUSIVE,
3442               file_test_block_count, 0, 0, false, 1);
3443     transaction_complete(tr);
3444     assert(!tr->failed);
3445     transaction_activate(tr);
3446 
3447     open_test_file_etc(tr, &file, "alternate", FILE_OPEN_NO_CREATE,
3448                        FILE_OP_SUCCESS);
3449     file_close(&file);
3450     transaction_complete(tr);
3451     assert(!tr->failed);
3452 
3453     /* reboot into main */
3454     block_test_swap_reinit(tr, FS_INIT_FLAGS_NONE);
3455 
3456     open_test_file_etc(tr, &file, "main", FILE_OPEN_NO_CREATE, FILE_OP_SUCCESS);
3457     file_close(&file);
3458 }
3459 
3460 #if 0
3461 static void file_allocate_leave_10_test2(struct transaction *tr)
3462 {
3463     int i;
3464     for (i = 1; i < 10; i++) {
3465         file_allocate_all_test(tr, 1, 0, 1, "test1", FILE_OPEN_NO_CREATE);
3466         file_allocate_all_test(tr, 1, 1, i, "test2", FILE_OPEN_CREATE);
3467         file_delete(tr, "test2", false);
3468         transaction_complete(tr);
3469         assert(!tr->failed);
3470         transaction_activate(tr);
3471     }
3472 }
3473 #endif
3474 
3475 #define TEST(a, ...) \
3476     { .name = #a, .func = (a), ##__VA_ARGS__ }
3477 struct {
3478     const char* name;
3479     bool no_free_check;
3480     void (*func)(struct transaction* tr);
3481 } tests[] = {
3482         TEST(empty_test),
3483         TEST(empty_test),
3484         TEST(block_tree_test),
3485         TEST(block_set_test),
3486         TEST(block_map_test),
3487         TEST(allocate_frag_test, .no_free_check = true),
3488         TEST(allocate_free_same_test, .no_free_check = true),
3489         TEST(allocate_free_other_test, .no_free_check = true),
3490         TEST(free_frag_rem_test, .no_free_check = true),
3491         TEST(free_test),
3492         TEST(allocate_2_transactions_test, .no_free_check = true),
3493         TEST(free_2_transactions_same_test, .no_free_check = true),
3494         TEST(free_2_transactions_same_test_2),
3495         TEST(allocate_all_test),
3496         TEST(block_tree_allocate_all_test),
3497         TEST(super_block_write_failure_test),
3498         TEST(block_put_dirty_discard_test),
3499         TEST(file_create1_small_test),
3500         TEST(file_write1_small_test),
3501         TEST(file_delete1_small_test),
3502         TEST(file_read_after_delete_test),
3503         TEST(file_create1_small_test),
3504         TEST(file_splittr1_small_test),
3505         TEST(file_delete1_small_test),
3506         TEST(file_create1_small_test),
3507         TEST(file_splittr1o4_small_test),
3508         TEST(file_delete1_small_test),
3509         TEST(file_create_write_delete1_small_test),
3510         TEST(file_splittr1c_small_test),
3511         TEST(file_splittr1o4c_small_test),
3512         TEST(file_splittr1o4cl_small_test),
3513         TEST(file_create1_test),
3514         TEST(file_write1h_test),
3515         TEST(file_write1_test),
3516         TEST(file_delete1_test),
3517         TEST(file_create2_test),
3518         TEST(file_delete2_test),
3519         TEST(file_create2_read_after_commit_test),
3520         TEST(file_delete2_test),
3521         TEST(file_move_test),
3522         TEST(file_create1_test),
3523         TEST(file_write1_test),
3524         TEST(file_move12_test),
3525         TEST(file_move21_test),
3526         TEST(file_delete1_test),
3527         TEST(file_create3_conflict_test),
3528         TEST(file_create_delete_2_transaction_test),
3529         TEST(file_create_many_test),
3530         TEST(file_create1_small_test),
3531         TEST(file_write1_small_test),
3532         TEST(file_write1_small_test),
3533         TEST(file_delete1_small_test),
3534         TEST(file_iterate_many_test),
3535         TEST(file_delete_many_test),
3536         TEST(file_create1_test),
3537         TEST(file_allocate_all1_test),
3538         TEST(file_write1_test),
3539         TEST(file_delete1_no_free_test),
3540         TEST(file_allocate_all1_test),
3541         TEST(file_create_all_test),
3542         TEST(file_create1_test),
3543         TEST(file_write1_test),
3544         TEST(file_delete_create_write1_test),
3545         TEST(file_write1_test),
3546         TEST(file_delete1_test),
3547         TEST(file_allocate_all_2tr_1_test),
3548         TEST(file_allocate_all_8tr_1_test),
3549         TEST(file_create1_test),
3550         TEST(file_allocate_all_complete1_test),
3551         TEST(file_delete1_no_free_test),
3552         TEST(file_allocate_all_complete1_test),
3553         TEST(file_delete1_no_free_test),
3554         TEST(file_create1_test),
3555         TEST(file_allocate_all_complete_multi1_test),
3556         TEST(file_delete1_no_free_test),
3557         TEST(file_allocate_all_complete_multi1_test),
3558         TEST(file_delete1_no_free_test),
3559         TEST(file_create1_test),
3560         TEST(file_allocate_leave_10_test),
3561         TEST(file_create1_small_test),
3562         TEST(file_write1_small_test),
3563         TEST(file_delete1_small_test),
3564         TEST(file_create1_small_test),
3565         TEST(file_splittr1_small_test),
3566         TEST(file_delete1_small_test),
3567         TEST(file_create1_small_test),
3568         TEST(file_splittr1o4_small_test),
3569         TEST(file_delete1_small_test),
3570         TEST(file_allocate_all1_test),
3571         //    TEST(file_write1_test),
3572         //    TEST(file_allocate_leave_10_test2),
3573         TEST(file_delete1_no_free_test),
3574         TEST(fs_create_checkpoint),
3575         TEST(fs_modify_with_checkpoint),
3576         TEST(fs_clear_checkpoint),
3577         TEST(fs_rebuild_free_set),
3578         TEST(fs_rebuild_fragmented_free_set),
3579         TEST(fs_rebuild_with_pending_file),
3580         TEST(fs_rebuild_with_pending_transaction),
3581         TEST(fs_repair_flag),
3582         TEST(fs_repair_with_alternate),
3583         TEST(future_fs_version_test),
3584         TEST(unknown_required_flags_test),
3585         TEST(fs_recovery_clear_roots_test),
3586         TEST(fs_check_file_child_test),
3587         TEST(fs_check_free_child_test),
3588         TEST(fs_check_sparse_file_test),
3589         TEST(fs_corrupt_data_blocks_test),
3590         TEST(fs_persist_needs_full_scan_test),
3591         TEST(fs_recovery_clear_test),
3592         TEST(fs_restore_nonexistent_checkpoint_test),
3593         TEST(fs_auto_checkpoint_test),
3594         TEST(fs_recovery_restore_cleanup),
3595         TEST(fs_recovery_restore_test),
3596         TEST(fs_recovery_restore_test2),
3597         TEST(fs_recovery_restore_cleanup),
3598         TEST(fs_alternate_negative_test),
3599         TEST(fs_alternate_test),
3600         TEST(fs_alternate_empty_test),
3601         TEST(fs_alternate_recovery_test),
3602         TEST(fs_alternate_init_test),
3603 };
3604 
main(int argc,const char * argv[])3605 int main(int argc, const char* argv[]) {
3606     // struct block_set_node *node;
3607     struct block_device dev = {
3608             .start_read = block_test_start_read,
3609             .start_write = block_test_start_write,
3610             .block_count = BLOCK_COUNT,
3611             .block_size = 256,
3612             .block_num_size = 8,
3613             .mac_size = 16,
3614             .tamper_detecting = true,
3615             .io_ops = LIST_INITIAL_VALUE(dev.io_ops),
3616     };
3617     struct block_device dev256 = {
3618             .start_read = block_test_start_read,
3619             .start_write = block_test_start_write,
3620             .block_count = 0x10000,
3621             .block_size = 256,
3622             .block_num_size = 2,
3623             .mac_size = 2,
3624             .tamper_detecting = true,
3625             .io_ops = LIST_INITIAL_VALUE(dev256.io_ops),
3626     };
3627     struct fs fs = {
3628             .dev = &dev,
3629             .transactions = LIST_INITIAL_VALUE(fs.transactions),
3630             .allocated = LIST_INITIAL_VALUE(fs.allocated),
3631             .files =
3632                     {
3633                             .copy_on_write = true,
3634                             //.allow_copy_on_write = true,
3635                     },
3636     };
3637     struct transaction tr = {};
3638     unsigned int i;
3639     bool test_remount = true;
3640 
3641     if (argc > 1) {
3642         print_lookup = true;
3643     }
3644 
3645     assert(test_free_start < test_free_split);
3646     assert(test_free_split < test_free_end);
3647 
3648     stats_timer_reset();
3649 
3650     block_tree_check_config(&dev);
3651     block_tree_check_config(&dev256);
3652     block_tree_check_config_done();
3653     crypt_init();
3654     block_cache_init();
3655 
3656     fs_init(&fs, FILE_SYSTEM_TEST, &key, &dev, &dev, FS_INIT_FLAGS_DO_CLEAR);
3657     fs.reserved_count = 18; /* HACK: override default reserved space */
3658     transaction_init(&tr, &fs, false);
3659 
3660     for (i = 0; i < countof(tests); i++) {
3661         mock_error_report_clear();
3662         transaction_activate(&tr);
3663         printf("%s: start test: %s\n", __func__, tests[i].name);
3664         tests[i].func(&tr);
3665         transaction_complete(&tr);
3666         assert(!block_cache_debug_get_ref_block_count());
3667         if (!tests[i].no_free_check) {
3668             full_assert(check_fs(&tr));
3669         }
3670         if (0) {  // per test stats
3671             stats_timer_print();
3672             stats_timer_reset();
3673         }
3674         printf("%s: test done: %s\n", __func__, tests[i].name);
3675         assert(!tr.failed);
3676         if (test_remount) {
3677             transaction_free(&tr);
3678             fs_destroy(&fs);
3679             block_cache_dev_destroy(&dev);
3680             fs_init(&fs, FILE_SYSTEM_TEST, &key, &dev, &dev,
3681                     FS_INIT_FLAGS_NONE);
3682             fs.reserved_count = 18; /* HACK: override default reserved space */
3683             transaction_init(&tr, &fs, false);
3684         }
3685     }
3686     full_assert(check_fs(&tr));
3687     files_print(&tr);
3688     block_set_print(&tr, &tr.fs->free);
3689     stats_timer_print();
3690     transaction_free(&tr);
3691     fs_destroy(&fs);
3692     block_cache_dev_destroy(&dev);
3693     crypt_shutdown();
3694 
3695     printf("%s: done\n", __func__);
3696 
3697     return 0;
3698 }
3699