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