From 14884808b5ee426b37ccb497dd2e262a03070b1c Mon Sep 17 00:00:00 2001 From: TiejunZhou <50469179+TiejunMS@users.noreply.github.com> Date: Tue, 2 Jan 2024 06:22:02 +0000 Subject: [PATCH] Transition FileX to Eclipse Fundation --- .devcontainer/devcontainer.json | 13 + .gitattributes | 40 + .github/ISSUE_TEMPLATE/bug_report.md | 35 + .github/ISSUE_TEMPLATE/feature_request.md | 20 + .../hardware-or-architecture-support.md | 19 + .github/workflows/regression_test.yml | 25 + .gitignore | 16 + CMakeLists.txt | 77 + CONTRIBUTING.md | 14 + LICENSE.txt | 249 + LICENSED-HARDWARE.txt | 45 + README.md | 152 + SECURITY.md | 35 + cmake/arm-none-eabi.cmake | 29 + cmake/cortex_m0.cmake | 13 + cmake/cortex_m3.cmake | 13 + cmake/cortex_m4.cmake | 13 + cmake/cortex_m7.cmake | 13 + cmake/utilities.cmake | 15 + common/CMakeLists.txt | 221 + common/inc/fx_api.h | 1539 ++++ common/inc/fx_directory.h | 122 + common/inc/fx_fault_tolerant.h | 407 + common/inc/fx_file.h | 125 + common/inc/fx_media.h | 109 + common/inc/fx_system.h | 175 + common/inc/fx_unicode.h | 110 + common/inc/fx_user_sample.h | 236 + common/inc/fx_utility.h | 82 + common/src/fx_directory_attributes_read.c | 156 + common/src/fx_directory_attributes_set.c | 193 + common/src/fx_directory_create.c | 711 ++ common/src/fx_directory_default_get.c | 106 + common/src/fx_directory_default_get_copy.c | 114 + common/src/fx_directory_default_set.c | 422 ++ common/src/fx_directory_delete.c | 451 ++ common/src/fx_directory_entry_read.c | 722 ++ common/src/fx_directory_entry_write.c | 1152 +++ common/src/fx_directory_first_entry_find.c | 136 + .../src/fx_directory_first_full_entry_find.c | 146 + common/src/fx_directory_free_search.c | 770 ++ common/src/fx_directory_information_get.c | 235 + common/src/fx_directory_local_path_clear.c | 114 + common/src/fx_directory_local_path_get.c | 129 + common/src/fx_directory_local_path_get_copy.c | 123 + common/src/fx_directory_local_path_restore.c | 117 + common/src/fx_directory_local_path_set.c | 451 ++ common/src/fx_directory_long_name_get.c | 85 + .../src/fx_directory_long_name_get_extended.c | 139 + common/src/fx_directory_name_extract.c | 161 + common/src/fx_directory_name_test.c | 143 + common/src/fx_directory_next_entry_find.c | 437 ++ .../src/fx_directory_next_full_entry_find.c | 515 ++ common/src/fx_directory_rename.c | 486 ++ common/src/fx_directory_search.c | 936 +++ common/src/fx_directory_short_name_get.c | 85 + .../fx_directory_short_name_get_extended.c | 170 + common/src/fx_fault_tolerant_add_FAT_log.c | 113 + common/src/fx_fault_tolerant_add_bitmap_log.c | 30 + .../src/fx_fault_tolerant_add_checksum_log.c | 30 + common/src/fx_fault_tolerant_add_dir_log.c | 132 + common/src/fx_fault_tolerant_apply_logs.c | 268 + .../fx_fault_tolerant_calculate_checksum.c | 103 + .../src/fx_fault_tolerant_cleanup_FAT_chain.c | 356 + .../src/fx_fault_tolerant_create_log_file.c | 233 + common/src/fx_fault_tolerant_enable.c | 500 ++ common/src/fx_fault_tolerant_read_FAT.c | 167 + .../fx_fault_tolerant_read_directory_sector.c | 178 + common/src/fx_fault_tolerant_read_log_file.c | 93 + common/src/fx_fault_tolerant_recover.c | 159 + common/src/fx_fault_tolerant_reset_log_file.c | 142 + common/src/fx_fault_tolerant_set_FAT_chain.c | 125 + .../src/fx_fault_tolerant_transaction_end.c | 207 + .../src/fx_fault_tolerant_transaction_fail.c | 105 + .../src/fx_fault_tolerant_transaction_start.c | 115 + common/src/fx_fault_tolerant_write_log_file.c | 112 + common/src/fx_file_allocate.c | 84 + common/src/fx_file_attributes_read.c | 158 + common/src/fx_file_attributes_set.c | 221 + common/src/fx_file_best_effort_allocate.c | 97 + common/src/fx_file_close.c | 186 + common/src/fx_file_create.c | 303 + common/src/fx_file_date_time_set.c | 137 + common/src/fx_file_delete.c | 382 + common/src/fx_file_extended_allocate.c | 543 ++ .../fx_file_extended_best_effort_allocate.c | 583 ++ common/src/fx_file_extended_relative_seek.c | 163 + common/src/fx_file_extended_seek.c | 300 + common/src/fx_file_extended_truncate.c | 347 + .../src/fx_file_extended_truncate_release.c | 814 ++ common/src/fx_file_open.c | 543 ++ common/src/fx_file_read.c | 441 ++ common/src/fx_file_relative_seek.c | 90 + common/src/fx_file_rename.c | 550 ++ common/src/fx_file_seek.c | 83 + common/src/fx_file_truncate.c | 83 + common/src/fx_file_truncate_release.c | 86 + common/src/fx_file_write.c | 1435 ++++ common/src/fx_file_write_notify_set.c | 83 + common/src/fx_media_abort.c | 189 + common/src/fx_media_boot_info_extract.c | 203 + common/src/fx_media_cache_invalidate.c | 134 + common/src/fx_media_check.c | 712 ++ common/src/fx_media_check_FAT_chain_check.c | 162 + .../src/fx_media_check_lost_cluster_check.c | 169 + common/src/fx_media_close.c | 425 ++ common/src/fx_media_close_notify_set.c | 82 + .../src/fx_media_extended_space_available.c | 115 + common/src/fx_media_flush.c | 352 + common/src/fx_media_format.c | 695 ++ common/src/fx_media_format_oem_name_set.c | 97 + common/src/fx_media_format_type_set.c | 88 + common/src/fx_media_format_volume_id_set.c | 88 + common/src/fx_media_open.c | 1011 +++ common/src/fx_media_open_notify_set.c | 82 + common/src/fx_media_read.c | 127 + common/src/fx_media_space_available.c | 107 + common/src/fx_media_volume_get.c | 89 + common/src/fx_media_volume_get_extended.c | 323 + common/src/fx_media_volume_set.c | 411 ++ common/src/fx_media_write.c | 138 + common/src/fx_partition_offset_calculate.c | 605 ++ common/src/fx_ram_driver.c | 370 + common/src/fx_system_date_get.c | 119 + common/src/fx_system_date_set.c | 88 + common/src/fx_system_initialize.c | 207 + common/src/fx_system_time_get.c | 119 + common/src/fx_system_time_set.c | 88 + common/src/fx_system_timer_entry.c | 343 + common/src/fx_trace_event_insert.c | 146 + common/src/fx_trace_event_update.c | 134 + common/src/fx_trace_object_register.c | 99 + common/src/fx_trace_object_unregister.c | 97 + common/src/fx_unicode_directory_create.c | 282 + .../src/fx_unicode_directory_entry_change.c | 390 + common/src/fx_unicode_directory_entry_read.c | 748 ++ common/src/fx_unicode_directory_rename.c | 344 + common/src/fx_unicode_directory_search.c | 501 ++ common/src/fx_unicode_file_create.c | 283 + common/src/fx_unicode_file_rename.c | 344 + common/src/fx_unicode_length_get.c | 81 + common/src/fx_unicode_length_get_extended.c | 103 + common/src/fx_unicode_name_get.c | 87 + common/src/fx_unicode_name_get_extended.c | 136 + common/src/fx_unicode_short_name_get.c | 87 + .../src/fx_unicode_short_name_get_extended.c | 121 + common/src/fx_utility_16_unsigned_read.c | 85 + common/src/fx_utility_16_unsigned_write.c | 81 + common/src/fx_utility_32_unsigned_read.c | 87 + common/src/fx_utility_32_unsigned_write.c | 83 + common/src/fx_utility_64_unsigned_read.c | 90 + common/src/fx_utility_64_unsigned_write.c | 81 + common/src/fx_utility_FAT_entry_read.c | 433 ++ common/src/fx_utility_FAT_entry_write.c | 257 + common/src/fx_utility_FAT_flush.c | 548 ++ common/src/fx_utility_FAT_map_flush.c | 172 + common/src/fx_utility_FAT_sector_get.c | 111 + common/src/fx_utility_absolute_path_get.c | 32 + ..._utility_logical_sector_cache_entry_read.c | 359 + common/src/fx_utility_logical_sector_flush.c | 522 ++ common/src/fx_utility_logical_sector_read.c | 647 ++ common/src/fx_utility_logical_sector_write.c | 444 ++ common/src/fx_utility_memory_copy.c | 89 + common/src/fx_utility_memory_set.c | 93 + common/src/fx_utility_string_length_get.c | 92 + common/src/fx_utility_token_length_get.c | 29 + common/src/fxe_directory_attributes_read.c | 96 + common/src/fxe_directory_attributes_set.c | 104 + common/src/fxe_directory_create.c | 95 + common/src/fxe_directory_default_get.c | 97 + common/src/fxe_directory_default_get_copy.c | 97 + common/src/fxe_directory_default_set.c | 97 + common/src/fxe_directory_delete.c | 94 + common/src/fxe_directory_first_entry_find.c | 96 + .../src/fxe_directory_first_full_entry_find.c | 108 + common/src/fxe_directory_information_get.c | 111 + common/src/fxe_directory_local_path_clear.c | 92 + common/src/fxe_directory_local_path_get.c | 93 + .../src/fxe_directory_local_path_get_copy.c | 95 + common/src/fxe_directory_local_path_restore.c | 93 + common/src/fxe_directory_local_path_set.c | 97 + common/src/fxe_directory_long_name_get.c | 97 + .../fxe_directory_long_name_get_extended.c | 104 + common/src/fxe_directory_name_test.c | 96 + common/src/fxe_directory_next_entry_find.c | 96 + .../src/fxe_directory_next_full_entry_find.c | 108 + common/src/fxe_directory_rename.c | 96 + common/src/fxe_directory_short_name_get.c | 97 + .../fxe_directory_short_name_get_extended.c | 104 + common/src/fxe_fault_tolerant_enable.c | 99 + common/src/fxe_file_allocate.c | 95 + common/src/fxe_file_attributes_read.c | 96 + common/src/fxe_file_attributes_set.c | 103 + common/src/fxe_file_best_effort_allocate.c | 98 + common/src/fxe_file_close.c | 94 + common/src/fxe_file_create.c | 95 + common/src/fxe_file_date_time_set.c | 293 + common/src/fxe_file_delete.c | 94 + common/src/fxe_file_extended_allocate.c | 95 + .../fxe_file_extended_best_effort_allocate.c | 98 + common/src/fxe_file_extended_relative_seek.c | 110 + common/src/fxe_file_extended_seek.c | 95 + common/src/fxe_file_extended_truncate.c | 95 + .../src/fxe_file_extended_truncate_release.c | 96 + common/src/fxe_file_open.c | 137 + common/src/fxe_file_read.c | 98 + common/src/fxe_file_relative_seek.c | 110 + common/src/fxe_file_rename.c | 96 + common/src/fxe_file_seek.c | 95 + common/src/fxe_file_truncate.c | 95 + common/src/fxe_file_truncate_release.c | 96 + common/src/fxe_file_write.c | 96 + common/src/fxe_file_write_notify_set.c | 96 + common/src/fxe_media_abort.c | 94 + common/src/fxe_media_cache_invalidate.c | 96 + common/src/fxe_media_check.c | 136 + common/src/fxe_media_close.c | 94 + common/src/fxe_media_close_notify_set.c | 96 + .../src/fxe_media_extended_space_available.c | 96 + common/src/fxe_media_flush.c | 96 + common/src/fxe_media_format.c | 122 + common/src/fxe_media_open.c | 189 + common/src/fxe_media_open_notify_set.c | 96 + common/src/fxe_media_read.c | 96 + common/src/fxe_media_space_available.c | 96 + common/src/fxe_media_volume_get.c | 100 + common/src/fxe_media_volume_get_extended.c | 107 + common/src/fxe_media_volume_set.c | 97 + common/src/fxe_media_write.c | 97 + common/src/fxe_system_date_get.c | 92 + common/src/fxe_system_date_set.c | 257 + common/src/fxe_system_time_get.c | 92 + common/src/fxe_system_time_set.c | 106 + common/src/fxe_unicode_directory_create.c | 108 + common/src/fxe_unicode_directory_rename.c | 122 + common/src/fxe_unicode_file_create.c | 108 + common/src/fxe_unicode_file_rename.c | 122 + common/src/fxe_unicode_name_get.c | 101 + common/src/fxe_unicode_name_get_extended.c | 109 + common/src/fxe_unicode_short_name_get.c | 109 + .../src/fxe_unicode_short_name_get_extended.c | 116 + docs/deps.png | Bin 0 -> 65876 bytes docs/filex-features.png | Bin 0 -> 356507 bytes ports/cortex_m0/gnu/CMakeLists.txt | 9 + ports/cortex_m0/gnu/inc/fx_port.h | 25 + ports/cortex_m3/gnu/CMakeLists.txt | 9 + ports/cortex_m3/gnu/inc/fx_port.h | 25 + ports/cortex_m4/gnu/CMakeLists.txt | 9 + ports/cortex_m4/gnu/inc/fx_port.h | 25 + ports/cortex_m7/gnu/CMakeLists.txt | 9 + ports/cortex_m7/gnu/inc/fx_port.h | 25 + ports/generic/inc/fx_port.h | 227 + ports/linux/gnu/CMakeLists.txt | 10 + ports/linux/gnu/inc/fx_port.h | 354 + ports/mips/gnu/inc/fx_port.h | 25 + ports/win32/vs_2019/inc/fx_port.h | 219 + samples/demo_filex.c | 251 + scripts/build.sh | 2 + scripts/install.sh | 28 + scripts/test.sh | 2 + test/cmake/.gitignore | 1 + test/cmake/CMakeLists.txt | 105 + test/cmake/coverage.sh | 8 + test/cmake/libs/CMakeLists.txt | 24 + test/cmake/regression/CMakeLists.txt | 156 + test/cmake/run.sh | 8 + test/cmake/samples/CMakeLists.txt | 17 + ...filex_directory_attributes_read_set_test.c | 298 + .../filex_directory_create_delete_test.c | 3056 ++++++++ ...ilex_directory_default_path_get_set_test.c | 1273 ++++ .../filex_directory_duplicate_entries_test.c | 251 + .../filex_directory_first_next_find_test.c | 1306 ++++ .../filex_directory_local_path_test.c | 803 ++ .../filex_directory_long_short_get_test.c | 382 + .../filex_directory_naming_test.c | 633 ++ .../filex_directory_rename_test.c | 290 + ...x_fault_tolerant_corrupted_log_file_test.c | 246 + ...ex_fault_tolerant_delete_large_data_test.c | 374 + ..._attributes_set_directory_interrupt_test.c | 364 + ...y_attributes_set_redo_log_interrupt_test.c | 422 ++ ...irectory_create_directory_interrupt_test.c | 688 ++ ...directory_create_redo_log_interrupt_test.c | 703 ++ ...irectory_delete_directory_interrupt_test.c | 676 ++ ...directory_delete_redo_log_interrupt_test.c | 672 ++ ...irectory_rename_directory_interrupt_test.c | 691 ++ ...directory_rename_redo_log_interrupt_test.c | 690 ++ .../filex_fault_tolerant_enable_1_test.c | 491 ++ .../filex_fault_tolerant_enable_2_test.c | 425 ++ .../filex_fault_tolerant_enable_3_test.c | 1082 +++ .../filex_fault_tolerant_enable_4_test.c | 282 + .../filex_fault_tolerant_enable_test.c | 333 + ...t_file_allocate_directory_interrupt_test.c | 737 ++ ...olerant_file_allocate_fat_crossover_test.c | 321 + ...olerant_file_allocate_fat_interrupt_test.c | 741 ++ ...nt_file_allocate_redo_log_interrupt_test.c | 737 ++ .../filex_fault_tolerant_file_allocate_test.c | 329 + ...nt_file_allocate_undo_log_interrupt_test.c | 737 ++ ..._attributes_set_directory_interrupt_test.c | 425 ++ ...e_attributes_set_redo_log_interrupt_test.c | 425 ++ ...e_best_allocate_directory_interrupt_test.c | 737 ++ ...nt_file_best_allocate_fat_interrupt_test.c | 737 ++ ...le_best_allocate_redo_log_interrupt_test.c | 737 ++ ...le_best_allocate_undo_log_interrupt_test.c | 735 ++ ..._best_effort_allocate_fat_crossover_test.c | 322 + ...ilex_fault_tolerant_file_corruption_test.c | 386 + ...ant_file_create_directory_interrupt_test.c | 764 ++ ...rant_file_create_redo_log_interrupt_test.c | 730 ++ ...ant_file_delete_directory_interrupt_test.c | 686 ++ ..._tolerant_file_delete_fat_interrupt_test.c | 687 ++ ...nt_file_delete_fat_multiple_sectors_test.c | 481 ++ ...rant_file_delete_redo_log_interrupt_test.c | 718 ++ .../filex_fault_tolerant_file_delete_test.c | 428 ++ ...rant_file_delete_undo_log_interrupt_test.c | 716 ++ ...lex_fault_tolerant_file_random_seek_test.c | 286 + ...ant_file_rename_directory_interrupt_test.c | 721 ++ ...rant_file_rename_redo_log_interrupt_test.c | 720 ++ .../filex_fault_tolerant_file_seek_test.c | 253 + ...t_file_truncate_directory_interrupt_test.c | 737 ++ ...nt_file_truncate_redo_log_interrupt_test.c | 467 ++ ...ant_file_truncate_release_available_test.c | 371 + ...runcate_release_directory_interrupt_test.c | 738 ++ ...file_truncate_release_fat_interrupt_test.c | 738 ++ ...truncate_release_redo_log_interrupt_test.c | 753 ++ ...truncate_release_undo_log_interrupt_test.c | 794 ++ ...fault_tolerant_file_write_available_test.c | 609 ++ ..._tolerant_file_write_data_interrupt_test.c | 514 ++ ...rant_file_write_directory_interrupt_test.c | 819 ++ ...tolerant_file_write_fat_crossover_2_test.c | 319 + ...t_tolerant_file_write_fat_crossover_test.c | 445 ++ ...t_tolerant_file_write_fat_interrupt_test.c | 811 ++ ...erant_file_write_redo_log_interrupt_test.c | 800 ++ ...erant_file_write_undo_log_interrupt_test.c | 800 ++ .../filex_fault_tolerant_log_full_test.c | 293 + ...ault_tolerant_log_recover_directory_test.c | 885 +++ ...ilex_fault_tolerant_log_recover_fat_test.c | 647 ++ ...irectory_create_directory_interrupt_test.c | 600 ++ ...directory_create_redo_log_interrupt_test.c | 597 ++ ...irectory_rename_directory_interrupt_test.c | 702 ++ ...rant_long_directory_rename_io_error_test.c | 547 ++ ...directory_rename_redo_log_interrupt_test.c | 682 ++ ...ong_file_create_directory_interrupt_test.c | 766 ++ ...long_file_create_redo_log_interrupt_test.c | 732 ++ ...ilex_fault_tolerant_loop_write_data_test.c | 401 + .../filex_fault_tolerant_media_check_test.c | 191 + .../filex_fault_tolerant_media_full_test.c | 282 + .../filex_fault_tolerant_recover_fail_test.c | 364 + ...irectory_create_directory_interrupt_test.c | 852 +++ ...directory_create_redo_log_interrupt_test.c | 1098 +++ ...irectory_rename_directory_interrupt_test.c | 1086 +++ ...directory_rename_redo_log_interrupt_test.c | 1036 +++ ...ode_file_create_directory_interrupt_test.c | 780 ++ ...code_file_create_redo_log_interrupt_test.c | 759 ++ ...ode_file_rename_directory_interrupt_test.c | 768 ++ ...code_file_rename_redo_log_interrupt_test.c | 768 ++ ...rite_large_data_directory_interrupt_test.c | 498 ++ ...ge_data_fat_chain_cleanup_interrupt_test.c | 524 ++ ...rant_write_large_data_fat_interrupt_test.c | 523 ++ ...tolerant_write_large_data_interrupt_test.c | 495 ++ ...write_large_data_redo_log_interrupt_test.c | 499 ++ ...lex_fault_tolerant_write_large_data_test.c | 362 + ...write_large_data_undo_log_interrupt_test.c | 498 ++ .../filex_file_allocate_test.c | 343 + .../filex_file_allocate_truncate_test.c | 1150 +++ .../filex_file_attributes_read_set_test.c | 423 ++ .../filex_file_create_delete_test.c | 1100 +++ .../filex_file_date_time_set_test.c | 646 ++ test/regression_test/filex_file_name_test.c | 213 + test/regression_test/filex_file_naming_test.c | 477 ++ .../filex_file_read_write_test.c | 2586 +++++++ test/regression_test/filex_file_rename_test.c | 353 + test/regression_test/filex_file_seek_test.c | 860 +++ .../filex_file_write_available_cluster_test.c | 336 + .../filex_file_write_notify_test.c | 260 + .../filex_file_write_seek_test.c | 346 + test/regression_test/filex_media_abort_test.c | 551 ++ .../filex_media_cache_invalidate_test.c | 509 ++ test/regression_test/filex_media_check_test.c | 2246 ++++++ test/regression_test/filex_media_flush_test.c | 497 ++ .../filex_media_format_open_close_test.c | 1056 +++ .../filex_media_hidden_sectors_test.c | 286 + .../filex_media_multiple_open_close_test.c | 2027 +++++ .../filex_media_read_write_sector_test.c | 1253 ++++ .../filex_media_volume_directory_entry_test.c | 179 + .../filex_media_volume_get_set_test.c | 1006 +++ .../filex_system_date_time_test.c | 431 ++ test/regression_test/filex_unicode_2_test.c | 212 + test/regression_test/filex_unicode_3_test.c | 165 + test/regression_test/filex_unicode_4_test.c | 503 ++ .../filex_unicode_directory_entry_2_test.c | 144 + ...ilex_unicode_directory_entry_change_test.c | 211 + .../filex_unicode_directory_entry_test.c | 352 + .../filex_unicode_directory_rename_test.c | 444 ++ .../filex_unicode_fat_entry_1_test.c | 141 + .../filex_unicode_fat_entry_2_test.c | 154 + .../filex_unicode_fat_entry_3_test.c | 233 + .../filex_unicode_fat_entry_test.c | 139 + ...icode_file_directory_rename_extra_2_test.c | 530 ++ ...unicode_file_directory_rename_extra_test.c | 223 + .../filex_unicode_file_rename_test.c | 442 ++ .../filex_unicode_name_string_test.c | 175 + test/regression_test/filex_unicode_test.c | 640 ++ .../filex_utility_fat_flush_test.c | 430 ++ test/regression_test/filex_utility_test.c | 821 +++ test/regression_test/filextestcontrol.c | 6556 +++++++++++++++++ test/regression_test/fx_ram_driver_test.c | 2575 +++++++ test/regression_test/fx_ram_driver_test.h | 38 + 406 files changed, 143803 insertions(+) create mode 100644 .devcontainer/devcontainer.json create mode 100644 .gitattributes create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/hardware-or-architecture-support.md create mode 100644 .github/workflows/regression_test.yml create mode 100755 .gitignore create mode 100755 CMakeLists.txt create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE.txt create mode 100644 LICENSED-HARDWARE.txt create mode 100755 README.md create mode 100644 SECURITY.md create mode 100644 cmake/arm-none-eabi.cmake create mode 100644 cmake/cortex_m0.cmake create mode 100644 cmake/cortex_m3.cmake create mode 100644 cmake/cortex_m4.cmake create mode 100644 cmake/cortex_m7.cmake create mode 100644 cmake/utilities.cmake create mode 100644 common/CMakeLists.txt create mode 100644 common/inc/fx_api.h create mode 100644 common/inc/fx_directory.h create mode 100644 common/inc/fx_fault_tolerant.h create mode 100644 common/inc/fx_file.h create mode 100644 common/inc/fx_media.h create mode 100644 common/inc/fx_system.h create mode 100644 common/inc/fx_unicode.h create mode 100644 common/inc/fx_user_sample.h create mode 100644 common/inc/fx_utility.h create mode 100644 common/src/fx_directory_attributes_read.c create mode 100644 common/src/fx_directory_attributes_set.c create mode 100644 common/src/fx_directory_create.c create mode 100644 common/src/fx_directory_default_get.c create mode 100644 common/src/fx_directory_default_get_copy.c create mode 100644 common/src/fx_directory_default_set.c create mode 100644 common/src/fx_directory_delete.c create mode 100644 common/src/fx_directory_entry_read.c create mode 100644 common/src/fx_directory_entry_write.c create mode 100644 common/src/fx_directory_first_entry_find.c create mode 100644 common/src/fx_directory_first_full_entry_find.c create mode 100644 common/src/fx_directory_free_search.c create mode 100644 common/src/fx_directory_information_get.c create mode 100644 common/src/fx_directory_local_path_clear.c create mode 100644 common/src/fx_directory_local_path_get.c create mode 100644 common/src/fx_directory_local_path_get_copy.c create mode 100644 common/src/fx_directory_local_path_restore.c create mode 100644 common/src/fx_directory_local_path_set.c create mode 100644 common/src/fx_directory_long_name_get.c create mode 100644 common/src/fx_directory_long_name_get_extended.c create mode 100644 common/src/fx_directory_name_extract.c create mode 100644 common/src/fx_directory_name_test.c create mode 100644 common/src/fx_directory_next_entry_find.c create mode 100644 common/src/fx_directory_next_full_entry_find.c create mode 100644 common/src/fx_directory_rename.c create mode 100644 common/src/fx_directory_search.c create mode 100644 common/src/fx_directory_short_name_get.c create mode 100644 common/src/fx_directory_short_name_get_extended.c create mode 100644 common/src/fx_fault_tolerant_add_FAT_log.c create mode 100644 common/src/fx_fault_tolerant_add_bitmap_log.c create mode 100644 common/src/fx_fault_tolerant_add_checksum_log.c create mode 100644 common/src/fx_fault_tolerant_add_dir_log.c create mode 100644 common/src/fx_fault_tolerant_apply_logs.c create mode 100644 common/src/fx_fault_tolerant_calculate_checksum.c create mode 100644 common/src/fx_fault_tolerant_cleanup_FAT_chain.c create mode 100644 common/src/fx_fault_tolerant_create_log_file.c create mode 100644 common/src/fx_fault_tolerant_enable.c create mode 100644 common/src/fx_fault_tolerant_read_FAT.c create mode 100644 common/src/fx_fault_tolerant_read_directory_sector.c create mode 100644 common/src/fx_fault_tolerant_read_log_file.c create mode 100644 common/src/fx_fault_tolerant_recover.c create mode 100644 common/src/fx_fault_tolerant_reset_log_file.c create mode 100644 common/src/fx_fault_tolerant_set_FAT_chain.c create mode 100644 common/src/fx_fault_tolerant_transaction_end.c create mode 100644 common/src/fx_fault_tolerant_transaction_fail.c create mode 100644 common/src/fx_fault_tolerant_transaction_start.c create mode 100644 common/src/fx_fault_tolerant_write_log_file.c create mode 100644 common/src/fx_file_allocate.c create mode 100644 common/src/fx_file_attributes_read.c create mode 100644 common/src/fx_file_attributes_set.c create mode 100644 common/src/fx_file_best_effort_allocate.c create mode 100644 common/src/fx_file_close.c create mode 100644 common/src/fx_file_create.c create mode 100644 common/src/fx_file_date_time_set.c create mode 100644 common/src/fx_file_delete.c create mode 100644 common/src/fx_file_extended_allocate.c create mode 100644 common/src/fx_file_extended_best_effort_allocate.c create mode 100644 common/src/fx_file_extended_relative_seek.c create mode 100644 common/src/fx_file_extended_seek.c create mode 100644 common/src/fx_file_extended_truncate.c create mode 100644 common/src/fx_file_extended_truncate_release.c create mode 100644 common/src/fx_file_open.c create mode 100644 common/src/fx_file_read.c create mode 100644 common/src/fx_file_relative_seek.c create mode 100644 common/src/fx_file_rename.c create mode 100644 common/src/fx_file_seek.c create mode 100644 common/src/fx_file_truncate.c create mode 100644 common/src/fx_file_truncate_release.c create mode 100644 common/src/fx_file_write.c create mode 100644 common/src/fx_file_write_notify_set.c create mode 100644 common/src/fx_media_abort.c create mode 100644 common/src/fx_media_boot_info_extract.c create mode 100644 common/src/fx_media_cache_invalidate.c create mode 100644 common/src/fx_media_check.c create mode 100644 common/src/fx_media_check_FAT_chain_check.c create mode 100644 common/src/fx_media_check_lost_cluster_check.c create mode 100644 common/src/fx_media_close.c create mode 100644 common/src/fx_media_close_notify_set.c create mode 100644 common/src/fx_media_extended_space_available.c create mode 100644 common/src/fx_media_flush.c create mode 100644 common/src/fx_media_format.c create mode 100644 common/src/fx_media_format_oem_name_set.c create mode 100644 common/src/fx_media_format_type_set.c create mode 100644 common/src/fx_media_format_volume_id_set.c create mode 100644 common/src/fx_media_open.c create mode 100644 common/src/fx_media_open_notify_set.c create mode 100644 common/src/fx_media_read.c create mode 100644 common/src/fx_media_space_available.c create mode 100644 common/src/fx_media_volume_get.c create mode 100644 common/src/fx_media_volume_get_extended.c create mode 100644 common/src/fx_media_volume_set.c create mode 100644 common/src/fx_media_write.c create mode 100644 common/src/fx_partition_offset_calculate.c create mode 100644 common/src/fx_ram_driver.c create mode 100644 common/src/fx_system_date_get.c create mode 100644 common/src/fx_system_date_set.c create mode 100644 common/src/fx_system_initialize.c create mode 100644 common/src/fx_system_time_get.c create mode 100644 common/src/fx_system_time_set.c create mode 100644 common/src/fx_system_timer_entry.c create mode 100644 common/src/fx_trace_event_insert.c create mode 100644 common/src/fx_trace_event_update.c create mode 100644 common/src/fx_trace_object_register.c create mode 100644 common/src/fx_trace_object_unregister.c create mode 100644 common/src/fx_unicode_directory_create.c create mode 100644 common/src/fx_unicode_directory_entry_change.c create mode 100644 common/src/fx_unicode_directory_entry_read.c create mode 100644 common/src/fx_unicode_directory_rename.c create mode 100644 common/src/fx_unicode_directory_search.c create mode 100644 common/src/fx_unicode_file_create.c create mode 100644 common/src/fx_unicode_file_rename.c create mode 100644 common/src/fx_unicode_length_get.c create mode 100644 common/src/fx_unicode_length_get_extended.c create mode 100644 common/src/fx_unicode_name_get.c create mode 100644 common/src/fx_unicode_name_get_extended.c create mode 100644 common/src/fx_unicode_short_name_get.c create mode 100644 common/src/fx_unicode_short_name_get_extended.c create mode 100644 common/src/fx_utility_16_unsigned_read.c create mode 100644 common/src/fx_utility_16_unsigned_write.c create mode 100644 common/src/fx_utility_32_unsigned_read.c create mode 100644 common/src/fx_utility_32_unsigned_write.c create mode 100644 common/src/fx_utility_64_unsigned_read.c create mode 100644 common/src/fx_utility_64_unsigned_write.c create mode 100644 common/src/fx_utility_FAT_entry_read.c create mode 100644 common/src/fx_utility_FAT_entry_write.c create mode 100644 common/src/fx_utility_FAT_flush.c create mode 100644 common/src/fx_utility_FAT_map_flush.c create mode 100644 common/src/fx_utility_FAT_sector_get.c create mode 100644 common/src/fx_utility_absolute_path_get.c create mode 100644 common/src/fx_utility_logical_sector_cache_entry_read.c create mode 100644 common/src/fx_utility_logical_sector_flush.c create mode 100644 common/src/fx_utility_logical_sector_read.c create mode 100644 common/src/fx_utility_logical_sector_write.c create mode 100644 common/src/fx_utility_memory_copy.c create mode 100644 common/src/fx_utility_memory_set.c create mode 100644 common/src/fx_utility_string_length_get.c create mode 100644 common/src/fx_utility_token_length_get.c create mode 100644 common/src/fxe_directory_attributes_read.c create mode 100644 common/src/fxe_directory_attributes_set.c create mode 100644 common/src/fxe_directory_create.c create mode 100644 common/src/fxe_directory_default_get.c create mode 100644 common/src/fxe_directory_default_get_copy.c create mode 100644 common/src/fxe_directory_default_set.c create mode 100644 common/src/fxe_directory_delete.c create mode 100644 common/src/fxe_directory_first_entry_find.c create mode 100644 common/src/fxe_directory_first_full_entry_find.c create mode 100644 common/src/fxe_directory_information_get.c create mode 100644 common/src/fxe_directory_local_path_clear.c create mode 100644 common/src/fxe_directory_local_path_get.c create mode 100644 common/src/fxe_directory_local_path_get_copy.c create mode 100644 common/src/fxe_directory_local_path_restore.c create mode 100644 common/src/fxe_directory_local_path_set.c create mode 100644 common/src/fxe_directory_long_name_get.c create mode 100644 common/src/fxe_directory_long_name_get_extended.c create mode 100644 common/src/fxe_directory_name_test.c create mode 100644 common/src/fxe_directory_next_entry_find.c create mode 100644 common/src/fxe_directory_next_full_entry_find.c create mode 100644 common/src/fxe_directory_rename.c create mode 100644 common/src/fxe_directory_short_name_get.c create mode 100644 common/src/fxe_directory_short_name_get_extended.c create mode 100644 common/src/fxe_fault_tolerant_enable.c create mode 100644 common/src/fxe_file_allocate.c create mode 100644 common/src/fxe_file_attributes_read.c create mode 100644 common/src/fxe_file_attributes_set.c create mode 100644 common/src/fxe_file_best_effort_allocate.c create mode 100644 common/src/fxe_file_close.c create mode 100644 common/src/fxe_file_create.c create mode 100644 common/src/fxe_file_date_time_set.c create mode 100644 common/src/fxe_file_delete.c create mode 100644 common/src/fxe_file_extended_allocate.c create mode 100644 common/src/fxe_file_extended_best_effort_allocate.c create mode 100644 common/src/fxe_file_extended_relative_seek.c create mode 100644 common/src/fxe_file_extended_seek.c create mode 100644 common/src/fxe_file_extended_truncate.c create mode 100644 common/src/fxe_file_extended_truncate_release.c create mode 100644 common/src/fxe_file_open.c create mode 100644 common/src/fxe_file_read.c create mode 100644 common/src/fxe_file_relative_seek.c create mode 100644 common/src/fxe_file_rename.c create mode 100644 common/src/fxe_file_seek.c create mode 100644 common/src/fxe_file_truncate.c create mode 100644 common/src/fxe_file_truncate_release.c create mode 100644 common/src/fxe_file_write.c create mode 100644 common/src/fxe_file_write_notify_set.c create mode 100644 common/src/fxe_media_abort.c create mode 100644 common/src/fxe_media_cache_invalidate.c create mode 100644 common/src/fxe_media_check.c create mode 100644 common/src/fxe_media_close.c create mode 100644 common/src/fxe_media_close_notify_set.c create mode 100644 common/src/fxe_media_extended_space_available.c create mode 100644 common/src/fxe_media_flush.c create mode 100644 common/src/fxe_media_format.c create mode 100644 common/src/fxe_media_open.c create mode 100644 common/src/fxe_media_open_notify_set.c create mode 100644 common/src/fxe_media_read.c create mode 100644 common/src/fxe_media_space_available.c create mode 100644 common/src/fxe_media_volume_get.c create mode 100644 common/src/fxe_media_volume_get_extended.c create mode 100644 common/src/fxe_media_volume_set.c create mode 100644 common/src/fxe_media_write.c create mode 100644 common/src/fxe_system_date_get.c create mode 100644 common/src/fxe_system_date_set.c create mode 100644 common/src/fxe_system_time_get.c create mode 100644 common/src/fxe_system_time_set.c create mode 100644 common/src/fxe_unicode_directory_create.c create mode 100644 common/src/fxe_unicode_directory_rename.c create mode 100644 common/src/fxe_unicode_file_create.c create mode 100644 common/src/fxe_unicode_file_rename.c create mode 100644 common/src/fxe_unicode_name_get.c create mode 100644 common/src/fxe_unicode_name_get_extended.c create mode 100644 common/src/fxe_unicode_short_name_get.c create mode 100644 common/src/fxe_unicode_short_name_get_extended.c create mode 100644 docs/deps.png create mode 100644 docs/filex-features.png create mode 100644 ports/cortex_m0/gnu/CMakeLists.txt create mode 100644 ports/cortex_m0/gnu/inc/fx_port.h create mode 100644 ports/cortex_m3/gnu/CMakeLists.txt create mode 100644 ports/cortex_m3/gnu/inc/fx_port.h create mode 100644 ports/cortex_m4/gnu/CMakeLists.txt create mode 100644 ports/cortex_m4/gnu/inc/fx_port.h create mode 100644 ports/cortex_m7/gnu/CMakeLists.txt create mode 100644 ports/cortex_m7/gnu/inc/fx_port.h create mode 100644 ports/generic/inc/fx_port.h create mode 100644 ports/linux/gnu/CMakeLists.txt create mode 100644 ports/linux/gnu/inc/fx_port.h create mode 100644 ports/mips/gnu/inc/fx_port.h create mode 100644 ports/win32/vs_2019/inc/fx_port.h create mode 100644 samples/demo_filex.c create mode 100755 scripts/build.sh create mode 100755 scripts/install.sh create mode 100755 scripts/test.sh create mode 100644 test/cmake/.gitignore create mode 100644 test/cmake/CMakeLists.txt create mode 100755 test/cmake/coverage.sh create mode 100644 test/cmake/libs/CMakeLists.txt create mode 100644 test/cmake/regression/CMakeLists.txt create mode 100755 test/cmake/run.sh create mode 100644 test/cmake/samples/CMakeLists.txt create mode 100644 test/regression_test/filex_directory_attributes_read_set_test.c create mode 100644 test/regression_test/filex_directory_create_delete_test.c create mode 100644 test/regression_test/filex_directory_default_path_get_set_test.c create mode 100644 test/regression_test/filex_directory_duplicate_entries_test.c create mode 100644 test/regression_test/filex_directory_first_next_find_test.c create mode 100644 test/regression_test/filex_directory_local_path_test.c create mode 100644 test/regression_test/filex_directory_long_short_get_test.c create mode 100644 test/regression_test/filex_directory_naming_test.c create mode 100644 test/regression_test/filex_directory_rename_test.c create mode 100644 test/regression_test/filex_fault_tolerant_corrupted_log_file_test.c create mode 100644 test/regression_test/filex_fault_tolerant_delete_large_data_test.c create mode 100644 test/regression_test/filex_fault_tolerant_directory_attributes_set_directory_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_directory_attributes_set_redo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_directory_create_directory_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_directory_create_redo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_directory_delete_directory_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_directory_delete_redo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_directory_rename_directory_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_directory_rename_redo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_enable_1_test.c create mode 100644 test/regression_test/filex_fault_tolerant_enable_2_test.c create mode 100644 test/regression_test/filex_fault_tolerant_enable_3_test.c create mode 100644 test/regression_test/filex_fault_tolerant_enable_4_test.c create mode 100644 test/regression_test/filex_fault_tolerant_enable_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_allocate_directory_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_allocate_fat_crossover_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_allocate_fat_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_allocate_redo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_allocate_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_allocate_undo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_attributes_set_directory_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_attributes_set_redo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_best_allocate_directory_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_best_allocate_fat_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_best_allocate_redo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_best_allocate_undo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_best_effort_allocate_fat_crossover_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_corruption_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_create_directory_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_create_redo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_delete_directory_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_delete_fat_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_delete_fat_multiple_sectors_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_delete_redo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_delete_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_delete_undo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_random_seek_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_rename_directory_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_rename_redo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_seek_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_truncate_directory_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_truncate_redo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_truncate_release_available_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_truncate_release_directory_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_truncate_release_fat_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_truncate_release_redo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_truncate_release_undo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_write_available_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_write_data_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_write_directory_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_write_fat_crossover_2_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_write_fat_crossover_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_write_fat_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_write_redo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_file_write_undo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_log_full_test.c create mode 100644 test/regression_test/filex_fault_tolerant_log_recover_directory_test.c create mode 100644 test/regression_test/filex_fault_tolerant_log_recover_fat_test.c create mode 100644 test/regression_test/filex_fault_tolerant_long_directory_create_directory_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_long_directory_create_redo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_long_directory_rename_directory_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_long_directory_rename_io_error_test.c create mode 100644 test/regression_test/filex_fault_tolerant_long_directory_rename_redo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_long_file_create_directory_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_long_file_create_redo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_loop_write_data_test.c create mode 100644 test/regression_test/filex_fault_tolerant_media_check_test.c create mode 100644 test/regression_test/filex_fault_tolerant_media_full_test.c create mode 100644 test/regression_test/filex_fault_tolerant_recover_fail_test.c create mode 100644 test/regression_test/filex_fault_tolerant_unicode_directory_create_directory_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_unicode_directory_create_redo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_unicode_directory_rename_directory_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_unicode_directory_rename_redo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_unicode_file_create_directory_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_unicode_file_create_redo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_unicode_file_rename_directory_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_unicode_file_rename_redo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_write_large_data_directory_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_write_large_data_fat_chain_cleanup_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_write_large_data_fat_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_write_large_data_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_write_large_data_redo_log_interrupt_test.c create mode 100644 test/regression_test/filex_fault_tolerant_write_large_data_test.c create mode 100644 test/regression_test/filex_fault_tolerant_write_large_data_undo_log_interrupt_test.c create mode 100644 test/regression_test/filex_file_allocate_test.c create mode 100644 test/regression_test/filex_file_allocate_truncate_test.c create mode 100644 test/regression_test/filex_file_attributes_read_set_test.c create mode 100644 test/regression_test/filex_file_create_delete_test.c create mode 100644 test/regression_test/filex_file_date_time_set_test.c create mode 100644 test/regression_test/filex_file_name_test.c create mode 100644 test/regression_test/filex_file_naming_test.c create mode 100644 test/regression_test/filex_file_read_write_test.c create mode 100644 test/regression_test/filex_file_rename_test.c create mode 100644 test/regression_test/filex_file_seek_test.c create mode 100644 test/regression_test/filex_file_write_available_cluster_test.c create mode 100644 test/regression_test/filex_file_write_notify_test.c create mode 100644 test/regression_test/filex_file_write_seek_test.c create mode 100644 test/regression_test/filex_media_abort_test.c create mode 100644 test/regression_test/filex_media_cache_invalidate_test.c create mode 100644 test/regression_test/filex_media_check_test.c create mode 100644 test/regression_test/filex_media_flush_test.c create mode 100644 test/regression_test/filex_media_format_open_close_test.c create mode 100644 test/regression_test/filex_media_hidden_sectors_test.c create mode 100644 test/regression_test/filex_media_multiple_open_close_test.c create mode 100644 test/regression_test/filex_media_read_write_sector_test.c create mode 100644 test/regression_test/filex_media_volume_directory_entry_test.c create mode 100644 test/regression_test/filex_media_volume_get_set_test.c create mode 100644 test/regression_test/filex_system_date_time_test.c create mode 100644 test/regression_test/filex_unicode_2_test.c create mode 100644 test/regression_test/filex_unicode_3_test.c create mode 100644 test/regression_test/filex_unicode_4_test.c create mode 100644 test/regression_test/filex_unicode_directory_entry_2_test.c create mode 100644 test/regression_test/filex_unicode_directory_entry_change_test.c create mode 100644 test/regression_test/filex_unicode_directory_entry_test.c create mode 100644 test/regression_test/filex_unicode_directory_rename_test.c create mode 100644 test/regression_test/filex_unicode_fat_entry_1_test.c create mode 100644 test/regression_test/filex_unicode_fat_entry_2_test.c create mode 100644 test/regression_test/filex_unicode_fat_entry_3_test.c create mode 100644 test/regression_test/filex_unicode_fat_entry_test.c create mode 100644 test/regression_test/filex_unicode_file_directory_rename_extra_2_test.c create mode 100644 test/regression_test/filex_unicode_file_directory_rename_extra_test.c create mode 100644 test/regression_test/filex_unicode_file_rename_test.c create mode 100644 test/regression_test/filex_unicode_name_string_test.c create mode 100644 test/regression_test/filex_unicode_test.c create mode 100644 test/regression_test/filex_utility_fat_flush_test.c create mode 100644 test/regression_test/filex_utility_test.c create mode 100644 test/regression_test/filextestcontrol.c create mode 100644 test/regression_test/fx_ram_driver_test.c create mode 100644 test/regression_test/fx_ram_driver_test.h diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..9b4fdaf --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,13 @@ +{ + "image": "ghcr.io/tiejunms/azure_rtos_docker", + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "ms-vscode.cpptools", + "ms-vscode.cmake-tools" + ], + + "remoteUser": "vscode", + + "runArgs": [ "--cap-add=NET_ADMIN"] +} \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..ee9315d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,40 @@ +.git* export-ignore +.hooks* export-ignore + +# Custom attribute to mark sources as using our C code style. +[attr]our-c-style whitespace=tab-in-indent eol=lf format.clang-format-6.0 + +# Custom attribute to mark sources as generated. +# Do not perform whitespace checks. Do not format. +[attr]generated whitespace=-tab-in-indent,-indent-with-non-tab -format.clang-format-6.0 + +bootstrap eol=lf +configure eol=lf +*.[1-9] eol=lf +*.bash eol=lf +*.sh eol=lf +*.sh.in eol=lf + +*.bat eol=crlf +*.bat.in eol=crlf +*.sln eol=crlf +*.vcproj eol=crlf + +*.pfx -text +*.png -text +*.png.in -text + +*.c our-c-style +*.cc our-c-style +*.cpp our-c-style +*.cu our-c-style +*.cxx our-c-style +*.h our-c-style +*.hh our-c-style +*.hpp our-c-style +*.hxx our-c-style +*.notcu our-c-style + +*.cmake whitespace=tab-in-indent +*.rst whitespace=tab-in-indent conflict-marker-size=79 +*.txt whitespace=tab-in-indent \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..8dc87bc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,35 @@ +--- +name: Bug report +about: Create a report to help us improve Azure RTOS. +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +Please also mention any information which could help others to understand +the problem you're facing: +- What target device are you using? +- Which version of Azure RTOS? +- What toolchain and environment? +- What have you tried to diagnose or workaround this issue? + +**To Reproduce** +Steps to reproduce the behavior: +1. Build my project in IAR Workbench. +1. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Impact** +What impact does this issue have on your progress (e.g., annoyance, showstopper) + +**Logs and console output** +If applicable, add console logs or other types of debug information like Wireshark capture as `.zip` file. + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..0221c22 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea or enhancement to existing feature for Azure RTOS. +title: '' +labels: feature +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/hardware-or-architecture-support.md b/.github/ISSUE_TEMPLATE/hardware-or-architecture-support.md new file mode 100644 index 0000000..8ecbd34 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/hardware-or-architecture-support.md @@ -0,0 +1,19 @@ +--- +name: Hardware or architecture support +about: Suggest adding hardware or new architecture support. +title: '' +labels: hardware +assignees: '' + +--- + +**Is this request related to a particular hardware platform, SoC, board? Please describe.** +Describe in details the hardware support being requested and why this support benefits Azure RTOS. + +**Describe why you are asking for this support?** +Describe why you are asking for this support. + +If this is a new board or SoC, please state whether you are willing to maintain the Azure RTOS support together with you. + +**Additional context** +Add any other context or graphics (drag-and-drop an image) about the hardware here. diff --git a/.github/workflows/regression_test.yml b/.github/workflows/regression_test.yml new file mode 100644 index 0000000..1c7ad1b --- /dev/null +++ b/.github/workflows/regression_test.yml @@ -0,0 +1,25 @@ +# This is a basic workflow that is manually triggered + +name: regression_test + +# Controls when the action will run. Triggers the workflow on push or pull request +# events but only for the master branch +on: + workflow_dispatch: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "linux_job" + run_tests: + permissions: + contents: read + issues: read + checks: write + pull-requests: write + pages: write + id-token: write + uses: azure-rtos/threadx/.github/workflows/regression_template.yml@master \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..2d01435 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +.vscode/ +_deps/ +build/ +coverage_report/ +CMakeFiles/ +CMakeScripts/ +CMakeLists.txt.user +CMakeCache.txt +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +.run.sh + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 0000000..b08ba99 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,77 @@ +cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR) + +# Set up the project +project(filex + LANGUAGES C ASM +) + + +option(FX_STANDALONE_ENABLE "Enable Filex in standalone mode" OFF) + +if(NOT DEFINED THREADX_ARCH) + message(FATAL_ERROR "Error: THREADX_ARCH not defined") +endif() +if(NOT DEFINED THREADX_TOOLCHAIN) + message(FATAL_ERROR "Error: THREADX_TOOLCHAIN not defined") +endif() + +# Define our target library and an alias for consumers +add_library(${PROJECT_NAME}) +add_library("azrtos::${PROJECT_NAME}" ALIAS ${PROJECT_NAME}) + +# Define any required dependencies between this library and others +if(NOT FX_STANDALONE_ENABLE) + target_link_libraries(${PROJECT_NAME} PUBLIC + "azrtos::threadx" + ) +endif() + +# A place for generated/copied include files (no need to change) +set(CUSTOM_INC_DIR ${CMAKE_CURRENT_BINARY_DIR}/custom_inc) + +# Pick up the port specific stuff first +if(DEFINED FILEX_CUSTOM_PORT) + add_subdirectory(${FILEX_CUSTOM_PORT} filex_port) +else() + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/ports/${THREADX_ARCH}/${THREADX_TOOLCHAIN}) +endif() + +# Then the common files +add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/common) + + + + +# Include the user's override file if required +if (NOT FX_USER_FILE) +message(STATUS "Using default fx_user.h file") +set(FX_USER_FILE ${CMAKE_CURRENT_LIST_DIR}/common/inc/fx_user_sample.h) +else() + message(STATUS "Using custom fx_user.h file from ${FX_USER_FILE}") +endif() +configure_file(${FX_USER_FILE} ${CUSTOM_INC_DIR}/fx_user.h COPYONLY) +target_include_directories(${PROJECT_NAME} + PUBLIC + ${CUSTOM_INC_DIR} +) + +if(NOT FX_STANDALONE_ENABLE) + target_compile_definitions(${PROJECT_NAME} PUBLIC "FX_INCLUDE_USER_DEFINE_FILE" ) +else() + target_compile_definitions(${PROJECT_NAME} PUBLIC "FX_INCLUDE_USER_DEFINE_FILE" -DFX_STANDALONE_ENABLE) +endif() + +# Enable a build target that produces a ZIP file of all sources +set(CPACK_SOURCE_GENERATOR "ZIP") +set(CPACK_SOURCE_IGNORE_FILES + \\.git/ + \\.github/ + _build/ + \\.git + \\.gitattributes + \\.gitignore + ".*~$" +) +set(CPACK_VERBATIM_VARIABLES YES) +include(CPack) + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..c282e9a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,14 @@ +# Contributing + +This project welcomes contributions and suggestions. Most contributions require you to +agree to a Contributor License Agreement (CLA) declaring that you have the right to, +and actually do, grant us the rights to use your contribution. For details, visit +https://cla.microsoft.com. + +When you submit a pull request, a CLA-bot will automatically determine whether you need +to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the +instructions provided by the bot. You will only need to do this once across all repositories using our CLA. + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) +or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..6929d7e --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,249 @@ +MICROSOFT SOFTWARE LICENSE TERMS + +MICROSOFT AZURE RTOS + + +These license terms are an agreement between you and Microsoft Corporation (or +one of its affiliates). They apply to the software named above and any Microsoft +services or software updates (except to the extent such services or updates are +accompanied by new or additional terms, in which case those different terms +apply prospectively and do not alter your or Microsoft’s rights relating to +pre-updated software or services). IF YOU COMPLY WITH THESE LICENSE TERMS, YOU +HAVE THE RIGHTS BELOW. BY USING THE SOFTWARE, YOU ACCEPT THESE TERMS. + +1. INSTALLATION AND USE RIGHTS. + +a) General. You may (I) install, use and modify the software and (ii) install and use the included Microsoft +Applications (if any), each solely for internal development, testing and evaluation purposes. +Distribution or production use is governed by the license terms set forth in +Section 2. You may also obtain distribution or production use rights through a separate agreement with +Microsoft. + +b) Contributions. Microsoft welcomes contributions to this software. In the event +that you make a contribution to this software you will be required to agree to a +Contributor License Agreement (CLA) declaring that you have the right to, and +actually do, grant Microsoft the rights to use your contribution. For details, +visit https://cla.microsoft.com. + +c) Included Microsoft Applications. The software may include other Microsoft +applications which are governed by the licenses embedded in or made available +with those applications. + +d) Third Party Components. The software may include third party components with +separate legal notices or governed by other agreements, as may be described +within the software or in the ThirdPartyNotices file(s) accompanying the +software. + +e) Competitive Benchmarking. If you are a direct competitor, and you access or use +the software for purposes of competitive benchmarking, analysis, or intelligence +gathering, you waive as against Microsoft, its subsidiaries, and its affiliated +companies (including prospectively) any competitive use, access, and +benchmarking test restrictions in the terms governing your software to the +extent your terms of use are, or purport to be, more restrictive than +Microsoft’s terms. If you do not waive any such purported restrictions in the +terms governing your software, you are not allowed to access or use this +software, and will not do so. + +2. DISTRIBUTION AND PRODUCTION USE. If you have obtained and/or are developing on +microprocessor(s) and/or microcontroller(s) (“hardware”) listed in the file +named “LICENSED-HARDWARE.txt” included in the repository and/or distributed with +the software you have the following rights in and to the software solely when +used in combination with the hardware. In the event hardware is not listed in +the LICENSED-HARDWARE.txt file, you do not have the rights in this Section 2. + +a) Distribution and Production Use Rights. + +i. You may use the software in production (e.g. program the modified or unmodified +software to devices you own or control) and distribute (i.e. make available to +third parties) the modified or unmodified binary image produced from this code. + +ii. You may permit your device distributors or developers to copy and distribute the +binary image as programmed or to be programmed to your devices. + +iii. You may redistribute the unmodified or modified source to your device +distributors or developers. Modifications must be clearly marked. Any +redistribution in source code form must contain this license and any other +licenses that accompany the software. + +b) Requirements. For any code you distribute, you must: + +i. when distributed in binary form, except as embedded in a device, include with +such distribution the terms of this agreement; +ii. when distributed in source code form to distributors or developers of your +devices, include with such distribution the terms of this agreement; and +iii. indemnify, defend and hold harmless Microsoft from any claims, including claims arising from any High Risk Uses, and inclusive of attorneys’ fees, related to the distribution or use of your devices that include the software, except to the extent that any intellectual property claim is based solely on the unmodified software. + +c) Restrictions. You may not: +i. use or modify the software to create competing real time operating system +software; + +ii. remove any copyright notices or licenses contained in the software; + +iii. use Microsoft’s trademarks or trade dress in your application in any way that +suggests your device or application comes from or is endorsed by Microsoft; + +iv. transfer individual components, specific libraries, classes, functions or code +fragments of the software separately for purposes unrelated to the software; or + +v. use or distribute the software in any way that would subject the software or +Microsoft’s intellectual property or technology to any other license terms. + +3. SCOPE OF LICENSE. The software is licensed, not sold. Microsoft reserves all +other rights. Unless applicable law gives you more rights despite this +limitation, you will not (and have no right to): + +a) remove, minimize, block, or modify any notices of Microsoft or its suppliers in +the software; + +b) use the software in any way that is against the law or to create or propagate +malware; or + +c) share, publish, distribute, or lease the software (except as permitted in +Section 2 above), or provide the software as a stand-alone offering for others +to use. + +4. DATA. This software may interact with other Microsoft products that collect data +that is transmitted to Microsoft. To learn more about how Microsoft processes +personal data we collect, please see the Microsoft Privacy Statement at +https://go.microsoft.com/fwlink/?LinkId=248681. + +5. EXPORT RESTRICTIONS. You must comply with all domestic and international export +laws and regulations that apply to the software, which include restrictions on +destinations, end users, and end use. For further information on export +restrictions, visit https://aka.ms/exporting. + +6. SUPPORT SERVICES. Microsoft is not obligated under this agreement to provide any +support services for the software. Any support provided is “as is”, “with all +faults”, and without warranty of any kind. + +7. UPDATES. Microsoft may periodically update the software. You may obtain updates +only from Microsoft or Microsoft-authorized sources. Updates may not include or +support all existing software features, services, or peripheral devices. + +8. TERMINATION. Without prejudice to any other rights, Microsoft may terminate this +agreement if you fail to comply with any of its terms or conditions. In such +event, you must destroy all copies of the software and all of its component +parts. + +9. ENTIRE AGREEMENT. This agreement, and any other terms Microsoft may provide for +supplements, updates, or third-party applications, is the entire agreement for +the software. To the extent you have entered into a separate agreement with +Microsoft relating specifically to the software, the terms in such agreement +shall control. + +10. APPLICABLE LAW AND PLACE TO RESOLVE DISPUTES. If you acquired the software in +the United States or Canada, the laws of the state or province where you live +(or, if a business, where your principal place of business is located) govern +the interpretation of this agreement, claims for its breach, and all other +claims (including consumer protection, unfair competition, and tort claims), +regardless of conflict of laws principles. If you acquired the software in any +other country, its laws apply. If U.S. federal jurisdiction exists, you and +Microsoft consent to exclusive jurisdiction and venue in the federal court in +King County, Washington for all disputes heard in court. If not, you and +Microsoft consent to exclusive jurisdiction and venue in the Superior Court of +King County, Washington for all disputes heard in court. + +11. CONSUMER RIGHTS; REGIONAL VARIATIONS. This agreement describes certain legal +rights. You may have other rights, including consumer rights, under the laws of +your state or country. Separate and apart from your relationship with Microsoft, +you may also have rights with respect to the party from which you acquired the +software. This agreement does not change those other rights if the laws of your +state or country do not permit it to do so. For example, if you acquired the +software in one of the below regions, or mandatory country law applies, then the +following provisions apply to you: + +a) Australia. You have statutory guarantees under the Australian Consumer Law and +nothing in this agreement is intended to affect those rights. + +b) Germany and Austria. + +i. Warranty. The properly licensed software will perform substantially as +described in any Microsoft materials that accompany the software. However, +Microsoft gives no contractual guarantee in relation to the licensed software. + +ii. Limitation of Liability. In case of intentional conduct, gross negligence, +claims based on the Product Liability Act, as well as, in case of death or +personal or physical injury, Microsoft is liable according to the statutory law. + + +Subject to the foregoing clause ii., Microsoft will only be liable for slight +negligence if Microsoft is in breach of such material contractual obligations, +the fulfillment of which facilitate the due performance of this agreement, the +breach of which would endanger the purpose of this agreement and the compliance +with which a party may constantly trust in (so-called "cardinal obligations"). +In other cases of slight negligence, Microsoft will not be liable for slight +negligence. +12. DISCLAIMER OF WARRANTY. +a) THE SOFTWARE IS LICENSED “AS IS.” YOU BEAR THE RISK OF +USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES, OR CONDITIONS. TO +THE EXTENT PERMITTED UNDER APPLICABLE LAWS, MICROSOFT EXCLUDES ALL IMPLIED +WARRANTIES, INCLUDING MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT. +b) HIGH RISK USE DISCLAIMER. WARNING: THE SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE WHERE FAILURE OR FAULT OF ANY KIND OF THE SOFTWARE COULD RESULT IN DEATH OR SERIOUS BODILY INJURY, OR IN PHYSICAL OR ENVIRONMENTAL DAMAGE (“collectively High Risk Use”). Accordingly, You must design and implement your hardware and software such that, in the event of any interruption, defect, error, or other failure of the software, the safety of people, property, and the environment are not reduced below a level that is reasonable, appropriate, and legal, whether in general or for a specific industry. Your High Risk Use of the software is at Your own risk. + +13. LIMITATION ON AND EXCLUSION OF DAMAGES. IF YOU HAVE ANY BASIS FOR RECOVERING +DAMAGES DESPITE THE PRECEDING DISCLAIMER OF WARRANTY, YOU CAN RECOVER FROM +MICROSOFT AND ITS SUPPLIERS ONLY DIRECT DAMAGES UP TO U.S. $5.00. YOU CANNOT +RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL, +INDIRECT, OR INCIDENTAL DAMAGES. + +This limitation applies to (a) anything related to the software, services, +content (including code) on third party Internet sites, or third party +applications; and (b) claims for breach of contract, warranty, guarantee, or +condition; strict liability, negligence, or other tort; or any other claim; in +each case to the extent permitted by applicable law. + +It also applies even if Microsoft knew or should have known about the +possibility of the damages. The above limitation or exclusion may not apply to +you because your state, province, or country may not allow the exclusion or +limitation of incidental, consequential, or other damages. + + Please note: As this software is distributed in Canada, some of the clauses in +this agreement are provided below in French. + +Remarque: Ce logiciel étant distribué au Canada, certaines des clauses dans ce +contrat sont fournies ci-dessous en français. + +EXONÉRATION DE GARANTIE. +a) Le logiciel visé par une licence est offert « tel quel +». Toute utilisation de ce logiciel est à votre seule risque et péril. Microsoft +n’accorde aucune autre garantie expresse. Vous pouvez bénéficier de droits +additionnels en vertu du droit local sur la protection des consommateurs, que ce +contrat ne peut modifier. La ou elles sont permises par le droit locale, les +garanties implicites de qualité marchande, d’adéquation à un usage particulier +et d’absence de contrefaçon sont exclues. +b) CLAUSE D’EXCLUSION DE RESPONSABILITÉ RELATIVE À L’UTILISATION À HAUT RISQUE. +AVERTISSEMENT: LE LOGICIEL N’EST PAS CONÇU OU DESTINÉ À ÊTRE UTILISÉ LORSQU’UNE +DÉFAILLANCE OU UN DÉFAUT DE QUELQUE NATURE QUE CE SOIT POURRAIT ENTRAÎNER LA +MORT OU DES BLESSURES CORPORELLES GRAVES, OU DES DOMMAGES PHYSIQUES OU +ENVIRONNEMENTAUX (« Utilisation à haut risque »). Par conséquent, vous devez concevoir et mettre en +œuvre votre équipement et votre logiciel de manière à ce que, en cas d’interruption, de défaut, d’erreur +ou de toute autre défaillance du logiciel, la sécurité des personnes, des biens et de l’environnement ne +soit pas réduite en dessous d’un niveau raisonnable, approprié et légal, que ce soit en général ou pour +un secteur spécifique. Votre utilisation à haut risque du logiciel est à vos propres risques. + +LIMITATION DES DOMMAGES-INTÉRÊTS ET EXCLUSION DE RESPONSABILITÉ POUR LES +DOMMAGES. Vous pouvez obtenir de Microsoft et de ses fournisseurs une +indemnisation en cas de dommages directs uniquement à hauteur de 5,00 $ US. Vous +ne pouvez prétendre à aucune indemnisation pour les autres dommages, y compris +les dommages spéciaux, indirects ou accessoires et pertes de bénéfices. + +Cette limitation concerne: + +•tout ce qui est relié au logiciel, aux services ou au contenu (y compris le +code) figurant sur des sites Internet tiers ou dans des programmes tiers; et + +•les réclamations au titre de violation de contrat ou de garantie, ou au titre +de responsabilité stricte, de négligence ou d’une autre faute dans la limite +autorisée par la loi en vigueur. + +Elle s’applique également, même si Microsoft connaissait ou devrait connaître +l’éventualité d’un tel dommage. Si votre pays n’autorise pas l’exclusion ou la +limitation de responsabilité pour les dommages indirects, accessoires ou de +quelque nature que ce soit, il se peut que la limitation ou l’exclusion +ci-dessus ne s’appliquera pas à votre égard. + +EFFET JURIDIQUE. Le présent contrat décrit certains droits juridiques. Vous +pourriez avoir d’autres droits prévus par les lois de votre pays. Le présent +contrat ne modifie pas les droits que vous confèrent les lois de votre pays si +celles-ci ne le permettent pas. diff --git a/LICENSED-HARDWARE.txt b/LICENSED-HARDWARE.txt new file mode 100644 index 0000000..b142459 --- /dev/null +++ b/LICENSED-HARDWARE.txt @@ -0,0 +1,45 @@ +LICENSED HARDWARE LIST + +Last Updated: 2023-02-10 + +Microsoft has entered into OEM Agreements with manufacturers of the following +microprocessors and microcontrollers (the “hardware”) to enable those +manufacturers to include and distribute Azure RTOS in certain hardware. If you +have obtained and/or are developing on microprocessor(s) and/or +microcontroller(s) (“hardware”) listed below you inherit the “Distribution and +Production Use” rights in Section 2 of the Microsoft Software License Terms for +Microsoft Azure RTOS. If hardware is not listed below, you do not have those +rights. + + +STMicroelectronics: + + STM32 MCUs + STM32 MPUs + +MediaTek: + + MT3620 MCU (Azure Sphere) + +Microchip: + + PIC 32-bit MCUs + SAM 32-bit MCUs + SAM 32-bit MPUs + +NXP: + + i.MX RT10xx and RT116x/7x series crossover MCUs + LPC5500 series + MCX N series + +Renesas: + + Synergy Platform + RX Family of MCUs + RA Family of MCUs + RZ Family of MPUs + +-------------------------------------------------------------------------------- + +More coming soon. Please check back frequently for updates. diff --git a/README.md b/README.md new file mode 100755 index 0000000..7ae9186 --- /dev/null +++ b/README.md @@ -0,0 +1,152 @@ +# Azure RTOS FileX + +This is a high-performance, file allocation table (FAT)-compatible file system that’s fully integrated with Azure RTOS ThreadX and available for all supported processors. Like Azure RTOS ThreadX, Azure RTOS FileX is designed to have a small footprint and high performance, making it ideal for today’s deeply embedded applications that require file management operations. FileX supports most physical media, including RAM, Azure RTOS USBX, SD CARD, and NAND/NOR flash memories via Azure RTOS LevelX. + +Here are the key features and modules of FileX: + +![FileX Key Features](./docs/filex-features.png) + +## Getting Started + +Azure RTOS FileX as part of Azure RTOS has been integrated to the semiconductor's SDKs and development environment. You can develop using the tools of choice from [STMicroelectronics](https://www.st.com/content/st_com/en/campaigns/x-cube-azrtos-azure-rtos-stm32.html), [NXP](https://www.nxp.com/design/software/embedded-software/azure-rtos-for-nxp-microcontrollers:AZURE-RTOS), [Renesas](https://github.com/renesas/azure-rtos) and [Microchip](https://mu.microchip.com/get-started-simplifying-your-iot-design-with-azure-rtos). + +We also provide [samples](https://github.com/azure-rtos/samples) using hero development boards from semiconductors you can build and test with. + +See [Overview of Azure RTOS FileX](https://learn.microsoft.com/azure/rtos/filex/overview-filex) for the high-level overview, and all documentation and APIs can be found in: [Azure RTOS FileX documentation](https://learn.microsoft.com/azure/rtos/filex). + +## Repository Structure and Usage + +### Directory layout + + . + ├── cmake # CMakeList files for building the project + ├── common # Core FileX files + ├── ports # Architecture and compiler specific files + ├── samples # Sample codes + ├── LICENSE.txt # License terms + ├── LICENSE-HARDWARE.txt # Licensed hardware from semiconductors + ├── CONTRIBUTING.md # Contribution guidance + └── SECURITY.md # Microsoft repo security guidance + +### Branches & Releases + +The master branch has the most recent code with all new features and bug fixes. It does not represent the latest General Availability (GA) release of the library. Each official release (preview or GA) will be tagged to mark the commit and push it into the Github releases tab, e.g. `v6.2-rel`. + +> When you see xx-xx-xxxx, 6.x or x.x in function header, this means the file is not officially released yet. They will be updated in the next release. See example below. +``` +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level Cortex-M23/GNU */ +/* 6.x */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for any low-level processor */ +/* initialization, including setting up interrupt vectors, setting */ +/* up a periodic timer interrupt source, saving the system stack */ +/* pointer for use in ISR processing later, and finding the first */ +/* available RAM memory address for tx_application_define. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* xx-xx-xxxx Scott Larson Include tx_user.h, */ +/* resulting in version 6.x */ +/* */ +/**************************************************************************/ +``` + +## Component dependencies + +The main components of Azure RTOS are each provided in their own repository, but there are dependencies between them, as shown in the following graph. This is important to understand when setting up your builds. + +![dependency graph](docs/deps.png) + +> You will have to take the dependency graph above into account when building anything other than ThreadX itself. + +### Building and using the library + +Instruction for building the FileX as static library using Arm GNU Toolchain and CMake. If you are using toolchain and IDE from semiconductor, you might follow its own instructions to use Azure RTOS components as explained in the [Getting Started](#getting-started) section. + +1. Install the following tools: + + * [CMake](https://cmake.org/download/) version 3.0 or later + * [Arm GNU Toolchain for arm-none-eabi](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads) + * [Ninja](https://ninja-build.org/) + +1. Build the [ThreadX library](https://github.com/azure-rtos/threadx#building-and-using-the-library) as the dependency. + +1. Cloning the repo. + + ```bash + $ git clone https://github.com/azure-rtos/filex.git + ``` + +1. Define the features and addons you need in `fx_user.h` and build together with the component source code. You can refer to [`fx_user_sample.h`](https://github.com/azure-rtos/filex/blob/master/common/inc/fx_user_sample.h) as an example. + +1. Building as a static library + + Each component of Azure RTOS comes with a composable CMake-based build system that supports many different MCUs and host systems. Integrating any of these components into your device app code is as simple as adding a git submodule and then including it in your build using the CMake `add_subdirectory()`. + + While the typical usage pattern is to include FileX into your device code source tree to be built & linked with your code, you can compile this project as a standalone static library to confirm your build is set up correctly. + + An example of building the library for Cortex-M4: + + ```bash + $ cmake -Bbuild -GNinja -DCMAKE_TOOLCHAIN_FILE=cmake/cortex_m4.cmake . + + $ cmake --build ./build + ``` + +## Professional support + +[Professional support plans](https://azure.microsoft.com/support/options/) are available from Microsoft. For community support and others, see the [Resources](#resources) section below. + +## Licensing + +License terms for using Azure RTOS are defined in the LICENSE.txt file of this repo. Please refer to this file for all definitive licensing information. No additional license fees are required for deploying Azure RTOS on hardware defined in the [LICENSED-HARDWARE.txt](./LICENSED-HARDWARE.txt) file. If you are using hardware not listed in the file or having licensing questions in general, please contact Microsoft directly at https://aka.ms/azrtos-license. + +## Resources + +The following are references to additional Azure RTOS resources: + +- **Product introduction and white papers**: https://azure.com/rtos +- **General technical questions**: https://aka.ms/QnA/azure-rtos +- **Product issues and bugs, or feature requests**: https://github.com/azure-rtos/filex/issues +- **Licensing and sales questions**: https://aka.ms/azrtos-license +- **Product roadmap and support policy**: https://aka.ms/azrtos/lts +- **Blogs and videos**: http://msiotblog.com and https://aka.ms/iotshow +- **Azure RTOS TraceX Installer**: https://aka.ms/azrtos-tracex-installer + +You can also check [previous questions](https://stackoverflow.com/questions/tagged/azure-rtos+filex) or ask new ones on StackOverflow using the `azure-rtos` and `filex` tags. + +## Security + +Azure RTOS provides OEMs with components to secure communication and to create code and data isolation using underlying MCU/MPU hardware protection mechanisms. It is ultimately the responsibility of the device builder to ensure the device fully meets the evolving security requirements associated with its specific use case. + +## Contribution + +Please follow the instructions provided in the [CONTRIBUTING.md](./CONTRIBUTING.md) for the corresponding repository. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..323bf81 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,35 @@ +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). \ No newline at end of file diff --git a/cmake/arm-none-eabi.cmake b/cmake/arm-none-eabi.cmake new file mode 100644 index 0000000..147ce7a --- /dev/null +++ b/cmake/arm-none-eabi.cmake @@ -0,0 +1,29 @@ +# Toolchain settings +set(CMAKE_C_COMPILER arm-none-eabi-gcc) +set(CMAKE_CXX_COMPILER arm-none-eabi-g++) +set(AS arm-none-eabi-as) +set(AR arm-none-eabi-ar) +set(OBJCOPY arm-none-eabi-objcopy) +set(OBJDUMP arm-none-eabi-objdump) +set(SIZE arm-none-eabi-size) + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +# this makes the test compiles use static library option so that we don't need to pre-set linker flags and scripts +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + +set(CMAKE_C_FLAGS "${MCPU_FLAGS} ${VFP_FLAGS} ${SPEC_FLAGS} -fdata-sections -ffunction-sections -mlong-calls" CACHE INTERNAL "c compiler flags") +set(CMAKE_CXX_FLAGS "${MCPU_FLAGS} ${VFP_FLAGS} -fdata-sections -ffunction-sections -fno-rtti -fno-exceptions -mlong-calls" CACHE INTERNAL "cxx compiler flags") +set(CMAKE_ASM_FLAGS "${MCPU_FLAGS} -x assembler-with-cpp" CACHE INTERNAL "asm compiler flags") +set(CMAKE_EXE_LINKER_FLAGS "${MCPU_FLAGS} ${LD_FLAGS} -Wl,--gc-sections" CACHE INTERNAL "exe link flags") + +SET(CMAKE_C_FLAGS_DEBUG "-Og -g -ggdb3" CACHE INTERNAL "c debug compiler flags") +SET(CMAKE_CXX_FLAGS_DEBUG "-Og -g -ggdb3" CACHE INTERNAL "cxx debug compiler flags") +SET(CMAKE_ASM_FLAGS_DEBUG "-g -ggdb3" CACHE INTERNAL "asm debug compiler flags") + +SET(CMAKE_C_FLAGS_RELEASE "-O3" CACHE INTERNAL "c release compiler flags") +SET(CMAKE_CXX_FLAGS_RELEASE "-O3" CACHE INTERNAL "cxx release compiler flags") +SET(CMAKE_ASM_FLAGS_RELEASE "" CACHE INTERNAL "asm release compiler flags") \ No newline at end of file diff --git a/cmake/cortex_m0.cmake b/cmake/cortex_m0.cmake new file mode 100644 index 0000000..82a50ec --- /dev/null +++ b/cmake/cortex_m0.cmake @@ -0,0 +1,13 @@ +# Name of the target +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR cortex-m0) + +set(THREADX_ARCH "cortex_m0") +set(THREADX_TOOLCHAIN "gnu") + +set(MCPU_FLAGS "-mthumb -mcpu=cortex-m0") +set(VFP_FLAGS "") +set(SPEC_FLAGS "--specs=nosys.specs") +# set(LD_FLAGS "-nostartfiles") + +include(${CMAKE_CURRENT_LIST_DIR}/arm-none-eabi.cmake) \ No newline at end of file diff --git a/cmake/cortex_m3.cmake b/cmake/cortex_m3.cmake new file mode 100644 index 0000000..9cc1e06 --- /dev/null +++ b/cmake/cortex_m3.cmake @@ -0,0 +1,13 @@ +# Name of the target +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR cortex-m3) + +set(THREADX_ARCH "cortex_m3") +set(THREADX_TOOLCHAIN "gnu") + +set(MCPU_FLAGS "-mthumb -mcpu=cortex-m3") +set(VFP_FLAGS "") +set(SPEC_FLAGS "--specs=nosys.specs") +# set(LD_FLAGS "-nostartfiles") + +include(${CMAKE_CURRENT_LIST_DIR}/arm-none-eabi.cmake) \ No newline at end of file diff --git a/cmake/cortex_m4.cmake b/cmake/cortex_m4.cmake new file mode 100644 index 0000000..c391735 --- /dev/null +++ b/cmake/cortex_m4.cmake @@ -0,0 +1,13 @@ +# Name of the target +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR cortex-m4) + +set(THREADX_ARCH "cortex_m4") +set(THREADX_TOOLCHAIN "gnu") + +set(MCPU_FLAGS "-mthumb -mcpu=cortex-m4") +set(VFP_FLAGS "") +set(SPEC_FLAGS "--specs=nosys.specs") +# set(LD_FLAGS "-nostartfiles") + +include(${CMAKE_CURRENT_LIST_DIR}/arm-none-eabi.cmake) \ No newline at end of file diff --git a/cmake/cortex_m7.cmake b/cmake/cortex_m7.cmake new file mode 100644 index 0000000..c4e48fa --- /dev/null +++ b/cmake/cortex_m7.cmake @@ -0,0 +1,13 @@ +# Name of the target +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR cortex-m7) + +set(THREADX_ARCH "cortex_m7") +set(THREADX_TOOLCHAIN "gnu") + +set(MCPU_FLAGS "-mthumb -mcpu=cortex-m7") +set(VFP_FLAGS "") +set(SPEC_FLAGS "--specs=nosys.specs") +# set(LD_FLAGS "-nostartfiles") + +include(${CMAKE_CURRENT_LIST_DIR}/arm-none-eabi.cmake) \ No newline at end of file diff --git a/cmake/utilities.cmake b/cmake/utilities.cmake new file mode 100644 index 0000000..801d87e --- /dev/null +++ b/cmake/utilities.cmake @@ -0,0 +1,15 @@ +# Add the required subdirectory and register it for linkage +function(add_azrtos_component_dir dirname) + # Store the current list in a temp + set(tmp ${azrtos_targets}) + # Add the subdir + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/lib/${dirname}) + # If there is a linker script defined, use it + if(EXISTS ${LINKER_SCRIPT}) + target_link_options(${dirname} INTERFACE -T ${LINKER_SCRIPT}) + endif() + # Add this target into the temp + list(APPEND tmp "azrtos::${dirname}") + # Copy the temp back up to the parent list + set(azrtos_targets ${tmp} PARENT_SCOPE) +endfunction() \ No newline at end of file diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt new file mode 100644 index 0000000..e08512a --- /dev/null +++ b/common/CMakeLists.txt @@ -0,0 +1,221 @@ +target_sources(${PROJECT_NAME} PRIVATE + # {{BEGIN_TARGET_SOURCES}} + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_attributes_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_attributes_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_default_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_default_get_copy.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_default_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_entry_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_entry_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_first_entry_find.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_first_full_entry_find.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_free_search.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_information_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_local_path_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_local_path_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_local_path_get_copy.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_local_path_restore.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_local_path_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_long_name_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_long_name_get_extended.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_name_extract.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_name_test.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_next_entry_find.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_next_full_entry_find.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_rename.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_search.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_short_name_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_directory_short_name_get_extended.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_fault_tolerant_add_FAT_log.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_fault_tolerant_add_bitmap_log.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_fault_tolerant_add_checksum_log.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_fault_tolerant_add_dir_log.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_fault_tolerant_apply_logs.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_fault_tolerant_calculate_checksum.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_fault_tolerant_cleanup_FAT_chain.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_fault_tolerant_create_log_file.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_fault_tolerant_enable.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_fault_tolerant_read_FAT.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_fault_tolerant_read_directory_sector.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_fault_tolerant_read_log_file.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_fault_tolerant_recover.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_fault_tolerant_reset_log_file.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_fault_tolerant_set_FAT_chain.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_fault_tolerant_transaction_end.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_fault_tolerant_transaction_fail.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_fault_tolerant_transaction_start.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_fault_tolerant_write_log_file.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_file_allocate.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_file_attributes_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_file_attributes_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_file_best_effort_allocate.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_file_close.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_file_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_file_date_time_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_file_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_file_extended_allocate.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_file_extended_best_effort_allocate.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_file_extended_relative_seek.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_file_extended_seek.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_file_extended_truncate.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_file_extended_truncate_release.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_file_open.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_file_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_file_relative_seek.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_file_rename.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_file_seek.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_file_truncate.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_file_truncate_release.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_file_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_file_write_notify_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_media_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_media_boot_info_extract.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_media_cache_invalidate.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_media_check.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_media_check_FAT_chain_check.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_media_check_lost_cluster_check.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_media_close.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_media_close_notify_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_media_extended_space_available.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_media_flush.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_media_format.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_media_format_oem_name_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_media_format_type_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_media_format_volume_id_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_media_open.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_media_open_notify_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_media_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_media_space_available.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_media_volume_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_media_volume_get_extended.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_media_volume_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_media_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_partition_offset_calculate.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_ram_driver.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_system_date_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_system_date_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_system_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_system_time_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_system_time_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_system_timer_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_trace_event_insert.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_trace_event_update.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_trace_object_register.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_trace_object_unregister.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_unicode_directory_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_unicode_directory_entry_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_unicode_directory_entry_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_unicode_directory_rename.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_unicode_directory_search.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_unicode_file_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_unicode_file_rename.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_unicode_length_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_unicode_length_get_extended.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_unicode_name_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_unicode_name_get_extended.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_unicode_short_name_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_unicode_short_name_get_extended.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_utility_16_unsigned_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_utility_16_unsigned_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_utility_32_unsigned_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_utility_32_unsigned_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_utility_64_unsigned_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_utility_64_unsigned_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_utility_FAT_entry_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_utility_FAT_entry_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_utility_FAT_flush.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_utility_FAT_map_flush.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_utility_FAT_sector_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_utility_absolute_path_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_utility_logical_sector_cache_entry_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_utility_logical_sector_flush.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_utility_logical_sector_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_utility_logical_sector_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_utility_memory_copy.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_utility_memory_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_utility_string_length_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_utility_token_length_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_directory_attributes_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_directory_attributes_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_directory_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_directory_default_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_directory_default_get_copy.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_directory_default_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_directory_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_directory_first_entry_find.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_directory_first_full_entry_find.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_directory_information_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_directory_local_path_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_directory_local_path_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_directory_local_path_get_copy.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_directory_local_path_restore.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_directory_local_path_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_directory_long_name_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_directory_long_name_get_extended.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_directory_name_test.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_directory_next_entry_find.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_directory_next_full_entry_find.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_directory_rename.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_directory_short_name_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_directory_short_name_get_extended.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_fault_tolerant_enable.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_file_allocate.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_file_attributes_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_file_attributes_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_file_best_effort_allocate.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_file_close.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_file_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_file_date_time_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_file_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_file_extended_allocate.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_file_extended_best_effort_allocate.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_file_extended_relative_seek.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_file_extended_seek.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_file_extended_truncate.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_file_extended_truncate_release.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_file_open.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_file_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_file_relative_seek.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_file_rename.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_file_seek.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_file_truncate.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_file_truncate_release.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_file_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_file_write_notify_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_media_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_media_cache_invalidate.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_media_check.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_media_close.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_media_close_notify_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_media_extended_space_available.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_media_flush.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_media_format.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_media_open.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_media_open_notify_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_media_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_media_space_available.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_media_volume_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_media_volume_get_extended.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_media_volume_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_media_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_system_date_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_system_date_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_system_time_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_system_time_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_unicode_directory_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_unicode_directory_rename.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_unicode_file_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_unicode_file_rename.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_unicode_name_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_unicode_name_get_extended.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_unicode_short_name_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/fxe_unicode_short_name_get_extended.c + + # {{END_TARGET_SOURCES}} +) + +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc +) diff --git a/common/inc/fx_api.h b/common/inc/fx_api.h new file mode 100644 index 0000000..e583f73 --- /dev/null +++ b/common/inc/fx_api.h @@ -0,0 +1,1539 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Application Interface */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* APPLICATION INTERFACE DEFINITION RELEASE */ +/* */ +/* fx_api.h PORTABLE C */ +/* 6.4.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file defines the basic Application Interface (API) to the */ +/* high-performance FileX FAT compatible embedded file system. */ +/* All service prototypes and data structure definitions are defined */ +/* in this file. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), and */ +/* updated product constants, */ +/* and added conditionals to */ +/* disable few declarations */ +/* for code size reduction, */ +/* resulting in version 6.1 */ +/* 11-09-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1.2 */ +/* 12-31-2020 William E. Lamie Modified comment(s), and */ +/* updated product constants, */ +/* resulting in version 6.1.3 */ +/* 03-02-2021 William E. Lamie Modified comment(s), and */ +/* added standalone support, */ +/* resulting in version 6.1.5 */ +/* 04-02-2021 William E. Lamie Modified comment(s), and */ +/* updated product constants, */ +/* resulting in version 6.1.6 */ +/* 06-02-2021 William E. Lamie Modified comment(s), and */ +/* updated product constants, */ +/* resulting in version 6.1.7 */ +/* 08-02-2021 William E. Lamie Modified comment(s), and */ +/* updated product constants, */ +/* resulting in version 6.1.8 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s), fixed */ +/* errors without cache, */ +/* resulting in version 6.1.10 */ +/* 04-25-2022 William E. Lamie Modified comment(s), and */ +/* updated product constants, */ +/* resulting in version 6.1.11 */ +/* 07-29-2022 William E. Lamie Modified comment(s), and */ +/* updated product constants, */ +/* resulting in version 6.1.12 */ +/* 10-31-2022 Xiuwen Cai Modified comment(s), and */ +/* updated product constants, */ +/* resulting in version 6.2.0 */ +/* 03-08-2023 Xiuwen Cai Modified comment(s), and */ +/* updated product constants, */ +/* resulting in version 6.2.1 */ +/* 10-31-2023 Xiuwen Cai Modified comment(s), and */ +/* updated product constants, */ +/* resulting in version 6.3.0 */ +/* 12-31-2023 Xiuwen Cai Modified comment(s), and */ +/* updated product constants, */ +/* resulting in version 6.4.0 */ +/* */ +/**************************************************************************/ + +#ifndef FX_API_H +#define FX_API_H + + +/* Determine if a C++ compiler is being used. If so, ensure that standard + C is used to process the API information. */ + +#ifdef __cplusplus + +/* Yes, C++ compiler is present. Use standard C. */ +extern "C" { + +#endif + + +/* Disable warning of parameter not used. */ +#ifndef FX_PARAMETER_NOT_USED +#define FX_PARAMETER_NOT_USED(p) ((void)(p)) +#endif /* FX_PARAMETER_NOT_USED */ + + +/* Disable ThreadX error checking for internal FileX source code. */ + +#ifdef FX_SOURCE_CODE +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif +#endif + + +/* Include the FileX port-specific file. */ + +#include "fx_port.h" + +/* Define compiler library include files */ + +#ifdef FX_STANDALONE_ENABLE +#include "string.h" +#endif + +/* Define the major/minor version information that can be used by the application + and the FileX source as well. */ + +#define AZURE_RTOS_FILEX +#define FILEX_MAJOR_VERSION 6 +#define FILEX_MINOR_VERSION 4 +#define FILEX_PATCH_VERSION 0 + +/* Define the following symbols for backward compatibility */ +#define EL_PRODUCT_FILEX + +#ifdef FX_STANDALONE_ENABLE + +/* FileX will be used without Azure RTOS ThreadX */ + +#ifndef FX_SINGLE_THREAD +#define FX_SINGLE_THREAD +#endif /* !FX_SINGLE_THREAD */ + + +/* FileX will be used with local path logic disabled */ + +#ifndef FX_NO_LOCAL_PATH +#define FX_NO_LOCAL_PATH +#endif /* !FX_NO_LOCAL_PATH */ + + +/* FileX is built without update to the time parameters. */ + +#ifndef FX_NO_TIMER +#define FX_NO_TIMER +#endif /* !FX_NO_TIMER */ + +#endif + + +/* Override the interrupt protection provided in FileX port files to simply use ThreadX protection, + which is often in-line assembly. */ + +#ifndef FX_LEGACY_INTERRUPT_PROTECTION +#undef FX_INT_SAVE_AREA +#undef FX_DISABLE_INTS +#undef FX_RESTORE_INTS +#define FX_INT_SAVE_AREA TX_INTERRUPT_SAVE_AREA +#define FX_DISABLE_INTS TX_DISABLE +#define FX_RESTORE_INTS TX_RESTORE +#endif + + +/* Define the update rate of the system timer. These values may also be defined at the command + line when compiling the fx_system_initialize.c module in the FileX library build. Alternatively, they can + be modified in this file. Note: the update rate must be an even number of seconds greater + than or equal to 2, which is the minimal update rate for FAT time. */ + +#ifndef FX_UPDATE_RATE_IN_SECONDS +#define FX_UPDATE_RATE_IN_SECONDS 10 /* Update time at 10 second intervals */ +#endif + +#ifndef FX_UPDATE_RATE_IN_TICKS +#define FX_UPDATE_RATE_IN_TICKS 1000 /* Same update rate, but in ticks */ +#endif + + +/* Determine if fault tolerance is selected. If so, turn on the old fault tolerant options - + if they are not defined already. */ + +#ifdef FX_ENABLE_FAULT_TOLERANT +#ifndef FX_FAULT_TOLERANT_DATA +#define FX_FAULT_TOLERANT_DATA +#endif +#ifndef FX_FAULT_TOLERANT +#define FX_FAULT_TOLERANT +#endif +#endif + + +/* Determine if cache is disabled. If so, disable direct read sector cache. */ +#ifdef FX_DISABLE_CACHE +#ifndef FX_DISABLE_DIRECT_DATA_READ_CACHE_FILL +#define FX_DISABLE_DIRECT_DATA_READ_CACHE_FILL +#endif +#endif + + +/* Determine if local paths are enabled and if the local path setup code has not been defined. + If so, define the default local path setup code for files that reference the local path. */ + +#ifndef FX_NO_LOCAL_PATH +#ifndef FX_LOCAL_PATH_SETUP +#ifndef FX_SINGLE_THREAD +#define FX_LOCAL_PATH_SETUP extern TX_THREAD *_tx_thread_current_ptr; +#else +#define FX_NO_LOCAL_PATH +#endif +#endif +#endif + +/* Determine if tracing is enabled. */ + +#if defined(TX_ENABLE_EVENT_TRACE) && !defined(FX_STANDALONE_ENABLE) + + +/* Trace is enabled. Remap calls so that interrupts can be disabled around the actual event logging. */ + +#include "tx_trace.h" + + +/* Define the object types in FileX, if not defined. */ + +#ifndef FX_TRACE_OBJECT_TYPE_MEDIA +#define FX_TRACE_OBJECT_TYPE_MEDIA 9 /* P1 = FAT cache size, P2 = sector cache size */ +#define FX_TRACE_OBJECT_TYPE_FILE 10 /* none */ +#endif + + +/* Define event filters that can be used to selectively disable certain events or groups of events. */ + +#define FX_TRACE_ALL_EVENTS 0x00007800 /* All FileX events */ +#define FX_TRACE_INTERNAL_EVENTS 0x00000800 /* FileX internal events */ +#define FX_TRACE_MEDIA_EVENTS 0x00001000 /* FileX media events */ +#define FX_TRACE_DIRECTORY_EVENTS 0x00002000 /* FileX directory events */ +#define FX_TRACE_FILE_EVENTS 0x00004000 /* FileX file events */ + + +/* Define the trace events in FileX, if not defined. */ + +/* Define FileX Trace Events, along with a brief description of the additional information fields, + where I1 -> Information Field 1, I2 -> Information Field 2, etc. */ + +/* Define the FileX internal events first. */ + +#ifndef FX_TRACE_INTERNAL_LOG_SECTOR_CACHE_MISS +#define FX_TRACE_INTERNAL_LOG_SECTOR_CACHE_MISS 201 /* I1 = media ptr, I2 = sector, I3 = total misses, I4 = cache size */ +#define FX_TRACE_INTERNAL_DIR_CACHE_MISS 202 /* I1 = media ptr, I2 = total misses */ +#define FX_TRACE_INTERNAL_MEDIA_FLUSH 203 /* I1 = media ptr, I2 = dirty sectors */ +#define FX_TRACE_INTERNAL_DIR_ENTRY_READ 204 /* I1 = media ptr */ +#define FX_TRACE_INTERNAL_DIR_ENTRY_WRITE 205 /* I1 = media ptr */ +#define FX_TRACE_INTERNAL_IO_DRIVER_READ 206 /* I1 = media ptr, I2 = sector, I3 = number of sectors, I4 = buffer */ +#define FX_TRACE_INTERNAL_IO_DRIVER_WRITE 207 /* I1 = media ptr, I2 = sector, I3 = number of sectors, I4 = buffer */ +#define FX_TRACE_INTERNAL_IO_DRIVER_FLUSH 208 /* I1 = media ptr */ +#define FX_TRACE_INTERNAL_IO_DRIVER_ABORT 209 /* I1 = media ptr */ +#define FX_TRACE_INTERNAL_IO_DRIVER_INIT 210 /* I1 = media ptr */ +#define FX_TRACE_INTERNAL_IO_DRIVER_BOOT_READ 211 /* I1 = media ptr, I2 = buffer */ +#define FX_TRACE_INTERNAL_IO_DRIVER_RELEASE_SECTORS 212 /* I1 = media ptr, I2 = sector, I3 = number of sectors */ +#define FX_TRACE_INTERNAL_IO_DRIVER_BOOT_WRITE 213 /* I1 = media ptr, I2 = buffer */ +#define FX_TRACE_INTERNAL_IO_DRIVER_UNINIT 214 /* I1 = media ptr */ + + +/* Define the FileX API events. */ + +#define FX_TRACE_DIRECTORY_ATTRIBUTES_READ 220 /* I1 = media ptr, I2 = directory name, I3 = attributes */ +#define FX_TRACE_DIRECTORY_ATTRIBUTES_SET 221 /* I1 = media ptr, I2 = directory name, I3 = attributes */ +#define FX_TRACE_DIRECTORY_CREATE 222 /* I1 = media ptr, I2 = directory name */ +#define FX_TRACE_DIRECTORY_DEFAULT_GET 223 /* I1 = media ptr, I2 = return path name */ +#define FX_TRACE_DIRECTORY_DEFAULT_SET 224 /* I1 = media ptr, I2 = new path name */ +#define FX_TRACE_DIRECTORY_DELETE 225 /* I1 = media ptr, I2 = directory name */ +#define FX_TRACE_DIRECTORY_FIRST_ENTRY_FIND 226 /* I1 = media ptr, I2 = directory name */ +#define FX_TRACE_DIRECTORY_FIRST_FULL_ENTRY_FIND 227 /* I1 = media ptr, I2 = directory name */ +#define FX_TRACE_DIRECTORY_INFORMATION_GET 228 /* I1 = media ptr, I2 = directory name */ +#define FX_TRACE_DIRECTORY_LOCAL_PATH_CLEAR 229 /* I1 = media ptr */ +#define FX_TRACE_DIRECTORY_LOCAL_PATH_GET 230 /* I1 = media ptr, I2 = return path name */ +#define FX_TRACE_DIRECTORY_LOCAL_PATH_RESTORE 231 /* I1 = media ptr, I2 = local path ptr */ +#define FX_TRACE_DIRECTORY_LOCAL_PATH_SET 232 /* I1 = media ptr, I2 = local path ptr, I3 = new path name */ +#define FX_TRACE_DIRECTORY_LONG_NAME_GET 233 /* I1 = media ptr, I2 = short file name, I3 = long file name */ +#define FX_TRACE_DIRECTORY_NAME_TEST 234 /* I1 = media ptr, I2 = directory name */ +#define FX_TRACE_DIRECTORY_NEXT_ENTRY_FIND 235 /* I1 = media ptr, I2 = directory name */ +#define FX_TRACE_DIRECTORY_NEXT_FULL_ENTRY_FIND 236 /* I1 = media ptr, I2 = directory name */ +#define FX_TRACE_DIRECTORY_RENAME 237 /* I1 = media ptr, I2 = old directory name, I3 = new directory name */ +#define FX_TRACE_DIRECTORY_SHORT_NAME_GET 238 /* I1 = media ptr, I2 = long file name, I3 = short file name */ +#define FX_TRACE_FILE_ALLOCATE 239 /* I1 = file ptr, I2 = size I3 = previous size, I4 = new size */ +#define FX_TRACE_FILE_ATTRIBUTES_READ 240 /* I1 = media ptr, I2 = file name, I3 = attributes */ +#define FX_TRACE_FILE_ATTRIBUTES_SET 241 /* I1 = media ptr, I2 = file name, I3 = attributes */ +#define FX_TRACE_FILE_BEST_EFFORT_ALLOCATE 242 /* I1 = file ptr, I2 = size, I3 = actual_size_allocated */ +#define FX_TRACE_FILE_CLOSE 243 /* I1 = file ptr, I3 = file size */ +#define FX_TRACE_FILE_CREATE 244 /* I1 = media ptr, I2 = file name */ +#define FX_TRACE_FILE_DATE_TIME_SET 245 /* I1 = media ptr, I2 = file name, I3 = year, I4 = month */ +#define FX_TRACE_FILE_DELETE 246 /* I1 = media ptr, I2 = file name */ +#define FX_TRACE_FILE_OPEN 247 /* I1 = media ptr, I2 = file ptr, I3 = file name, I4 = open type */ +#define FX_TRACE_FILE_READ 248 /* I1 = file ptr, I2 = buffer ptr, I3 = request size I4 = actual size */ +#define FX_TRACE_FILE_RELATIVE_SEEK 249 /* I1 = file ptr, I2 = byte offset, I3 = seek from, I4 = previous offset */ +#define FX_TRACE_FILE_RENAME 250 /* I1 = media ptr, I2 = old file name, I3 = new file name */ +#define FX_TRACE_FILE_SEEK 251 /* I1 = file ptr, I2 = byte offset, I3 = previous offset */ +#define FX_TRACE_FILE_TRUNCATE 252 /* I1 = file ptr, I2 = size, I3 = previous size, I4 = new size */ +#define FX_TRACE_FILE_TRUNCATE_RELEASE 253 /* I1 = file ptr, I2 = size, I3 = previous size, I4 = new size */ +#define FX_TRACE_FILE_WRITE 254 /* I1 = file ptr, I2 = buffer ptr, I3 = size, I4 = bytes written */ +#define FX_TRACE_MEDIA_ABORT 255 /* I1 = media ptr */ +#define FX_TRACE_MEDIA_CACHE_INVALIDATE 256 /* I1 = media ptr */ +#define FX_TRACE_MEDIA_CHECK 257 /* I1 = media ptr, I2 = scratch memory, I3 = scratch memory size, I4 =errors*/ +#define FX_TRACE_MEDIA_CLOSE 258 /* I1 = media ptr */ +#define FX_TRACE_MEDIA_FLUSH 259 /* I1 = media ptr */ +#define FX_TRACE_MEDIA_FORMAT 260 /* I1 = media ptr, I2 = root entries, I3 = sectors, I4 = sectors per cluster*/ +#define FX_TRACE_MEDIA_OPEN 261 /* I1 = media ptr, I2 = media driver, I3 = memory ptr, I4 = memory size */ +#define FX_TRACE_MEDIA_READ 262 /* I1 = media ptr, I2 = logical sector, I3 = buffer ptr, I4 = bytes read */ +#define FX_TRACE_MEDIA_SPACE_AVAILABLE 263 /* I1 = media ptr, I2 = available bytes ptr, I3 = available clusters */ +#define FX_TRACE_MEDIA_VOLUME_GET 264 /* I1 = media ptr, I2 = volume name, I3 = volume source */ +#define FX_TRACE_MEDIA_VOLUME_SET 265 /* I1 = media ptr, I2 = volume name */ +#define FX_TRACE_MEDIA_WRITE 266 /* I1 = media ptr, I2 = logical_sector, I3 = buffer_ptr, I4 = byte written */ +#define FX_TRACE_SYSTEM_DATE_GET 267 /* I1 = year, I2 = month, I3 = day */ +#define FX_TRACE_SYSTEM_DATE_SET 268 /* I1 = year, I2 = month, I3 = day */ +#define FX_TRACE_SYSTEM_INITIALIZE 269 /* None */ +#define FX_TRACE_SYSTEM_TIME_GET 270 /* I1 = hour, I2 = minute, I3 = second */ +#define FX_TRACE_SYSTEM_TIME_SET 271 /* I1 = hour, I2 = minute, I3 = second */ +#define FX_TRACE_UNICODE_DIRECTORY_CREATE 272 /* I1 = media ptr, I2 = source unicode, I3 = source length, I4 = short_name */ +#define FX_TRACE_UNICODE_DIRECTORY_RENAME 273 /* I1 = media ptr, I2 = source unicode, I3 = source length, I4 = new_name */ +#define FX_TRACE_UNICODE_FILE_CREATE 274 /* I1 = media ptr, I2 = source unicode, I3 = source length, I4 = short name */ +#define FX_TRACE_UNICODE_FILE_RENAME 275 /* I1 = media ptr, I2 = source unicode, I3 = source length, I4 = new name */ +#define FX_TRACE_UNICODE_LENGTH_GET 276 /* I1 = unicode name, I2 = length */ +#define FX_TRACE_UNICODE_NAME_GET 277 /* I1 = media ptr, I2 = source short name, I3 = unicode name, I4 = length */ +#define FX_TRACE_UNICODE_SHORT_NAME_GET 278 /* I1 = media ptr, I2 = source unicode name, I3 = length, I4 = short name */ +#endif + + +/* Map the trace macros to internal FileX versions so we can get interrupt protection. */ + +#ifdef FX_SOURCE_CODE + +#define FX_TRACE_OBJECT_REGISTER(t, p, n, a, b) _fx_trace_object_register(t, (VOID *)p, (CHAR *)n, (ULONG)a, (ULONG)b); +#define FX_TRACE_OBJECT_UNREGISTER(o) _fx_trace_object_unregister((VOID *)o); +#define FX_TRACE_IN_LINE_INSERT(i, a, b, c, d, f, g, h) _fx_trace_event_insert((ULONG)i, (ULONG)a, (ULONG)b, (ULONG)c, (ULONG)d, (ULONG)f, g, h); +#define FX_TRACE_EVENT_UPDATE(e, t, i, a, b, c, d) _fx_trace_event_update((TX_TRACE_BUFFER_ENTRY *)e, (ULONG)t, (ULONG)i, (ULONG)a, (ULONG)b, (ULONG)c, (ULONG)d); + + +/* Define FileX trace prototypes. */ + +VOID _fx_trace_object_register(UCHAR object_type, VOID *object_ptr, CHAR *object_name, ULONG parameter_1, ULONG parameter_2); +VOID _fx_trace_object_unregister(VOID *object_ptr); +VOID _fx_trace_event_insert(ULONG event_id, ULONG info_field_1, ULONG info_field_2, ULONG info_field_3, ULONG info_field_4, ULONG filter, TX_TRACE_BUFFER_ENTRY **current_event, ULONG *current_timestamp); +VOID _fx_trace_event_update(TX_TRACE_BUFFER_ENTRY *event, ULONG timestamp, ULONG event_id, ULONG info_field_1, ULONG info_field_2, ULONG info_field_3, ULONG info_field_4); + +#endif + +#else +#define FX_TRACE_OBJECT_REGISTER(t, p, n, a, b) +#define FX_TRACE_OBJECT_UNREGISTER(o) +#define FX_TRACE_IN_LINE_INSERT(i, a, b, c, d, f, g, h) +#define FX_TRACE_EVENT_UPDATE(e, t, i, a, b, c, d) +#endif + + +/* Define basic constants for FileX. */ + +#define FX_MEDIA_ID ((ULONG)0x4D454449) +#define FX_MEDIA_CLOSED_ID ((ULONG)0x4D454443) +#define FX_MEDIA_ABORTED_ID ((ULONG)0x4D454441) + +#define FX_FILE_ID ((ULONG)0x46494C45) +#define FX_FILE_CLOSED_ID ((ULONG)0x46494C43) +#define FX_FILE_ABORTED_ID ((ULONG)0x46494C41) + + +/* The maximum path includes the entire path and the file name. */ + +#ifndef FX_MAXIMUM_PATH +#define FX_MAXIMUM_PATH 256 +#endif + + +/* Define directory entry name sizes. Directory entries are found in the + root directory and in directory files (sub-directories). Each directory + entry has a short file name which is a fixed maximum of 13 characters and + and optional 255 character long file name. */ + +#ifndef FX_MAX_LONG_NAME_LEN +#define FX_MAX_LONG_NAME_LEN 256 /* Minimum value is 13, Maximum value is 256. */ +#endif + +#define FX_MAX_SHORT_NAME_LEN 13 /* Only allowed value is 13. */ + +#ifndef FX_MAX_LAST_NAME_LEN +#define FX_MAX_LAST_NAME_LEN 256 /* Should be as large or larger than FX_MAX_LONG_NAME_LEN. */ +#endif + + +/* Define constants related to the logical sector cache. These constants represent the maximum + number of logical sectors that can be cached. The actual number of logical sectors in the + cache is determined by the amount of memory supplied to fx_media_open for the FX_MEDIA + instance. */ + +#ifndef FX_MAX_SECTOR_CACHE +#define FX_MAX_SECTOR_CACHE 256 /* Maximum size of logical sector cache, + minimum value of 2 all other values must + be a power of 2. */ +#endif + +/* Define the mask for the hash index into the logical sector cache. If the logical sector cache + determined by the amount of memory supplied by the application to fx_media_open is larger than + FX_SECTOR_CACHE_HASH_ENABLE (usually 16) and can satisfy FX_MAX_SECTOR_CACHE sectors, the cache + is divided into 4 entry pieces (FX_SECTOR_CACHE_DEPTH) that are indexed by the formula: + + index = (cluster & media_ptr -> fx_media_sector_cache_hash_mask) * FX_SECTOR_CACHE_LEVELS + + The FX_SECTOR_CACHE_DEPTH define must not be changed unless the logical sector read/write + code is also changed. */ + +#define FX_SECTOR_CACHE_HASH_ENABLE 16 +#define FX_SECTOR_CACHE_DEPTH 4 + +#ifndef FX_FAT_MAP_SIZE +#define FX_FAT_MAP_SIZE 128 /* Minimum 1, maximum any. This represents how many 32-bit words used for the written FAT sector bit map. */ +#endif + +#ifndef FX_MAX_FAT_CACHE +#define FX_MAX_FAT_CACHE 16 /* Minimum value is 8, all values must be a power of 2. */ +#endif + + +/* Define the size of fault tolerant cache, which is used when freeing FAT chain. */ + +#ifndef FX_FAULT_TOLERANT_CACHE_SIZE +#define FX_FAULT_TOLERANT_CACHE_SIZE 1024 +#endif /* FX_FAULT_TOLERANT_CACHE_SIZE */ + + +/* Define the mask for the hash index into the FAT table. The FAT cache is divided into 4 entry pieces + that are indexed by the formula: + + index = (cluster & FX_FAT_CACHE_HASH_MASK) * FX_FAT_CACHE_LEVELS + + The FX_FAT_CACHE_DEPTH define must not be changed unless the FAT entry read/write code is also changed. */ + +#define FX_FAT_CACHE_DEPTH 4 +#define FX_FAT_CACHE_HASH_MASK ((FX_MAX_FAT_CACHE / FX_FAT_CACHE_DEPTH) - 1) + + +/* FileX API input parameters and general constants. */ + +#define FX_TRUE 1 +#define FX_FALSE 0 +#define FX_NULL 0 +#define FX_OPEN_FOR_READ 0 +#define FX_OPEN_FOR_WRITE 1 +#define FX_OPEN_FOR_READ_FAST 2 + +#define FX_12_BIT_FAT_SIZE 4086 +#define FX_16_BIT_FAT_SIZE 65525 +#define FX_MAX_12BIT_CLUST 0x0FF0 +#define FX_SIGN_EXTEND 0xF000 +#define FX_12BIT_SIZE 3 /* 2 FAT entries per 3 bytes */ + +#define FX_FAT_ENTRY_START 2 +#define FX_DIR_ENTRY_SIZE 32 +#define FX_DIR_NAME_SIZE 8 +#define FX_DIR_EXT_SIZE 3 +#define FX_DIR_RESERVED 8 +#define FX_DIR_ENTRY_FREE 0xE5 +#define FX_DIR_ENTRY_DONE 0x00 +#define FX_READ_ONLY 0x01 +#define FX_HIDDEN 0x02 +#define FX_SYSTEM 0x04 +#define FX_VOLUME 0x08 +#define FX_DIRECTORY 0x10 +#define FX_ARCHIVE 0x20 +#define FX_LONG_NAME (FX_READ_ONLY | FX_HIDDEN | FX_SYSTEM | FX_VOLUME) +#define FX_LONG_NAME_ENTRY_LEN 13 + + +/* Define FAT FAT entry values. */ + +#define FX_FREE_CLUSTER 0x0000 +#define FX_NOT_USED 0x0001 +#define FX_RESERVED_1 0xFFF0 +#define FX_RESERVED_2 0xFFF6 +#define FX_BAD_CLUSTER 0xFFF7 +#define FX_LAST_CLUSTER_1 0xFFF8 +#define FX_LAST_CLUSTER_2 0xFFFF + +#define FX_RESERVED_1_32 0x0FFFFFF0 +#define FX_RESERVED_2_32 0x0FFFFFF6 +#define FX_BAD_CLUSTER_32 0x0FFFFFF7 +#define FX_LAST_CLUSTER_1_32 0x0FFFFFF8 +#define FX_LAST_CLUSTER_2_32 0x0FFFFFFF + + +/* Define time/date FAT constants. */ + +#define FX_YEAR_SHIFT 9 +#define FX_MONTH_SHIFT 5 +#define FX_HOUR_SHIFT 11 +#define FX_MINUTE_SHIFT 5 +#define FX_YEAR_MASK 0x7F +#define FX_MONTH_MASK 0x0F +#define FX_DAY_MASK 0x1F +#define FX_HOUR_MASK 0x1F +#define FX_MINUTE_MASK 0x3F +#define FX_SECOND_MASK 0x1F +#define FX_BASE_YEAR 1980 +#define FX_MAXIMUM_YEAR 2107 +#define FX_MAXIMUM_MONTH 12 +#define FX_MAXIMUM_HOUR 23 +#define FX_MAXIMUM_MINUTE 59 +#define FX_MAXIMUM_SECOND 59 +#define FX_INITIAL_DATE 0x4A21 /* 01-01-2017 */ +#define FX_INITIAL_TIME 0x0000 /* 12:00 am */ + +/* FileX API return values. */ + +#define FX_SUCCESS 0x00 +#define FX_BOOT_ERROR 0x01 +#define FX_MEDIA_INVALID 0x02 +#define FX_FAT_READ_ERROR 0x03 +#define FX_NOT_FOUND 0x04 +#define FX_NOT_A_FILE 0x05 +#define FX_ACCESS_ERROR 0x06 +#define FX_NOT_OPEN 0x07 +#define FX_FILE_CORRUPT 0x08 +#define FX_END_OF_FILE 0x09 +#define FX_NO_MORE_SPACE 0x0A +#define FX_ALREADY_CREATED 0x0B +#define FX_INVALID_NAME 0x0C +#define FX_INVALID_PATH 0x0D +#define FX_NOT_DIRECTORY 0x0E +#define FX_NO_MORE_ENTRIES 0x0F +#define FX_DIR_NOT_EMPTY 0x10 +#define FX_MEDIA_NOT_OPEN 0x11 +#define FX_INVALID_YEAR 0x12 +#define FX_INVALID_MONTH 0x13 +#define FX_INVALID_DAY 0x14 +#define FX_INVALID_HOUR 0x15 +#define FX_INVALID_MINUTE 0x16 +#define FX_INVALID_SECOND 0x17 +#define FX_PTR_ERROR 0x18 +#define FX_INVALID_ATTR 0x19 +#define FX_CALLER_ERROR 0x20 +#define FX_BUFFER_ERROR 0x21 +#define FX_NOT_IMPLEMENTED 0x22 +#define FX_WRITE_PROTECT 0x23 +#define FX_INVALID_OPTION 0x24 +#define FX_SECTOR_INVALID 0x89 +#define FX_IO_ERROR 0x90 +#define FX_NOT_ENOUGH_MEMORY 0x91 +#define FX_ERROR_FIXED 0x92 +#define FX_ERROR_NOT_FIXED 0x93 +#define FX_NOT_AVAILABLE 0x94 +#define FX_INVALID_CHECKSUM 0x95 +#define FX_READ_CONTINUE 0x96 +#define FX_INVALID_STATE 0x97 + + +/* FileX driver interface constants. */ + +#define FX_DRIVER_READ 0 +#define FX_DRIVER_WRITE 1 +#define FX_DRIVER_FLUSH 2 +#define FX_DRIVER_ABORT 3 +#define FX_DRIVER_INIT 4 +#define FX_DRIVER_BOOT_READ 5 +#define FX_DRIVER_RELEASE_SECTORS 6 +#define FX_DRIVER_BOOT_WRITE 7 +#define FX_DRIVER_UNINIT 8 + + +/* Define relative seek constants. */ + +#define FX_SEEK_BEGIN 0 +#define FX_SEEK_END 1 +#define FX_SEEK_FORWARD 2 +#define FX_SEEK_BACK 3 + + +/* Define types for logical sectors. This information is passed to the driver + as additional information. */ + +#define FX_UNKNOWN_SECTOR 0 +#define FX_BOOT_SECTOR 1 +#define FX_FAT_SECTOR 2 +#define FX_DIRECTORY_SECTOR 3 +#define FX_DATA_SECTOR 4 + + +/* Define media diagnostic constants. */ + +#define FX_FAT_CHAIN_ERROR 0x01 +#define FX_DIRECTORY_ERROR 0x02 +#define FX_LOST_CLUSTER_ERROR 0x04 +#define FX_FILE_SIZE_ERROR 0x08 + + +/* Define boot record offset constants. */ + +#define FX_JUMP_INSTR 0x000 +#define FX_OEM_NAME 0x003 +#define FX_BYTES_SECTOR 0x00B +#define FX_SECTORS_CLUSTER 0x00D +#define FX_RESERVED_SECTORS 0x00E +#define FX_NUMBER_OF_FATS 0x010 +#define FX_ROOT_DIR_ENTRIES 0x011 +#define FX_SECTORS 0x013 +#define FX_MEDIA_TYPE 0x015 +#define FX_SECTORS_PER_FAT 0x016 +#define FX_SECTORS_PER_TRK 0x018 +#define FX_HEADS 0x01A +#define FX_HIDDEN_SECTORS 0x01C +#define FX_HUGE_SECTORS 0x020 +#define FX_DRIVE_NUMBER 0x024 +#define FX_RESERVED 0x025 +#define FX_BOOT_SIG 0x026 +#define FX_BOOT_SIG_32 0x042 +#define FX_VOLUME_ID 0x027 +#define FX_VOLUME_ID_32 0x043 +#define FX_VOLUME_LABEL 0x02B +#define FX_VOLUME_LABEL_32 0x047 +#define FX_FILE_SYSTEM_TYPE 0x036 +#define FX_SIG_OFFSET 0x1FE +#define FX_ROOT_CLUSTER_32 0x02C +#define FX_SECTORS_PER_FAT_32 0x024 +#define FX_SIG_BYTE_1 0x55 +#define FX_SIG_BYTE_2 0xAA + + +/* If not-defined, default port-specific processing extensions to white space. */ + +#ifndef FX_FILE_OPEN_EXTENSION +#define FX_FILE_OPEN_EXTENSION +#endif + +#ifndef FX_DIRECTORY_ENTRY_READ_EXTENSION +#define FX_DIRECTORY_ENTRY_READ_EXTENSION +#endif + +#ifndef FX_DIRECTORY_ENTRY_WRITE_EXTENSION +#define FX_DIRECTORY_ENTRY_WRITE_EXTENSION +#endif + +#ifndef FX_UTILITY_FAT_ENTRY_READ_EXTENSION +#define FX_UTILITY_FAT_ENTRY_READ_EXTENSION +#endif + +#ifndef FX_UTILITY_FAT_ENTRY_WRITE_EXTENSION +#define FX_UTILITY_FAT_ENTRY_WRITE_EXTENSION +#endif + +#ifndef FX_UTILITY_LOGICAL_SECTOR_FLUSH_EXTENSION +#define FX_UTILITY_LOGICAL_SECTOR_FLUSH_EXTENSION +#endif + +#ifndef FX_UTILITY_LOGICAL_SECTOR_READ_EXTENSION +#define FX_UTILITY_LOGICAL_SECTOR_READ_EXTENSION +#endif + +#ifndef FX_UTILITY_LOGICAL_SECTOR_READ_EXTENSION_1 +#define FX_UTILITY_LOGICAL_SECTOR_READ_EXTENSION_1 +#endif + +#ifndef FX_UTILITY_LOGICAL_SECTOR_WRITE_EXTENSION +#define FX_UTILITY_LOGICAL_SECTOR_WRITE_EXTENSION +#endif + + + + +/* Define the control block definitions for all system objects. */ + + +/* Define a single entry in the FAT cache. The FAT cache is used to reduce the + number of times the actual FAT sectors need to be accessed, thereby improving + performance. */ + +typedef struct FX_FAT_CACHE_ENTRY_STRUCT +{ + ULONG fx_fat_cache_entry_cluster; + ULONG fx_fat_cache_entry_value; + ULONG fx_fat_cache_entry_dirty; +} FX_FAT_CACHE_ENTRY; + + +/* Define the directory entry structure that contains information about a specific + directory entry. */ + +typedef struct FX_DIR_ENTRY_STRUCT +{ + + CHAR *fx_dir_entry_name; + CHAR fx_dir_entry_short_name[FX_MAX_SHORT_NAME_LEN]; /* Short file name, if LFN is present */ + UINT fx_dir_entry_long_name_present; /* 0 (default) => LFN not present; 1 => LFN present */ + UINT fx_dir_entry_long_name_shorted; /* LFN too large, file name was made shorter */ + UCHAR fx_dir_entry_attributes; /* Directory entry attributes */ + UCHAR fx_dir_entry_reserved; /* NT reserved, write 0 at create, otherwise ignore */ + UCHAR fx_dir_entry_created_time_ms; /* Create time in milliseconds, always write 0 */ + UINT fx_dir_entry_created_time; /* Created time */ + UINT fx_dir_entry_created_date; /* Created date */ + UINT fx_dir_entry_last_accessed_date; /* Last accessed date */ + UINT fx_dir_entry_time; /* Modified time */ + UINT fx_dir_entry_date; /* Modified cluster */ + ULONG fx_dir_entry_cluster; /* File/directory's starting cluster */ + ULONG64 fx_dir_entry_file_size; /* Size of the file in bytes */ + ULONG64 fx_dir_entry_log_sector; /* Logical sector of this directory entry */ + ULONG fx_dir_entry_byte_offset; /* Offset in logical sector of this directory entry */ + ULONG fx_dir_entry_number; /* Index into the directory */ + ULONG fx_dir_entry_last_search_cluster; /* Last cluster searched */ + ULONG fx_dir_entry_last_search_relative_cluster; /* Last relative cluster searched */ + ULONG64 fx_dir_entry_last_search_log_sector; /* Last logical sector searched */ + ULONG fx_dir_entry_last_search_byte_offset; /* Last offset in logical sector searched */ + ULONG64 fx_dir_entry_next_log_sector; + +} FX_DIR_ENTRY; + +typedef FX_DIR_ENTRY *FX_DIR_ENTRY_PTR; + + +/* Define the path data structure. This structure will contain + the current path and the information for performing directory + entry operations. */ + +typedef struct FX_PATH_STRUCT +{ + /* Define the path information. */ + FX_DIR_ENTRY fx_path_directory; + CHAR fx_path_string[FX_MAXIMUM_PATH]; + CHAR fx_path_name_buffer[FX_MAX_LONG_NAME_LEN]; + ULONG fx_path_current_entry; +} FX_PATH; + +typedef FX_PATH FX_LOCAL_PATH; + + +/* Define the cache control data structure. There are FX_MAX_SECTOR_CACHE + of these structures defined inside the FX_MEDIA structure. Each entry + maintains a cache representation of a media sector. */ + +typedef struct FX_CACHED_SECTOR_STRUCT +{ + + /* Define the buffer pointer associated with this cache entry. */ + UCHAR *fx_cached_sector_memory_buffer; + + /* Define the sector number that is cached. */ + ULONG64 fx_cached_sector; + + /* Define the flag that indicates whether or not the cached sector + has been modified and needs to be written to the media. */ + UCHAR fx_cached_sector_buffer_dirty; + + /* Define the valid flag that indicates whether or not this entry is + still valid. */ + UCHAR fx_cached_sector_valid; + + /* Define the sector type, which indicates what type of sector is present. */ + UCHAR fx_cached_sector_type; + + /* Define a reserved byte, reserved for future use. */ + UCHAR fx_cached_sector_reserved; + + /* Define the next cached sector pointer. This is used to implement + the "last used" algorithm when looking for cache entry to swap out to + the physical media. */ + struct FX_CACHED_SECTOR_STRUCT + *fx_cached_sector_next_used; + +} FX_CACHED_SECTOR; + + +/* Determine if the media control block has an extension defined. If not, + define the extension to whitespace. */ + +#ifndef FX_MEDIA_MODULE_EXTENSION +#define FX_MEDIA_MODULE_EXTENSION +#endif + + +/* Define the media control block. All information about each open + media device are maintained in by the FX_MEDIA data type. */ + +typedef struct FX_MEDIA_STRUCT +{ + + /* Define the media ID used for error checking. */ + ULONG fx_media_id; + + /* Define the media's name. */ + CHAR *fx_media_name; + + /* Remember the memory buffer area. */ + UCHAR *fx_media_memory_buffer; + ULONG fx_media_memory_size; + +#ifdef FX_DISABLE_CACHE + ULONG64 fx_media_memory_buffer_sector; +#else + + /* Define the flag that indicates whether the logical cache utilizes + a hash function or is a linear search. If set, the logical cache + is accessed via a hash function on the requested sector. */ + UINT fx_media_sector_cache_hashed; + + /* Define the number of sectors that can actually be cached based on the + user supplied buffer at media open time. */ + ULONG fx_media_sector_cache_size; + + /* Define the end of the cache area. This is used to determine + if the I/O is for the internal memory of the media. */ + UCHAR *fx_media_sector_cache_end; + + /* Define the list head of the cached sector entries. This + pointer points to the most recently used cache sector. */ + struct FX_CACHED_SECTOR_STRUCT + *fx_media_sector_cache_list_ptr; + + /* Define the bit map that represents the hashed cache sectors that are + valid. This bit map will help optimize the invalidation of the hashed + sector cache. */ + ULONG fx_media_sector_cache_hashed_sector_valid; + + /* Define the outstanding dirty sector counter. This is used to optimize + the searching of sectors to flush to the media. */ + ULONG fx_media_sector_cache_dirty_count; +#endif /* FX_DISABLE_CACHE */ + + /* Define the basic information about the associated media. */ + UINT fx_media_bytes_per_sector; + UINT fx_media_sectors_per_track; + UINT fx_media_heads; + + ULONG64 fx_media_total_sectors; + ULONG fx_media_total_clusters; + + + UINT fx_media_reserved_sectors; + UINT fx_media_root_sector_start; + UINT fx_media_root_sectors; + UINT fx_media_data_sector_start; + UINT fx_media_sectors_per_cluster; + UINT fx_media_sectors_per_FAT; + UINT fx_media_number_of_FATs; + UINT fx_media_12_bit_FAT; + UINT fx_media_32_bit_FAT; + ULONG fx_media_FAT32_additional_info_sector; + UINT fx_media_FAT32_additional_info_last_available; +#ifdef FX_DRIVER_USE_64BIT_LBA + ULONG64 fx_media_hidden_sectors; +#else + ULONG fx_media_hidden_sectors; +#endif + ULONG fx_media_root_cluster_32; + UINT fx_media_root_directory_entries; + ULONG fx_media_available_clusters; + ULONG fx_media_cluster_search_start; + + /* Define the information pertinent to the I/O driver interface. */ + + VOID *fx_media_driver_info; + UINT fx_media_driver_request; + UINT fx_media_driver_status; + UCHAR *fx_media_driver_buffer; +#ifdef FX_DRIVER_USE_64BIT_LBA + ULONG64 fx_media_driver_logical_sector; +#else + ULONG fx_media_driver_logical_sector; +#endif + ULONG fx_media_driver_sectors; + ULONG fx_media_driver_physical_sector; + UINT fx_media_driver_physical_track; + UINT fx_media_driver_physical_head; + UINT fx_media_driver_write_protect; /* The driver sets this to FX_TRUE when media is write protected. */ + UINT fx_media_driver_free_sector_update; /* The driver sets this to FX_TRUE when it needs to know freed clusters. */ + UINT fx_media_driver_system_write; + UINT fx_media_driver_data_sector_read; + UINT fx_media_driver_sector_type; + + /* Define the driver entry point. */ + VOID (*fx_media_driver_entry)(struct FX_MEDIA_STRUCT *); + + /* Define notify function called when media is open. */ + VOID (*fx_media_open_notify)(struct FX_MEDIA_STRUCT *); + + /* Define notify function called when media is closed. */ + VOID (*fx_media_close_notify)(struct FX_MEDIA_STRUCT *); + + /* Define the head pointer for the open files of this media. */ + struct FX_FILE_STRUCT + *fx_media_opened_file_list; + + /* Define the counter for keeping track of how many open files are + present. */ + ULONG fx_media_opened_file_count; + + /* Define the next and previous link pointers for the open media list. */ + struct FX_MEDIA_STRUCT + *fx_media_opened_next, + *fx_media_opened_previous; + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Define various statistics for this media instance. This information + should be useful in performance tuning and optimizing the application. */ + + ULONG fx_media_directory_attributes_reads; + ULONG fx_media_directory_attributes_sets; + ULONG fx_media_directory_creates; + ULONG fx_media_directory_default_gets; + ULONG fx_media_directory_default_sets; + ULONG fx_media_directory_deletes; + ULONG fx_media_directory_first_entry_finds; + ULONG fx_media_directory_first_full_entry_finds; + ULONG fx_media_directory_information_gets; + ULONG fx_media_directory_local_path_clears; + ULONG fx_media_directory_local_path_gets; + ULONG fx_media_directory_local_path_restores; + ULONG fx_media_directory_local_path_sets; + ULONG fx_media_directory_name_tests; + ULONG fx_media_directory_next_entry_finds; + ULONG fx_media_directory_next_full_entry_finds; + ULONG fx_media_directory_renames; + ULONG fx_media_file_allocates; + ULONG fx_media_file_attributes_reads; + ULONG fx_media_file_attributes_sets; + ULONG fx_media_file_best_effort_allocates; + ULONG fx_media_file_closes; + ULONG fx_media_file_creates; + ULONG fx_media_file_deletes; + ULONG fx_media_file_opens; + ULONG fx_media_file_reads; + ULONG fx_media_file_relative_seeks; + ULONG fx_media_file_renames; + ULONG fx_media_file_seeks; + ULONG fx_media_file_truncates; + ULONG fx_media_file_truncate_releases; + ULONG fx_media_file_writes; + ULONG fx_media_aborts; + ULONG fx_media_flushes; + ULONG fx_media_reads; + ULONG fx_media_writes; + ULONG fx_media_directory_entry_reads; + ULONG fx_media_directory_entry_writes; + ULONG fx_media_directory_searches; + ULONG fx_media_directory_free_searches; + ULONG fx_media_fat_entry_reads; + ULONG fx_media_fat_entry_writes; + ULONG fx_media_fat_entry_cache_read_hits; + ULONG fx_media_fat_entry_cache_read_misses; + ULONG fx_media_fat_entry_cache_write_hits; + ULONG fx_media_fat_entry_cache_write_misses; + ULONG fx_media_fat_cache_flushes; + ULONG fx_media_fat_sector_reads; + ULONG fx_media_fat_sector_writes; + ULONG fx_media_logical_sector_reads; + ULONG fx_media_logical_sector_writes; + ULONG fx_media_logical_sector_cache_read_hits; + ULONG fx_media_logical_sector_cache_read_misses; + ULONG fx_media_driver_read_requests; + ULONG fx_media_driver_write_requests; + ULONG fx_media_driver_boot_read_requests; + ULONG fx_media_driver_boot_write_requests; + ULONG fx_media_driver_release_sectors_requests; + ULONG fx_media_driver_flush_requests; +#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE + ULONG fx_media_directory_search_cache_hits; +#endif +#endif + + /* Define the media's protection object, which is a ThreadX mutex. + Only one thread is allowed to access any media or associated files + at a time. If FX_SINGLE_THREAD is defined, the FileX services are + going to be called from only one thread, hence the protection is + not needed. */ +#ifndef FX_SINGLE_THREAD + TX_MUTEX fx_media_protect; +#endif + +#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE + + /* Define the information used to remember the last directory entry found through + searching or walking the directory via directory entry next. This information + will be used to eliminate multiple searches for the same directory entry if + the accesses are done sequentially. */ + UINT fx_media_last_found_directory_valid; + FX_DIR_ENTRY fx_media_last_found_directory; + FX_DIR_ENTRY fx_media_last_found_entry; + CHAR fx_media_last_found_file_name[FX_MAX_LONG_NAME_LEN]; + CHAR fx_media_last_found_name[FX_MAX_LAST_NAME_LEN]; +#endif + + /* Define the current directory information for the media. */ + FX_PATH fx_media_default_path; + + /* Define FAT entry cache and the variable used to index the cache. */ + FX_FAT_CACHE_ENTRY fx_media_fat_cache[FX_MAX_FAT_CACHE]; + + /* Define the FAT secondary update map. This will be used on flush and + close to update sectors of any secondary FATs in the media. */ + UCHAR fx_media_fat_secondary_update_map[FX_FAT_MAP_SIZE]; + + /* Define a variable for the application's use. */ + ALIGN_TYPE fx_media_reserved_for_user; + + /* Define an area to allocate long file names so that local storage on + calling thread's stack is not used for long file names. This helps + reduce the amount of thread stack space needed when using FileX. */ + CHAR fx_media_name_buffer[4*FX_MAX_LONG_NAME_LEN]; + +#ifdef FX_RENAME_PATH_INHERIT + + /* Define the file and directory rename buffer that will be used to prepend + paths when necessary to the target file name. */ + CHAR fx_media_rename_buffer[FX_MAXIMUM_PATH]; +#endif + +#ifndef FX_DISABLE_CACHE + /* Define the sector cache control structures for this media. */ + struct FX_CACHED_SECTOR_STRUCT + fx_media_sector_cache[FX_MAX_SECTOR_CACHE]; + + /* Define the sector cache hash mask so that the hash algorithm can be used with + any power of 2 number of cache sectors. */ + ULONG fx_media_sector_cache_hash_mask; +#endif /* FX_DISABLE_CACHE */ + + /* Define a variable to disable burst cache. This is used by the underlying + driver. */ + ULONG fx_media_disable_burst_cache; + +#ifdef FX_ENABLE_FAULT_TOLERANT + + /* Fault tolerant information */ + /* Indicate whether fault tolerant is enabled. */ + + UCHAR fx_media_fault_tolerant_enabled; + + /* State of fault tolerant operation. */ + UCHAR fx_media_fault_tolerant_state; + + /* Transaction recursive count. */ + USHORT fx_media_fault_tolerant_transaction_count; + + /* Start cluster of log. */ + ULONG fx_media_fault_tolerant_start_cluster; + + /* Count of consecutive clusters of log. */ + ULONG fx_media_fault_tolerant_clusters; + + /* Count of total logs. */ + ULONG fx_media_fault_tolerant_total_logs; + + /* Pointer to the memory buffer area used for fault tolerant operations. */ + UCHAR *fx_media_fault_tolerant_memory_buffer; + + /* Size of memory buffer area used for fault tolerant operations. */ + ULONG fx_media_fault_tolerant_memory_buffer_size; + + /* Size of log file. */ + ULONG fx_media_fault_tolerant_file_size; + + /* Memory space used during the release of FAT list. */ + ULONG fx_media_fault_tolerant_cache[FX_FAULT_TOLERANT_CACHE_SIZE >> 2]; + + /* Sector number of cached FAT entries. */ + ULONG fx_media_fault_tolerant_cached_FAT_sector; +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Reserved value of FAT table. */ + ULONG fx_media_fat_reserved; + + /* Last value of FAT table. */ + ULONG fx_media_fat_last; + + /* Media geometry structure */ + UCHAR fx_media_FAT_type; + + /* Define the module port extension in the media control block. This + is typically defined to whitespace in fx_port.h. */ + FX_MEDIA_MODULE_EXTENSION + +} FX_MEDIA; + +typedef FX_MEDIA * FX_MEDIA_PTR; + + +/* Determine if the file control block has an extension defined. If not, + define the extension to whitespace. */ + +#ifndef FX_FILE_MODULE_EXTENSION +#define FX_FILE_MODULE_EXTENSION +#endif + + +/* Define the FileX file control block. All information about open + files are found in this data type. */ + +typedef struct FX_FILE_STRUCT +{ + + /* Define the file ID used for error checking. */ + ULONG fx_file_id; + + /* Define the file's name. */ + CHAR *fx_file_name; + + /* Define the open mode request. */ + ULONG fx_file_open_mode; + + /* Define the file modified field. */ + UCHAR fx_file_modified; + + /* Define the data storage parameters. */ + ULONG fx_file_total_clusters; + ULONG fx_file_first_physical_cluster; + ULONG fx_file_consecutive_cluster; + ULONG fx_file_last_physical_cluster; + ULONG fx_file_current_physical_cluster; + ULONG64 fx_file_current_logical_sector; + ULONG fx_file_current_logical_offset; + ULONG fx_file_current_relative_cluster; + ULONG fx_file_current_relative_sector; + ULONG64 fx_file_current_file_offset; + ULONG64 fx_file_current_file_size; + ULONG64 fx_file_current_available_size; +#ifdef FX_ENABLE_FAULT_TOLERANT + ULONG64 fx_file_maximum_size_used; +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Remember the media that is associated with this file. */ + FX_MEDIA *fx_file_media_ptr; + + /* Define the pointers necessary to maintain the open file on + the list of open files. */ + struct FX_FILE_STRUCT + *fx_file_opened_next, + *fx_file_opened_previous; + + /* Define the complete directory entry structure. */ + FX_DIR_ENTRY fx_file_dir_entry; + CHAR fx_file_name_buffer[FX_MAX_LONG_NAME_LEN]; + + /* Define a variable for the application's use */ + ULONG fx_file_disable_burst_cache; + + /* Define a notify function called when file is written to. */ + VOID (*fx_file_write_notify)(struct FX_FILE_STRUCT *); + + /* Define the module port extension in the file control block. This + is typically defined to whitespace in fx_port.h. */ + FX_FILE_MODULE_EXTENSION + +} FX_FILE; + +typedef FX_FILE *FX_FILE_PTR; + + +/* Define the FileX API mappings based on the error checking + selected by the user. Note: this section is only applicable to + application source code, hence the conditional that turns off this + stuff when the include file is processed by the FileX source. */ + +#ifndef FX_SOURCE_CODE + +#ifdef FX_DISABLE_ONE_LINE_FUNCTION +#define fx_file_seek(f, b) fx_file_extended_seek(f, (ULONG64)b) +#define fx_file_allocate(f, s) fx_file_extended_allocate(f, (ULONG64)s); +#define fx_file_truncate(f, s) fx_file_extended_truncate(f, (ULONG64)s); +#define fx_file_relative_seek(f, b, sf) fx_file_extended_relative_seek(f, (ULONG64)b, sf); +#define fx_file_truncate_release(f, s) fx_file_extended_truncate_release(f, (ULONG64)s); +#endif /* FX_DISABLE_ONE_LINE_FUNCTION */ + +/* Determine if error checking is desired. If so, map API functions + to the appropriate error checking front-ends. Otherwise, map API + functions to the core functions that actually perform the work. + Note: error checking is enabled by default. */ + +#ifdef FX_DISABLE_ERROR_CHECKING + +/* Services without error checking. */ + +#define fx_directory_attributes_read _fx_directory_attributes_read +#define fx_directory_attributes_set _fx_directory_attributes_set +#define fx_directory_create _fx_directory_create +#define fx_directory_default_get _fx_directory_default_get +#define fx_directory_default_get_copy _fx_directory_default_get_copy +#define fx_directory_default_set _fx_directory_default_set +#define fx_directory_delete _fx_directory_delete +#define fx_directory_first_entry_find _fx_directory_first_entry_find +#define fx_directory_first_full_entry_find _fx_directory_first_full_entry_find +#define fx_directory_information_get _fx_directory_information_get +#define fx_directory_local_path_clear _fx_directory_local_path_clear +#define fx_directory_local_path_get _fx_directory_local_path_get +#define fx_directory_local_path_get_copy _fx_directory_local_path_get_copy +#define fx_directory_local_path_restore _fx_directory_local_path_restore +#define fx_directory_local_path_set _fx_directory_local_path_set +#define fx_directory_long_name_get _fx_directory_long_name_get +#define fx_directory_long_name_get_extended _fx_directory_long_name_get_extended +#define fx_directory_name_test _fx_directory_name_test +#define fx_directory_next_entry_find _fx_directory_next_entry_find +#define fx_directory_next_full_entry_find _fx_directory_next_full_entry_find +#define fx_directory_rename _fx_directory_rename +#define fx_directory_short_name_get _fx_directory_short_name_get +#define fx_directory_short_name_get_extended _fx_directory_short_name_get_extended + +#ifndef FX_DISABLE_ONE_LINE_FUNCTION +#define fx_file_allocate _fx_file_allocate +#endif /* FX_DISABLE_ONE_LINE_FUNCTION */ +#define fx_file_attributes_read _fx_file_attributes_read +#define fx_file_attributes_set _fx_file_attributes_set +#define fx_file_best_effort_allocate _fx_file_best_effort_allocate +#define fx_file_close _fx_file_close +#define fx_file_create _fx_file_create +#define fx_file_date_time_set _fx_file_date_time_set +#define fx_file_delete _fx_file_delete +#define fx_file_open _fx_file_open +#define fx_file_read _fx_file_read +#ifndef FX_DISABLE_ONE_LINE_FUNCTION +#define fx_file_relative_seek _fx_file_relative_seek +#endif /* FX_DISABLE_ONE_LINE_FUNCTION */ +#define fx_file_rename _fx_file_rename +#ifndef FX_DISABLE_ONE_LINE_FUNCTION +#define fx_file_seek _fx_file_seek +#define fx_file_truncate _fx_file_truncate +#define fx_file_truncate_release _fx_file_truncate_release +#endif /* FX_DISABLE_ONE_LINE_FUNCTION */ +#define fx_file_write _fx_file_write +#define fx_file_write_notify_set _fx_file_write_notify_set +#define fx_file_extended_allocate _fx_file_extended_allocate +#define fx_file_extended_best_effort_allocate _fx_file_extended_best_effort_allocate +#define fx_file_extended_relative_seek _fx_file_extended_relative_seek +#define fx_file_extended_seek _fx_file_extended_seek +#define fx_file_extended_truncate _fx_file_extended_truncate +#define fx_file_extended_truncate_release _fx_file_extended_truncate_release + +#define fx_media_abort _fx_media_abort +#define fx_media_cache_invalidate _fx_media_cache_invalidate +#define fx_media_check _fx_media_check +#define fx_media_close _fx_media_close +#define fx_media_flush _fx_media_flush +#define fx_media_format _fx_media_format +#define fx_media_open _fx_media_open +#define fx_media_read _fx_media_read +#define fx_media_space_available _fx_media_space_available +#define fx_media_volume_get _fx_media_volume_get +#define fx_media_volume_get_extended _fx_media_volume_get_extended +#define fx_media_volume_set _fx_media_volume_set +#define fx_media_write _fx_media_write +#define fx_media_open_notify_set _fx_media_open_notify_set +#define fx_media_close_notify_set _fx_media_close_notify_set +#define fx_media_extended_space_available _fx_media_extended_space_available + +#define fx_unicode_directory_create _fx_unicode_directory_create +#define fx_unicode_directory_rename _fx_unicode_directory_rename +#define fx_unicode_file_create _fx_unicode_file_create +#define fx_unicode_file_rename _fx_unicode_file_rename +#define fx_unicode_length_get _fx_unicode_length_get +#define fx_unicode_length_get_extended _fx_unicode_length_get_extended +#define fx_unicode_name_get _fx_unicode_name_get +#define fx_unicode_name_get_extended _fx_unicode_name_get_extended +#define fx_unicode_short_name_get _fx_unicode_short_name_get +#define fx_unicode_short_name_get_extended _fx_unicode_short_name_get_extended + +#define fx_system_date_get _fx_system_date_get +#define fx_system_date_set _fx_system_date_set +#define fx_system_time_get _fx_system_time_get +#define fx_system_time_set _fx_system_time_set +#define fx_system_initialize _fx_system_initialize + +#ifdef FX_ENABLE_FAULT_TOLERANT +#define fx_fault_tolerant_enable _fx_fault_tolerant_enable +#endif /* FX_ENABLE_FAULT_TOLERANT */ + +#else + +/* Services with error checking. */ + +#define fx_directory_attributes_read _fxe_directory_attributes_read +#define fx_directory_attributes_set _fxe_directory_attributes_set +#define fx_directory_create _fxe_directory_create +#define fx_directory_default_get _fxe_directory_default_get +#define fx_directory_default_get_copy _fxe_directory_default_get_copy +#define fx_directory_default_set _fxe_directory_default_set +#define fx_directory_delete _fxe_directory_delete +#define fx_directory_first_entry_find _fxe_directory_first_entry_find +#define fx_directory_first_full_entry_find _fxe_directory_first_full_entry_find +#define fx_directory_information_get _fxe_directory_information_get +#define fx_directory_local_path_clear _fxe_directory_local_path_clear +#define fx_directory_local_path_get _fxe_directory_local_path_get +#define fx_directory_local_path_get_copy _fxe_directory_local_path_get_copy +#define fx_directory_local_path_restore _fxe_directory_local_path_restore +#define fx_directory_local_path_set(m, l, n) _fxe_directory_local_path_set(m, l, n, sizeof(FX_LOCAL_PATH)) +#define fx_directory_long_name_get _fxe_directory_long_name_get +#define fx_directory_long_name_get_extended _fxe_directory_long_name_get_extended +#define fx_directory_name_test _fxe_directory_name_test +#define fx_directory_next_entry_find _fxe_directory_next_entry_find +#define fx_directory_next_full_entry_find _fxe_directory_next_full_entry_find +#define fx_directory_rename _fxe_directory_rename +#define fx_directory_short_name_get _fxe_directory_short_name_get +#define fx_directory_short_name_get_extended _fxe_directory_short_name_get_extended + +#ifndef FX_DISABLE_ONE_LINE_FUNCTION +#define fx_file_allocate _fxe_file_allocate +#endif /* FX_DISABLE_ONE_LINE_FUNCTION */ +#define fx_file_attributes_read _fxe_file_attributes_read +#define fx_file_attributes_set _fxe_file_attributes_set +#define fx_file_best_effort_allocate _fxe_file_best_effort_allocate +#define fx_file_close _fxe_file_close +#define fx_file_create _fxe_file_create +#define fx_file_date_time_set _fxe_file_date_time_set +#define fx_file_delete _fxe_file_delete +#define fx_file_open(m, f, n, t) _fxe_file_open(m, f, n, t, sizeof(FX_FILE)) +#define fx_file_read _fxe_file_read +#ifndef FX_DISABLE_ONE_LINE_FUNCTION +#define fx_file_relative_seek _fxe_file_relative_seek +#endif /* FX_DISABLE_ONE_LINE_FUNCTION */ +#define fx_file_rename _fxe_file_rename +#ifndef FX_DISABLE_ONE_LINE_FUNCTION +#define fx_file_seek _fxe_file_seek +#define fx_file_truncate _fxe_file_truncate +#define fx_file_truncate_release _fxe_file_truncate_release +#endif /* FX_DISABLE_ONE_LINE_FUNCTION */ +#define fx_file_write _fxe_file_write +#define fx_file_write_notify_set _fxe_file_write_notify_set +#define fx_file_extended_allocate _fxe_file_extended_allocate +#define fx_file_extended_best_effort_allocate _fxe_file_extended_best_effort_allocate +#define fx_file_extended_relative_seek _fxe_file_extended_relative_seek +#define fx_file_extended_seek _fxe_file_extended_seek +#define fx_file_extended_truncate _fxe_file_extended_truncate +#define fx_file_extended_truncate_release _fxe_file_extended_truncate_release + +#define fx_media_abort _fxe_media_abort +#define fx_media_cache_invalidate _fxe_media_cache_invalidate +#define fx_media_check _fxe_media_check +#define fx_media_close _fxe_media_close +#define fx_media_flush _fxe_media_flush +#define fx_media_format _fxe_media_format +#define fx_media_open(m, n, d, i, p, s) _fxe_media_open(m, n, d, i, p, s, sizeof(FX_MEDIA)) +#define fx_media_read _fxe_media_read +#define fx_media_space_available _fxe_media_space_available +#define fx_media_volume_get _fxe_media_volume_get +#define fx_media_volume_get_extended _fxe_media_volume_get_extended +#define fx_media_volume_set _fxe_media_volume_set +#define fx_media_write _fxe_media_write +#define fx_media_open_notify_set _fxe_media_open_notify_set +#define fx_media_close_notify_set _fxe_media_close_notify_set +#define fx_media_extended_space_available _fxe_media_extended_space_available + +#define fx_unicode_directory_create _fxe_unicode_directory_create +#define fx_unicode_directory_rename _fxe_unicode_directory_rename +#define fx_unicode_file_create _fxe_unicode_file_create +#define fx_unicode_file_rename _fxe_unicode_file_rename +#define fx_unicode_length_get _fx_unicode_length_get +#define fx_unicode_length_get_extended _fx_unicode_length_get_extended +#define fx_unicode_name_get _fxe_unicode_name_get +#define fx_unicode_name_get_extended _fxe_unicode_name_get_extended +#define fx_unicode_short_name_get _fxe_unicode_short_name_get +#define fx_unicode_short_name_get_extended _fxe_unicode_short_name_get_extended + +#define fx_system_date_get _fxe_system_date_get +#define fx_system_date_set _fxe_system_date_set +#define fx_system_time_get _fxe_system_time_get +#define fx_system_time_set _fxe_system_time_set +#define fx_system_initialize _fx_system_initialize + +#ifdef FX_ENABLE_FAULT_TOLERANT +#define fx_fault_tolerant_enable _fxe_fault_tolerant_enable +#endif /* FX_ENABLE_FAULT_TOLERANT */ + +#endif + +/* Define the function prototypes of the FileX API. */ + +UINT fx_directory_attributes_read(FX_MEDIA *media_ptr, CHAR *directory_name, UINT *attributes_ptr); +UINT fx_directory_attributes_set(FX_MEDIA *media_ptr, CHAR *directory_name, UINT attributes); +UINT fx_directory_create(FX_MEDIA *media_ptr, CHAR *directory_name); +UINT fx_directory_default_get(FX_MEDIA *media_ptr, CHAR **return_path_name); +UINT fx_directory_default_get_copy(FX_MEDIA *media_ptr, CHAR *return_path_name_buffer, UINT return_path_name_buffer_size); +UINT fx_directory_default_set(FX_MEDIA *media_ptr, CHAR *new_path_name); +UINT fx_directory_delete(FX_MEDIA *media_ptr, CHAR *directory_name); +UINT fx_directory_first_entry_find(FX_MEDIA *media_ptr, CHAR *directory_name); +UINT fx_directory_first_full_entry_find(FX_MEDIA *media_ptr, CHAR *directory_name, UINT *attributes, + ULONG *size, UINT *year, UINT *month, UINT *day, UINT *hour, UINT *minute, UINT *second); +UINT fx_directory_information_get(FX_MEDIA *media_ptr, CHAR *directory_name, UINT *attributes, ULONG *size, + UINT *year, UINT *month, UINT *day, UINT *hour, UINT *minute, UINT *second); +UINT fx_directory_local_path_clear(FX_MEDIA *media_ptr); +UINT fx_directory_local_path_get(FX_MEDIA *media_ptr, CHAR **return_path_name); +UINT fx_directory_local_path_get_copy(FX_MEDIA *media_ptr, CHAR *return_path_name_buffer, UINT return_path_name_buffer_size); +UINT fx_directory_local_path_restore(FX_MEDIA *media_ptr, FX_LOCAL_PATH *local_path_ptr); +#ifdef FX_DISABLE_ERROR_CHECKING +UINT _fx_directory_local_path_set(FX_MEDIA *media_ptr, FX_LOCAL_PATH *local_path_ptr, CHAR *new_path_name); +#else +UINT _fxe_directory_local_path_set(FX_MEDIA *media_ptr, FX_LOCAL_PATH *local_path_ptr, CHAR *new_path_name, UINT local_path_control_block_size); +#endif +UINT fx_directory_long_name_get(FX_MEDIA *media_ptr, CHAR *short_file_name, CHAR *long_file_name); +UINT fx_directory_long_name_get_extended(FX_MEDIA* media_ptr, CHAR* short_file_name, CHAR* long_file_name, UINT long_file_name_buffer_length); +UINT fx_directory_name_test(FX_MEDIA *media_ptr, CHAR *directory_name); +UINT fx_directory_next_entry_find(FX_MEDIA *media_ptr, CHAR *directory_name); +UINT fx_directory_next_full_entry_find(FX_MEDIA *media_ptr, CHAR *directory_name, UINT *attributes, + ULONG *size, UINT *year, UINT *month, UINT *day, UINT *hour, UINT *minute, UINT *second); +UINT fx_directory_rename(FX_MEDIA *media_ptr, CHAR *old_directory_name, CHAR *new_directory_name); +UINT fx_directory_short_name_get(FX_MEDIA *media_ptr, CHAR *long_file_name, CHAR *short_file_name); +UINT fx_directory_short_name_get_extended(FX_MEDIA* media_ptr, CHAR* long_file_name, CHAR* short_file_name, UINT short_file_name_length); + +#ifndef FX_DISABLE_ONE_LINE_FUNCTION +UINT fx_file_allocate(FX_FILE *file_ptr, ULONG size); +#endif /* FX_DISABLE_ONE_LINE_FUNCTION*/ +UINT fx_file_attributes_read(FX_MEDIA *media_ptr, CHAR *file_name, UINT *attributes_ptr); +UINT fx_file_attributes_set(FX_MEDIA *media_ptr, CHAR *file_name, UINT attributes); +UINT fx_file_best_effort_allocate(FX_FILE *file_ptr, ULONG size, ULONG *actual_size_allocated); +UINT fx_file_close(FX_FILE *file_ptr); +UINT fx_file_create(FX_MEDIA *media_ptr, CHAR *file_name); +UINT fx_file_date_time_set(FX_MEDIA *media_ptr, CHAR *file_name, + UINT year, UINT month, UINT day, UINT hour, UINT minute, UINT second); +UINT fx_file_delete(FX_MEDIA *media_ptr, CHAR *file_name); +#ifdef FX_DISABLE_ERROR_CHECKING +UINT _fx_file_open(FX_MEDIA *media_ptr, FX_FILE *file_ptr, CHAR *file_name, + UINT open_type); +#else +UINT _fxe_file_open(FX_MEDIA *media_ptr, FX_FILE *file_ptr, CHAR *file_name, + UINT open_type, UINT file_control_block_size); +#endif +UINT fx_file_read(FX_FILE *file_ptr, VOID *buffer_ptr, ULONG request_size, ULONG *actual_size); +#ifndef FX_DISABLE_ONE_LINE_FUNCTION +UINT fx_file_relative_seek(FX_FILE *file_ptr, ULONG byte_offset, UINT seek_from); +#endif /* FX_DISABLE_ONE_LINE_FUNCTION */ +UINT fx_file_rename(FX_MEDIA *media_ptr, CHAR *old_file_name, CHAR *new_file_name); +#ifndef FX_DISABLE_ONE_LINE_FUNCTION +UINT fx_file_seek(FX_FILE *file_ptr, ULONG byte_offset); +UINT fx_file_truncate(FX_FILE *file_ptr, ULONG size); +UINT fx_file_truncate_release(FX_FILE *file_ptr, ULONG size); +#endif /* FX_DISABLE_ONE_LINE_FUNCTION */ +UINT fx_file_write(FX_FILE *file_ptr, VOID *buffer_ptr, ULONG size); +UINT fx_file_write_notify_set(FX_FILE *file_ptr, VOID (*file_write_notify)(FX_FILE *)); +UINT fx_file_extended_allocate(FX_FILE *file_ptr, ULONG64 size); +UINT fx_file_extended_best_effort_allocate(FX_FILE *file_ptr, ULONG64 size, ULONG64 *actual_size_allocated); +UINT fx_file_extended_relative_seek(FX_FILE *file_ptr, ULONG64 byte_offset, UINT seek_from); +UINT fx_file_extended_seek(FX_FILE *file_ptr, ULONG64 byte_offset); +UINT fx_file_extended_truncate(FX_FILE *file_ptr, ULONG64 size); +UINT fx_file_extended_truncate_release(FX_FILE *file_ptr, ULONG64 size); + +UINT fx_media_abort(FX_MEDIA *media_ptr); +UINT fx_media_cache_invalidate(FX_MEDIA *media_ptr); +UINT fx_media_check(FX_MEDIA *media_ptr, UCHAR *scratch_memory_ptr, ULONG scratch_memory_size, ULONG error_correction_option, ULONG *errors_detected); +UINT fx_media_close(FX_MEDIA *media_ptr); +UINT fx_media_flush(FX_MEDIA *media_ptr); +UINT fx_media_format(FX_MEDIA *media_ptr, VOID (*driver)(FX_MEDIA *media), VOID *driver_info_ptr, UCHAR *memory_ptr, UINT memory_size, + CHAR *volume_name, UINT number_of_fats, UINT directory_entries, UINT hidden_sectors, + ULONG total_sectors, UINT bytes_per_sector, UINT sectors_per_cluster, + UINT heads, UINT sectors_per_track); +#ifdef FX_DISABLE_ERROR_CHECKING +UINT _fx_media_open(FX_MEDIA *media_ptr, CHAR *media_name, + VOID (*media_driver)(FX_MEDIA *), VOID *driver_info_ptr, + VOID *memory_ptr, ULONG memory_size); +#else +UINT _fxe_media_open(FX_MEDIA *media_ptr, CHAR *media_name, + VOID (*media_driver)(FX_MEDIA *), VOID *driver_info_ptr, + VOID *memory_ptr, ULONG memory_size, UINT media_control_block_size); +#endif +UINT fx_media_read(FX_MEDIA *media_ptr, ULONG logical_sector, VOID *buffer_ptr); +UINT fx_media_space_available(FX_MEDIA *media_ptr, ULONG *available_bytes_ptr); +UINT fx_media_volume_get(FX_MEDIA *media_ptr, CHAR *volume_name, UINT volume_source); +UINT fx_media_volume_get_extended(FX_MEDIA *media_ptr, CHAR *volume_name, UINT volume_name_buffer_length, UINT volume_source); +UINT fx_media_volume_set(FX_MEDIA *media_ptr, CHAR *volume_name); +UINT fx_media_write(FX_MEDIA *media_ptr, ULONG logical_sector, VOID *buffer_ptr); +UINT fx_media_open_notify_set(FX_MEDIA *media_ptr, VOID (*media_open_notify)(FX_MEDIA *)); +UINT fx_media_close_notify_set(FX_MEDIA *media_ptr, VOID (*media_close_notify)(FX_MEDIA *)); +UINT fx_media_extended_space_available(FX_MEDIA *media_ptr, ULONG64 *available_bytes_ptr); + +UINT fx_system_date_get(UINT *year, UINT *month, UINT *day); +UINT fx_system_date_set(UINT year, UINT month, UINT day); +VOID fx_system_initialize(VOID); +UINT fx_system_time_get(UINT *hour, UINT *minute, UINT *second); +UINT fx_system_time_set(UINT hour, UINT minute, UINT second); + +UINT fx_unicode_directory_create(FX_MEDIA *media_ptr, + UCHAR *source_unicode_name, ULONG source_unicode_length, + CHAR *short_name); +UINT fx_unicode_directory_rename(FX_MEDIA *media_ptr, + UCHAR *old_unicode_name, ULONG old_unicode_length, + UCHAR *new_unicode_name, ULONG new_unicode_length, + CHAR *new_short_name); +UINT fx_unicode_file_create(FX_MEDIA *media_ptr, + UCHAR *source_unicode_name, ULONG source_unicode_length, + CHAR *short_name); +UINT fx_unicode_file_rename(FX_MEDIA *media_ptr, UCHAR *old_unicode_name, ULONG old_unicode_length, + UCHAR *new_unicode_name, ULONG new_unicode_length, CHAR *new_short_name); +ULONG fx_unicode_length_get(UCHAR *unicode_name); +ULONG fx_unicode_length_get_extended(UCHAR *unicode_name, UINT buffer_length); +UINT fx_unicode_name_get(FX_MEDIA *media_ptr, CHAR *source_short_name, + UCHAR *destination_unicode_name, ULONG *destination_unicode_length); +UINT fx_unicode_name_get_extended(FX_MEDIA *media_ptr, CHAR *source_short_name, + UCHAR *destination_unicode_name, ULONG *destination_unicode_length, ULONG unicode_name_buffer_length); +UINT fx_unicode_short_name_get(FX_MEDIA *media_ptr, + UCHAR *source_unicode_name, ULONG source_unicode_length, + CHAR *destination_short_name); +UINT fx_unicode_short_name_get_extended(FX_MEDIA *media_ptr, + UCHAR *source_unicode_name, ULONG source_unicode_length, + CHAR *destination_short_name, ULONG short_name_buffer_length); + +#ifdef FX_ENABLE_FAULT_TOLERANT +UINT fx_fault_tolerant_enable(FX_MEDIA *media_ptr, VOID *memory_buffer, UINT memory_size); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/* Define prototype for utility services commonly used by FileX I/O Drivers. This eliminates the + need to include internal FileX component files in I/O drivers. */ + +UINT _fx_utility_16_unsigned_read(UCHAR *source_ptr); +VOID _fx_utility_16_unsigned_write(UCHAR *dest_ptr, UINT value); +ULONG _fx_utility_32_unsigned_read(UCHAR *source_ptr); +VOID _fx_utility_32_unsigned_write(UCHAR *dest_ptr, ULONG value); +ULONG64 _fx_utility_64_unsigned_read(UCHAR *source_ptr); +VOID _fx_utility_64_unsigned_write(UCHAR *dest_ptr, ULONG64 value); +VOID _fx_utility_memory_copy(UCHAR *source_ptr, UCHAR *dest_ptr, ULONG size); +VOID _fx_utility_memory_set(UCHAR *dest_ptr, UCHAR value, ULONG size); + +#endif + +/* Determine if a C++ compiler is being used. If so, complete the standard + C conditional started above. */ +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/common/inc/fx_directory.h b/common/inc/fx_directory.h new file mode 100644 index 0000000..27b793c --- /dev/null +++ b/common/inc/fx_directory.h @@ -0,0 +1,122 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* fx_directory.h PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file defines the FileX Directory component constants, data */ +/* definitions, and external references. It is assumed that fx_api.h */ +/* (and fx_port.h) have already been included. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ + +#ifndef FX_DIRECTORY_H +#define FX_DIRECTORY_H + + +/* Define the external Directory component function prototypes. */ + +UINT _fx_directory_attributes_read(FX_MEDIA *media_ptr, CHAR *directory_name, UINT *attributes_ptr); +UINT _fx_directory_attributes_set(FX_MEDIA *media_ptr, CHAR *directory_name, UINT attributes); +UINT _fx_directory_create(FX_MEDIA *media_ptr, CHAR *directory_name); +UINT _fx_directory_default_get(FX_MEDIA *media_ptr, CHAR **return_path_name); +UINT _fx_directory_default_get_copy(FX_MEDIA *media_ptr, CHAR *return_path_name_buffer, UINT return_path_name_buffer_size); +UINT _fx_directory_default_set(FX_MEDIA *media_ptr, CHAR *new_path_name); +UINT _fx_directory_delete(FX_MEDIA *media_ptr, CHAR *directory_name); +UINT _fx_directory_entry_read_FAT(FX_MEDIA *media_ptr, FX_DIR_ENTRY *source_dir, ULONG *entry_ptr, + FX_DIR_ENTRY *destination_ptr); +UINT _fx_directory_first_entry_find(FX_MEDIA *media_ptr, CHAR *directory_name); +UINT _fx_directory_first_full_entry_find(FX_MEDIA *media_ptr, CHAR *directory_name, UINT *attributes, + ULONG *size, UINT *year, UINT *month, UINT *day, UINT *hour, UINT *minute, UINT *second); +UINT _fx_directory_information_get(FX_MEDIA *media_ptr, CHAR *directory_name, UINT *attributes, ULONG *size, + UINT *year, UINT *month, UINT *day, UINT *hour, UINT *minute, UINT *second); +UINT _fx_directory_local_path_clear(FX_MEDIA *media_ptr); +UINT _fx_directory_local_path_get(FX_MEDIA *media_ptr, CHAR **return_path_name); +UINT _fx_directory_local_path_get_copy(FX_MEDIA *media_ptr, CHAR *return_path_name_buffer, UINT return_path_name_buffer_size); +UINT _fx_directory_local_path_restore(FX_MEDIA *media_ptr, FX_LOCAL_PATH *local_path_ptr); +UINT _fx_directory_local_path_set(FX_MEDIA *media_ptr, FX_LOCAL_PATH *local_path_ptr, CHAR *new_path_name); +UINT _fx_directory_long_name_get(FX_MEDIA *media_ptr, CHAR *short_file_name, CHAR *long_file_name); +UINT _fx_directory_long_name_get_extended(FX_MEDIA *media_ptr, CHAR *short_file_name, CHAR *long_file_name, UINT long_file_name_buffer_length); +UINT _fx_directory_name_test(FX_MEDIA *media_ptr, CHAR *directory_name); +UINT _fx_directory_next_entry_find(FX_MEDIA *media_ptr, CHAR *directory_name); +UINT _fx_directory_next_full_entry_find(FX_MEDIA *media_ptr, CHAR *directory_name, UINT *attributes, + ULONG *size, UINT *year, UINT *month, UINT *day, UINT *hour, UINT *minute, UINT *second); +UINT _fx_directory_rename(FX_MEDIA *media_ptr, CHAR *old_directory_name, CHAR *new_directory_name); +UINT _fx_directory_short_name_get(FX_MEDIA *media_ptr, CHAR *long_file_name, CHAR *short_file_name); +UINT _fx_directory_short_name_get_extended(FX_MEDIA *media_ptr, CHAR *long_file_name, CHAR *short_file_name, UINT short_file_name_length); + +UINT _fxe_directory_attributes_read(FX_MEDIA *media_ptr, CHAR *directory_name, UINT *attributes_ptr); +UINT _fxe_directory_attributes_set(FX_MEDIA *media_ptr, CHAR *directory_name, UINT attributes); +UINT _fxe_directory_create(FX_MEDIA *media_ptr, CHAR *directory_name); +UINT _fxe_directory_default_get(FX_MEDIA *media_ptr, CHAR **return_path_name); +UINT _fxe_directory_default_get_copy(FX_MEDIA *media_ptr, CHAR *return_path_name_buffer, UINT return_path_name_buffer_size); +UINT _fxe_directory_default_set(FX_MEDIA *media_ptr, CHAR *new_path_name); +UINT _fxe_directory_delete(FX_MEDIA *media_ptr, CHAR *directory_name); +UINT _fxe_directory_first_entry_find(FX_MEDIA *media_ptr, CHAR *directory_name); +UINT _fxe_directory_first_full_entry_find(FX_MEDIA *media_ptr, CHAR *directory_name, UINT *attributes, + ULONG *size, UINT *year, UINT *month, UINT *day, UINT *hour, UINT *minute, UINT *second); +UINT _fxe_directory_information_get(FX_MEDIA *media_ptr, CHAR *directory_name, UINT *attributes, ULONG *size, + UINT *year, UINT *month, UINT *day, UINT *hour, UINT *minute, UINT *second); +UINT _fxe_directory_local_path_clear(FX_MEDIA *media_ptr); +UINT _fxe_directory_local_path_get(FX_MEDIA *media_ptr, CHAR **return_path_name); +UINT _fxe_directory_local_path_get_copy(FX_MEDIA *media_ptr, CHAR *return_path_name_buffer, UINT return_path_name_buffer_size); +UINT _fxe_directory_local_path_restore(FX_MEDIA *media_ptr, FX_LOCAL_PATH *local_path_ptr); +UINT _fxe_directory_local_path_set(FX_MEDIA *media_ptr, FX_LOCAL_PATH *local_path_ptr, CHAR *new_path_name, UINT local_path_control_block_size); +UINT _fxe_directory_long_name_get(FX_MEDIA *media_ptr, CHAR *short_file_name, CHAR *long_file_name); +UINT _fxe_directory_long_name_get_extended(FX_MEDIA *media_ptr, CHAR *short_file_name, CHAR *long_file_name, UINT long_file_name_buffer_length); +UINT _fxe_directory_name_test(FX_MEDIA *media_ptr, CHAR *directory_name); +UINT _fxe_directory_next_entry_find(FX_MEDIA *media_ptr, CHAR *directory_name); +UINT _fxe_directory_next_full_entry_find(FX_MEDIA *media_ptr, CHAR *directory_name, UINT *attributes, + ULONG *size, UINT *year, UINT *month, UINT *day, UINT *hour, UINT *minute, UINT *second); +UINT _fxe_directory_rename(FX_MEDIA *media_ptr, CHAR *old_directory_name, CHAR *new_directory_name); +UINT _fxe_directory_short_name_get(FX_MEDIA *media_ptr, CHAR *long_file_name, CHAR *short_file_name); +UINT _fxe_directory_short_name_get_extended(FX_MEDIA *media_ptr, CHAR *long_file_name, CHAR *short_file_name, UINT short_file_name_length); + + +/* Define the internal Directory component function prototypes. */ + +UINT _fx_directory_entry_read(FX_MEDIA *media_ptr, FX_DIR_ENTRY *source_dir, ULONG *entry, FX_DIR_ENTRY *destination_ptr); +UINT _fx_directory_entry_write(FX_MEDIA *media_ptr, FX_DIR_ENTRY *entry_ptr); +UINT _fx_directory_free_search(FX_MEDIA *media_ptr, FX_DIR_ENTRY *directory_ptr, FX_DIR_ENTRY *entry_ptr); +CHAR *_fx_directory_name_extract(CHAR *source_ptr, CHAR *dest_ptr); +UINT _fx_directory_search(FX_MEDIA *media_ptr, CHAR *name_ptr, FX_DIR_ENTRY *entry_ptr, FX_DIR_ENTRY *last_dir_ptr, CHAR **last_name_ptr); + +#endif + diff --git a/common/inc/fx_fault_tolerant.h b/common/inc/fx_fault_tolerant.h new file mode 100644 index 0000000..73aa586 --- /dev/null +++ b/common/inc/fx_fault_tolerant.h @@ -0,0 +1,407 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Fault Tolerant */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* fx_fault_tolerant.h PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file defines the FileX Fault Tolerant component constants, */ +/* data definitions, and external references. It is assumed that */ +/* fx_api.h (and fx_port.h) have already been included. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ + +#ifndef _FX_FAULT_TOLERANT_H_ +#define _FX_FAULT_TOLERANT_H_ + +#ifdef FX_ENABLE_FAULT_TOLERANT + +#ifndef FX_FAULT_TOLERANT +#error "FX_FAULT_TOLERANT must be defined with FX_ENABLE_FAULT_TOLERANT" +#endif /* FX_FAULT_TOLERANT */ + +/* Define ID of fault tolerant. */ +#define FX_FAULT_TOLERANT_ID 0x46544C52 + +/* Define fault tolerant version. */ +#define FX_FAULT_TOLERANT_VERSION_MAJOR 0x01 +#define FX_FAULT_TOLERANT_VERSION_MINOR 0x00 + +/* Define byte offset in boot sector where the cluster number of the Fault Tolerant Log file is. + Note that this field (byte 116 to 119) is marked as reserved by FAT 12/16/32 specification. */ +#ifndef FX_FAULT_TOLERANT_BOOT_INDEX +#define FX_FAULT_TOLERANT_BOOT_INDEX 116 +#endif /* FX_FAULT_TOLERANT_BOOT_INDEX */ + +/* Define the extension invoked when fault tolerant log is recovered or applied during enable API. */ +#ifndef FX_FAULT_TOLERANT_ENABLE_EXTENSION +#define FX_FAULT_TOLERANT_ENABLE_EXTENSION +#endif /* FX_FAULT_TOLERANT_ENABLE_EXTENSION */ + +/* Define the extension invoked when fault tolerant log is applied. */ +#ifndef FX_FAULT_TOLERANT_APPLY_LOGS_EXTENSION +#define FX_FAULT_TOLERANT_APPLY_LOGS_EXTENSION +#endif /* FX_FAULT_TOLERANT_APPLY_LOGS_EXTENSION */ + +/* If not-defined, default port-specific processing extensions to white space. */ +#ifndef FX_FAULT_TOLERANT_WRITE_LOG_FILE_EXTENSION +#define FX_FAULT_TOLERANT_WRITE_LOG_FILE_EXTENSION +#endif /* FX_FAULT_TOLERANT_WRITE_LOG_FILE_EXTENSION */ + +/* Define the maximum size of log file. */ +#define FX_FAULT_TOLERANT_MAXIMUM_LOG_FILE_SIZE 3072 + +/* For backward compatibility, map the symbols deprecated to FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE. */ +#ifndef FX_FAULT_TOLERANT_MINIMAL_CLUSTER +#define FX_FAULT_TOLERANT_MINIMAL_CLUSTER FX_FAULT_TOLERANT_MAXIMUM_LOG_FILE_SIZE +#endif /* FX_FAULT_TOLERANT_MINIMAL_CLUSTER */ +#ifndef FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE +#define FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE FX_FAULT_TOLERANT_MAXIMUM_LOG_FILE_SIZE +#endif /* FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE */ + +/* Define the states of the fault tolerant module. */ +#define FX_FAULT_TOLERANT_STATE_IDLE 0x00u +#define FX_FAULT_TOLERANT_STATE_STARTED 0x01u +#define FX_FAULT_TOLERANT_STATE_SET_FAT_CHAIN 0x02u + +/* Define types of logs. */ +#define FX_FAULT_TOLERANT_FAT_LOG_TYPE 1 +#define FX_FAULT_TOLERANT_DIR_LOG_TYPE 2 +#define FX_FAULT_TOLERANT_BITMAP_LOG_TYPE 3 + +/* Define operations of FAT chain. */ +#define FX_FAULT_TOLERANT_FAT_CHAIN_RECOVER 0 /* Recover new FAT chain. */ +#define FX_FAULT_TOLERANT_FAT_CHAIN_CLEANUP 1 /* Cleanup old FAT chain. */ + +/* Define flags for FAT chain log. */ +#define FX_FAULT_TOLERANT_FLAG_FAT_CHAIN_VALID 0x01 +#define FX_FAULT_TOLERANT_FLAG_BITMAP_USED 0x02 + +/* Define size of each section in log file. */ +#define FX_FAULT_TOLERANT_LOG_HEADER_SIZE sizeof(FX_FAULT_TOLERANT_LOG_HEADER) +#define FX_FAULT_TOLERANT_FAT_CHAIN_SIZE sizeof(FX_FAULT_TOLERANT_FAT_CHAIN) +#define FX_FAULT_TOLERANT_LOG_CONTENT_HEADER_SIZE sizeof(FX_FAULT_TOLERANT_LOG_CONTENT) + +/* Define offset of each section in log file. */ +#define FX_FAULT_TOLERANT_FAT_CHAIN_OFFSET (FX_FAULT_TOLERANT_LOG_HEADER_SIZE) +#define FX_FAULT_TOLERANT_LOG_CONTENT_OFFSET (FX_FAULT_TOLERANT_LOG_HEADER_SIZE + FX_FAULT_TOLERANT_FAT_CHAIN_SIZE) + +/* Define size of log entries. */ +/* The total size of DIR log entry is variable. 16 is the fixed size of DIR log entry. */ +#define FX_FAULT_TOLERANT_FAT_LOG_ENTRY_SIZE sizeof(FX_FAULT_TOLERANT_FAT_LOG) +#define FX_FAULT_TOLERANT_BITMAP_LOG_ENTRY_SIZE sizeof(FX_FAULT_TOLERANT_BITMAP_LOG) +#define FX_FAULT_TOLERANT_DIR_LOG_ENTRY_SIZE sizeof(FX_FAULT_TOLERANT_DIR_LOG) + +#ifdef FX_FAULT_TOLERANT_TRANSACTION_FAIL_FUNCTION +#define FX_FAULT_TOLERANT_TRANSACTION_FAIL _fx_fault_tolerant_transaction_fail +#else +/* Define transaction fail for fault tolerant. */ +#define FX_FAULT_TOLERANT_TRANSACTION_FAIL(m) \ + if ((m) -> fx_media_fault_tolerant_enabled == FX_TRUE) \ + { \ + (m) -> fx_media_fault_tolerant_transaction_count--; \ + if ((m) -> fx_media_fault_tolerant_transaction_count == 0) \ + { \ + _fx_fault_tolerant_recover(m); \ + _fx_fault_tolerant_reset_log_file(m); \ + } \ + } + +#endif /* FX_FAULT_TOLERANT_TRANSACTION_FAIL_FUNCTION */ + +/* Log file format + * The entire log file fits into one cluster. + * + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + ID + + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + Total Log Size (in Bytes) + Header Checksum + Log Header + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + version major + version minor + reserved + + * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + Checksum of FAT chain + Flag | Reserved + + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + Insertion Point - Front + + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + Head cluster of newly created FAT chain + + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ FAT Chain + * + Head cluster of original FAT chain + + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + Insertion Point - Back + + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + Next Deletion Point + + * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + checksum of log content + count of log entries + + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + (log 0) . + + * + . + + * + . + + * + . + + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + (log 1) . + + * + . + Log Entries + * + . + + * + . + + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + ... + + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + (log N) . + + * + . + + * + . + + * + . + + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + * + * Log Header + * + * * ID: It is fixed value FX_FAULT_TOLERANT_ID. + * * Total Size: Total size (in bytes) of the log file, including Log Header, FAT Chain and Log Entries + * * Header Checksum: It is the checksum of all data in log header. + * * Version Major: The major version number. + * * Version Minor: The minor version number. + * * Reserved: Reserved for future use. + * + * FAT Chain + * + * * Checksum of FAT chain: It is the checksum of all data in the FAT Chain section. + * * Flag: 8 bits. The 1-6 bits are not used (from left to right). + * bit 7: When set, bitmap is used in old FAT chain. + * bit 8: When set, FAT chain is valid. + * * Reserved: Reserved for future use. + * * Insertion Point - Front: The cluster where the newly created chain is attached to + * * Head cluster of new FAT chain. It is the head cluster of new FAT chain. + * * Head cluster of oritinal FAT chain. It is the head cluster of the original FAT chain, + * which will be removed. + * * Inswertion Point - Back: The original cluster right after the newly created chain is inserted. + * * Next Deletion Point: next session of cluster to delete. It is used during deleting FAT chain. + * The deltion is carried out in stages for perofmrance reasons. When delting a FAT chain, the + * Next Deletion Point is chosen, and the chain between head and the Next Deletion Point is deleted. + * + * Log Entries: + * + * * Checksum of log entries: It is the checksum of all data in Log Entries + * * Counter of log entries: It is the counter of all log entries in Log content. + * * log 0 ... log N: Log entries. The formats are described below. + */ + +/* Defined structure of log header. + + + */ +typedef struct FX_FAULT_TOLERANT_LOG_HEADER_STRUCT +{ + ULONG fx_fault_tolerant_log_header_id; + USHORT fx_fault_tolerant_log_header_total_size; + USHORT fx_fault_tolerant_log_header_checksum; + UCHAR fx_fault_tolerant_log_header_version_major; + UCHAR fx_fault_tolerant_log_header_version_minor; + USHORT fx_fault_tolerant_log_header_reserved; +} FX_FAULT_TOLERANT_LOG_HEADER; + +/* Define structure of FAT chain. */ +typedef struct FX_FAULT_TOLERANT_FAT_CHAIN_STRUCT +{ + USHORT fx_fault_tolerant_FAT_chain_checksumm; + UCHAR fx_fault_tolerant_FAT_chain_flag; + UCHAR fx_fault_tolerant_FAT_chain_reserved; + + ULONG fx_fault_tolerant_FAT_chain_insertion_front; + ULONG fx_fault_tolerant_FAT_chain_head_new; + ULONG fx_fault_tolerant_FAT_chain_head_original; + ULONG fx_fault_tolerant_FAT_chain_insertion_back; + ULONG fx_fault_tolerant_FAT_chain_next_deletion; +} FX_FAULT_TOLERANT_FAT_CHAIN; + + +/* Define structure of Log content. */ +typedef struct FX_FAULT_TOLERANT_LOG_CONTENT_STRUCT +{ + USHORT fx_fault_tolerant_log_content_checksum; + USHORT fx_fault_tolerant_log_content_count; +} FX_FAULT_TOLERANT_LOG_CONTENT; + + +/* 3 types of log entries are defined. */ +/* FAT log format + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + type + size + + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + cluster + + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + value + + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + * Description: + * + * Type: FX_FAULT_TOLERANT_FAT_LOG. + * Size: The size of this log entry. + * Cluster: The cluster number in FAT. + * Value: The value of cluster in FAT. + * + */ +typedef struct FX_FAULT_TOLERANT_FAT_LOG_STRUCT +{ + USHORT fx_fault_tolerant_FAT_log_type; + USHORT fx_fault_tolerant_FAT_log_size; + ULONG fx_fault_tolerant_FAT_log_cluster; + ULONG fx_fault_tolerant_FAT_log_value; +} FX_FAULT_TOLERANT_FAT_LOG; + +/* DIR log format + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + type + size + + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + sector offset + + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + log sector + + * + + + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + log data + + * + . + + * + . + + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + * Fields description, + * + * Type: FX_FAULT_TOLERANT_DIRECTORY_LOG + * Size: The size of this log entry. + * Sector Offset: The offset in original sector this DIR is trying to write to. + * Log Sector: The original sector this DIR is trying to write to. + * Log Data: Content of DIR entries. + * + */ +typedef struct FX_FAULT_TOLERANT_DIR_LOG_STRUCT +{ + USHORT fx_fault_tolerant_dir_log_type; + USHORT fx_fault_tolerant_dir_log_size; + ULONG fx_fault_tolerant_dir_log_offset; + ULONG64 fx_fault_tolerant_dir_log_sector; +} FX_FAULT_TOLERANT_DIR_LOG; + +/* Bitmap log format + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + type + size + + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + cluster + + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + value + + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + * Description, + * + * Type: FX_FAULT_TOLERANT_BITMAP_LOG. + * Size: The size of this log entry. + * Cluster: The cluster number in FAT. + * Value: The value of cluster in FAT. + */ +typedef struct FX_FAULT_TOLERANT_BITMAP_LOG_STRUCT +{ + USHORT fx_fault_tolerant_bitmap_log_type; + USHORT fx_fault_tolerant_bitmap_log_size; + ULONG fx_fault_tolerant_bitmap_log_cluster; + ULONG fx_fault_tolerant_bitmap_log_value; +} FX_FAULT_TOLERANT_BITMAP_LOG; + + +/* This function checks whether or not the log file exists, and creates the log file if it does not exist. */ +UINT _fx_fault_tolerant_enable(FX_MEDIA *media_ptr, VOID *memory_buffer, UINT memory_size); +UINT _fxe_fault_tolerant_enable(FX_MEDIA *media_ptr, VOID *memory_buffer, UINT memory_size); + +/* This function resets statistics and marks the start of a transaction. */ +UINT _fx_fault_tolerant_transaction_start(FX_MEDIA *media_ptr); + +/* This function marks the end of a transaction and completes the log file. + * Then it applies all data in the log file to the file system. + * Finally it resets the log header section. */ +UINT _fx_fault_tolerant_transaction_end(FX_MEDIA *media_ptr); + +/* This function resets the log file. */ +UINT _fx_fault_tolerant_reset_log_file(FX_MEDIA *media_ptr); + +/* This function applies the log file to the file system. */ +UINT _fx_fault_tolerant_apply_logs(FX_MEDIA *media_ptr); + +/* This function recovers FAT chain. */ +UINT _fx_fault_tolerant_recover(FX_MEDIA *media_ptr); + +/* This function adds a FAT log entry. */ +UINT _fx_fault_tolerant_add_FAT_log(FX_MEDIA *media_ptr, ULONG cluster, ULONG value); + +/* This function adds a directory log entry. */ +UINT _fx_fault_tolerant_add_dir_log(FX_MEDIA *media_ptr, ULONG64 logical_sector, ULONG offset, + UCHAR *data, ULONG data_size); + +/* This function sets the FAT chain. */ +UINT _fx_fault_tolerant_set_FAT_chain(FX_MEDIA *media_ptr, UINT use_bitmap, ULONG insertion_front, + ULONG new_head_cluster, ULONG original_head_cluster, ULONG insertion_back); + +/* This function reads a FAT entry from log file. If it doesn't exist in log file, return and read from FAT entry directly. */ +UINT _fx_fault_tolerant_read_FAT(FX_MEDIA *media_ptr, ULONG cluster, ULONG *value, ULONG log_type); + +/* This function reads directory sector from log file. */ +UINT _fx_fault_tolerant_read_directory_sector(FX_MEDIA *media_ptr, ULONG64 logical_sector, + VOID *buffer_ptr, ULONG64 sectors); + +/* This function calculates the checksum of the log header. */ +USHORT _fx_fault_tolerant_calculate_checksum(UCHAR *data, UINT len); + +/* This function cleans up FAT chain. */ +UINT _fx_fault_tolerant_cleanup_FAT_chain(FX_MEDIA *media_ptr, UINT operation); + +/* This function reads log file from file system to memory buffer. */ +UINT _fx_fault_tolerant_read_log_file(FX_MEDIA *media_ptr); + +/* This function writes data of one sector from memory to log file in file system. */ +UINT _fx_fault_tolerant_write_log_file(FX_MEDIA *media_ptr, ULONG relative_sector); + +/* This function creates log file. */ +UINT _fx_fault_tolerant_create_log_file(FX_MEDIA *media_ptr); + +#ifdef FX_FAULT_TOLERANT_TRANSACTION_FAIL_FUNCTION +/* This function cleans up resources created by fault tolerant when transaction fails. */ +UINT _fx_fault_tolerant_transaction_fail(FX_MEDIA *media_ptr); +#endif /* FX_FAULT_TOLERANT_TRANSACTION_FAIL_FUNCTION */ + + +#endif /* FX_ENABLE_FAULT_TOLERANT */ + +#endif /* _FX_FAULT_TOLERANT_H_ */ + diff --git a/common/inc/fx_file.h b/common/inc/fx_file.h new file mode 100644 index 0000000..f7df51b --- /dev/null +++ b/common/inc/fx_file.h @@ -0,0 +1,125 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* fx_file.h PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file defines the FileX File component constants, data */ +/* definitions, and external references. It is assumed that fx_api.h */ +/* (and fx_port.h) have already been included. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), and */ +/* added conditional to */ +/* disable one line function, */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ + +#ifndef FX_FILE_H +#define FX_FILE_H + + +/* Define the external File component function prototypes. */ + +#ifndef FX_DISABLE_ONE_LINE_FUNCTION +UINT _fx_file_allocate(FX_FILE *file_ptr, ULONG size); +#else +#define _fx_file_allocate(f, s) _fx_file_extended_allocate(f, (ULONG64)s); +#endif /* FX_DISABLE_ONE_LINE_FUNCTION */ +UINT _fx_file_attributes_read(FX_MEDIA *media_ptr, CHAR *file_name, UINT *attributes_ptr); +UINT _fx_file_attributes_set(FX_MEDIA *media_ptr, CHAR *file_name, UINT attributes); +UINT _fx_file_best_effort_allocate(FX_FILE *file_ptr, ULONG size, ULONG *actual_size_allocated); +UINT _fx_file_close(FX_FILE *file_ptr); +UINT _fx_file_create(FX_MEDIA *media_ptr, CHAR *file_name); +UINT _fx_file_date_time_set(FX_MEDIA *media_ptr, CHAR *file_name, + UINT year, UINT month, UINT day, UINT hour, UINT minute, UINT second); +UINT _fx_file_delete(FX_MEDIA *media_ptr, CHAR *file_name); +UINT _fx_file_open(FX_MEDIA *media_ptr, FX_FILE *file_ptr, CHAR *file_name, + UINT open_type); +UINT _fx_file_read(FX_FILE *file_ptr, VOID *buffer_ptr, ULONG request_size, ULONG *actual_size); +#ifndef FX_DISABLE_ONE_LINE_FUNCTION +UINT _fx_file_relative_seek(FX_FILE *file_ptr, ULONG byte_offset, UINT seek_from); +#else +#define _fx_file_relative_seek(f, b, sf) _fx_file_extended_relative_seek(f, (ULONG64)b, sf); +#endif /* FX_DISABLE_ONE_LINE_FUNCTION */ +UINT _fx_file_rename(FX_MEDIA *media_ptr, CHAR *old_file_name, CHAR *new_file_name); +#ifndef FX_DISABLE_ONE_LINE_FUNCTION +UINT _fx_file_seek(FX_FILE *file_ptr, ULONG byte_offset); +UINT _fx_file_truncate(FX_FILE *file_ptr, ULONG size); +UINT _fx_file_truncate_release(FX_FILE *file_ptr, ULONG size); +#else +#define _fx_file_seek(f, b) _fx_file_extended_seek(f, (ULONG64)b) +#define _fx_file_truncate(f, s) _fx_file_extended_truncate(f, (ULONG64)s); +#define _fx_file_truncate_release(f, s) _fx_file_extended_truncate_release(f, (ULONG64)s); +#endif /* FX_DISABLE_ONE_LINE_FUNCTION */ +UINT _fx_file_write(FX_FILE *file_ptr, VOID *buffer_ptr, ULONG size); +UINT _fx_file_write_notify_set(FX_FILE *file_ptr, VOID (*file_write_notify)(FX_FILE *)); +UINT _fx_file_extended_allocate(FX_FILE *file_ptr, ULONG64 size); +UINT _fx_file_extended_best_effort_allocate(FX_FILE *file_ptr, ULONG64 size, ULONG64 *actual_size_allocated); +UINT _fx_file_extended_relative_seek(FX_FILE *file_ptr, ULONG64 byte_offset, UINT seek_from); +UINT _fx_file_extended_seek(FX_FILE *file_ptr, ULONG64 byte_offset); +UINT _fx_file_extended_truncate(FX_FILE *file_ptr, ULONG64 size); +UINT _fx_file_extended_truncate_release(FX_FILE *file_ptr, ULONG64 size); + +UINT _fxe_file_allocate(FX_FILE *file_ptr, ULONG size); +UINT _fxe_file_attributes_read(FX_MEDIA *media_ptr, CHAR *file_name, UINT *attributes_ptr); +UINT _fxe_file_attributes_set(FX_MEDIA *media_ptr, CHAR *file_name, UINT attributes); +UINT _fxe_file_best_effort_allocate(FX_FILE *file_ptr, ULONG size, ULONG *actual_size_allocated); +UINT _fxe_file_close(FX_FILE *file_ptr); +UINT _fxe_file_create(FX_MEDIA *media_ptr, CHAR *file_name); +UINT _fxe_file_date_time_set(FX_MEDIA *media_ptr, CHAR *file_name, + UINT year, UINT month, UINT day, UINT hour, UINT minute, UINT second); +UINT _fxe_file_delete(FX_MEDIA *media_ptr, CHAR *file_name); +UINT _fxe_file_open(FX_MEDIA *media_ptr, FX_FILE *file_ptr, CHAR *file_name, + UINT open_type, UINT file_control_block_size); +UINT _fxe_file_read(FX_FILE *file_ptr, VOID *buffer_ptr, ULONG request_size, ULONG *actual_size); +UINT _fxe_file_relative_seek(FX_FILE *file_ptr, ULONG byte_offset, UINT seek_from); +UINT _fxe_file_rename(FX_MEDIA *media_ptr, CHAR *old_file_name, CHAR *new_file_name); +UINT _fxe_file_seek(FX_FILE *file_ptr, ULONG byte_offset); +UINT _fxe_file_truncate(FX_FILE *file_ptr, ULONG size); +UINT _fxe_file_truncate_release(FX_FILE *file_ptr, ULONG size); +UINT _fxe_file_write(FX_FILE *file_ptr, VOID *buffer_ptr, ULONG size); +UINT _fxe_file_write_notify_set(FX_FILE *file_ptr, VOID (*file_write_notify)(FX_FILE *)); +UINT _fxe_file_extended_allocate(FX_FILE *file_ptr, ULONG64 size); +UINT _fxe_file_extended_best_effort_allocate(FX_FILE *file_ptr, ULONG64 size, ULONG64 *actual_size_allocated); +UINT _fxe_file_extended_relative_seek(FX_FILE *file_ptr, ULONG64 byte_offset, UINT seek_from); +UINT _fxe_file_extended_seek(FX_FILE *file_ptr, ULONG64 byte_offset); +UINT _fxe_file_extended_truncate(FX_FILE *file_ptr, ULONG64 size); +UINT _fxe_file_extended_truncate_release(FX_FILE *file_ptr, ULONG64 size); + +#endif + diff --git a/common/inc/fx_media.h b/common/inc/fx_media.h new file mode 100644 index 0000000..38e3625 --- /dev/null +++ b/common/inc/fx_media.h @@ -0,0 +1,109 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* fx_media.h PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file defines the FileX Media component constants, data */ +/* definitions, and external references. It is assumed that fx_api.h */ +/* (and fx_port.h) have already been included. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ + +#ifndef FX_MEDIA_H +#define FX_MEDIA_H + + +/* Define the external Media component function prototypes. */ + +UINT _fx_media_abort(FX_MEDIA *media_ptr); +UINT _fx_media_cache_invalidate(FX_MEDIA *media_ptr); +UINT _fx_media_check(FX_MEDIA *media_ptr, UCHAR *scratch_memory_ptr, ULONG scratch_memory_size, ULONG error_correction_option, ULONG *errors_detected); +UINT _fx_media_close(FX_MEDIA *media_ptr); +UINT _fx_media_flush(FX_MEDIA *media_ptr); +UINT _fx_media_format(FX_MEDIA *media_ptr, VOID (*driver)(FX_MEDIA *media), VOID *driver_info_ptr, UCHAR *memory_ptr, UINT memory_size, + CHAR *volume_name, UINT number_of_fats, UINT directory_entries, UINT hidden_sectors, + ULONG total_sectors, UINT bytes_per_sector, UINT sectors_per_cluster, + UINT heads, UINT sectors_per_track); +UINT _fx_media_open(FX_MEDIA *media_ptr, CHAR *media_name, + VOID (*media_driver)(FX_MEDIA *), VOID *driver_info_ptr, + VOID *memory_ptr, ULONG memory_size); +UINT _fx_media_read(FX_MEDIA *media_ptr, ULONG logical_sector, VOID *buffer_ptr); +UINT _fx_media_space_available(FX_MEDIA *media_ptr, ULONG *available_bytes_ptr); +UINT _fx_media_volume_get(FX_MEDIA *media_ptr, CHAR *volume_name, UINT volume_source); +UINT _fx_media_volume_get_extended(FX_MEDIA *media_ptr, CHAR *volume_name, UINT volume_name_buffer_length, UINT volume_source); +UINT _fx_media_volume_set(FX_MEDIA *media_ptr, CHAR *volume_name); +UINT _fx_media_write(FX_MEDIA *media_ptr, ULONG logical_sector, VOID *buffer_ptr); +UINT _fx_media_open_notify_set(FX_MEDIA *media_ptr, VOID (*media_open_notify)(FX_MEDIA *)); +UINT _fx_media_close_notify_set(FX_MEDIA *media_ptr, VOID (*media_close_notify)(FX_MEDIA *)); +UINT _fx_media_extended_space_available(FX_MEDIA *media_ptr, ULONG64 *available_bytes_ptr); + + +UINT _fxe_media_abort(FX_MEDIA *media_ptr); +UINT _fxe_media_cache_invalidate(FX_MEDIA *media_ptr); +UINT _fxe_media_check(FX_MEDIA *media_ptr, UCHAR *scratch_memory_ptr, ULONG scratch_memory_size, ULONG error_correction_option, ULONG *errors_detected); +UINT _fxe_media_close(FX_MEDIA *media_ptr); +UINT _fxe_media_flush(FX_MEDIA *media_ptr); +UINT _fxe_media_format(FX_MEDIA *media_ptr, VOID (*driver)(FX_MEDIA *media), VOID *driver_info_ptr, UCHAR *memory_ptr, UINT memory_size, + CHAR *volume_name, UINT number_of_fats, UINT directory_entries, UINT hidden_sectors, + ULONG total_sectors, UINT bytes_per_sector, UINT sectors_per_cluster, + UINT heads, UINT sectors_per_track); +UINT _fxe_media_open(FX_MEDIA *media_ptr, CHAR *media_name, + VOID (*media_driver)(FX_MEDIA *), VOID *driver_info_ptr, + VOID *memory_ptr, ULONG memory_size, UINT media_control_block_size); +UINT _fxe_media_read(FX_MEDIA *media_ptr, ULONG logical_sector, VOID *buffer_ptr); +UINT _fxe_media_space_available(FX_MEDIA *media_ptr, ULONG *available_bytes_ptr); +UINT _fxe_media_volume_get(FX_MEDIA *media_ptr, CHAR *volume_name, UINT volume_source); +UINT _fxe_media_volume_get_extended(FX_MEDIA *media_ptr, CHAR *volume_name, UINT volume_name_buffer_length, UINT volume_source); +UINT _fxe_media_volume_set(FX_MEDIA *media_ptr, CHAR *volume_name); +UINT _fxe_media_write(FX_MEDIA *media_ptr, ULONG logical_sector, VOID *buffer_ptr); +UINT _fxe_media_open_notify_set(FX_MEDIA *media_ptr, VOID (*media_open_notify)(FX_MEDIA *)); +UINT _fxe_media_close_notify_set(FX_MEDIA *media_ptr, VOID (*media_close_notify)(FX_MEDIA *)); +UINT _fxe_media_extended_space_available(FX_MEDIA *media_ptr, ULONG64 *available_bytes_ptr); + + +/* Define the internal Media component function prototypes. */ + +ULONG _fx_media_check_FAT_chain_check(FX_MEDIA *media_ptr, ULONG starting_cluster, ULONG *last_valid_cluster, ULONG *total_valid_clusters, UCHAR *logical_fat); +ULONG _fx_media_check_lost_cluster_check(FX_MEDIA *media_ptr, UCHAR *logical_fat, ULONG total_clusters, ULONG error_correction_option); +UINT _fx_media_boot_info_extract(FX_MEDIA *media_ptr); + +#endif + diff --git a/common/inc/fx_system.h b/common/inc/fx_system.h new file mode 100644 index 0000000..a0b1547 --- /dev/null +++ b/common/inc/fx_system.h @@ -0,0 +1,175 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** System */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* fx_system.h PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file defines the FileX system constants and global data */ +/* definitions, including external references. It is assumed that */ +/* fx_api.h (and fx_port.h) have already been included. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ + +#ifndef FX_SYSTEM_H +#define FX_SYSTEM_H + + +/* Define System component constants. */ + +#define FX_TIMER_ID ((ULONG) 0x46585359) + + +/* Define the external System component function prototypes. */ + +VOID _fx_system_initialize(VOID); +UINT _fx_system_date_set(UINT year, UINT month, UINT day); +UINT _fx_system_time_set(UINT hour, UINT minute, UINT second); +UINT _fx_system_date_get(UINT *year, UINT *month, UINT *day); +UINT _fx_system_time_get(UINT *hour, UINT *minute, UINT *second); +VOID _fx_system_timer_entry(ULONG id); + +UINT _fxe_system_date_set(UINT year, UINT month, UINT day); +UINT _fxe_system_time_set(UINT hour, UINT minute, UINT second); +UINT _fxe_system_date_get(UINT *year, UINT *month, UINT *day); +UINT _fxe_system_time_get(UINT *hour, UINT *minute, UINT *second); + + +/* System Component data declarations follow. */ + +/* Determine if the initialization function of this component is including + this file. If so, make the data definitions really happen. Otherwise, + make them extern so other functions in the component can access them. */ + +#ifdef FX_SYSTEM_INIT +#define SYSTEM_DECLARE +#else +#define SYSTEM_DECLARE extern +#endif + + +/* Define the head pointer of the opened media list. */ + +SYSTEM_DECLARE FX_MEDIA *_fx_system_media_opened_ptr; + + +/* Define the variable that holds the number of open media. */ + +SYSTEM_DECLARE ULONG _fx_system_media_opened_count; + + +/* Define the system date variable. */ + +SYSTEM_DECLARE UINT _fx_system_date; + + +/* Define the system time variable. */ + +SYSTEM_DECLARE UINT _fx_system_time; + + +/* Define the variable that holds the maximum size of the sector cache. */ + +SYSTEM_DECLARE ULONG _fx_system_media_max_sector_cache; + + +/* Define the variable that holds the maximum size of the FAT cache. */ + +SYSTEM_DECLARE ULONG _fx_system_media_max_fat_cache; + + +/* Define the global FileX build options variables. These variables contain a bit + map representing how the FileX library was built. The following are the bit + field definitions: + + _fx_system_build_options_1: + + Bit(s) Meaning + + 31-24 FX_MAX_LONG_NAME_LEN + 23-16 FX_MAX_LAST_NAME_LEN + 15-11 Reserved + 10 FX_NO_TIMER defined + 9 FX_SINGLE_THREAD defined + 8 FX_DONT_UPDATE_OPEN_FILES defined + 7 FX_MEDIA_DISABLE_SEARCH_CACHE defined + 6 FX_MEDIA_STATISTICS_DISABLE defined + 5 Reserved + 4 FX_SINGLE_OPEN_LEGACY defined + 3 FX_RENAME_PATH_INHERIT defined + 2 FX_NO_LOCAL_PATH defined + 1 FX_FAULT_TOLERANT_DATA defined + 0 FX_FAULT_TOLERANT defined + + _fx_system_build_options_2: + + Bit(s) Meaning + + 31-16 FX_MAX_SECTOR_CACHE + 15-8 FX_FAT_MAP_SIZE + 7-0 FX_MAX_FAT_CACHE + + _fx_system_build_options_3: + + Bit(s) Meaning + + 31-24 Reserved + 23-16 FX_UPDATE_RATE_IN_SECONDS + 15-0 FX_UPDATE_RATE_IN_TICKS + + Note that values greater than the value that can be represented in the build options + bit field are represented as all ones in the bit field. For example, if FX_MAX_LONG_NAME_LEN + is 256, the value in the bits 31-24 of _fx_system_build_options_1 is 0xFF, which is 255 + decimal. */ + +SYSTEM_DECLARE ULONG _fx_system_build_options_1; +SYSTEM_DECLARE ULONG _fx_system_build_options_2; +SYSTEM_DECLARE ULONG _fx_system_build_options_3; + + +/* Define system timer control block. If accurate date/time stamps on + files is not needed, the define FX_NO_TIMER should be used when + compiling fx_system_initialize.c to eliminate the FileX timer. */ + +#ifndef FX_NO_TIMER +SYSTEM_DECLARE TX_TIMER _fx_system_timer; +#endif + +#endif + diff --git a/common/inc/fx_unicode.h b/common/inc/fx_unicode.h new file mode 100644 index 0000000..059d3aa --- /dev/null +++ b/common/inc/fx_unicode.h @@ -0,0 +1,110 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Unicode */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* fx_unicode.h PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file defines the FileX Unicode component constants, data */ +/* definitions, and external references. It is assumed that fx_api.h */ +/* (and fx_port.h) have already been included. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ + +#ifndef FX_UNICODE_H +#define FX_UNICODE_H + + +/* Define external data for the Unicode component. */ + +extern UCHAR _fx_unicode_temp_long_file_name[FX_MAX_LONG_NAME_LEN]; +extern UCHAR _fx_unicode_search_name[FX_MAX_LONG_NAME_LEN * 2]; + + +/* Define the external Unicode component function prototypes. */ + +UINT _fx_unicode_directory_create(FX_MEDIA *media_ptr, UCHAR *source_unicode_name, ULONG source_unicode_length, + CHAR *short_name); +UINT _fx_unicode_directory_rename(FX_MEDIA *media_ptr, UCHAR *old_unicode_name, ULONG old_unicode_length, + UCHAR *new_unicode_name, ULONG new_unicode_length, CHAR *new_short_name); +UINT _fx_unicode_file_create(FX_MEDIA *media_ptr, UCHAR *source_unicode_name, ULONG source_unicode_length, + CHAR *short_name); +ULONG _fx_unicode_length_get(UCHAR *unicode_name); +ULONG _fx_unicode_length_get_extended(UCHAR *unicode_name, UINT buffer_length); +UINT _fx_unicode_name_get(FX_MEDIA *media_ptr, CHAR *source_short_name, + UCHAR *destination_unicode_name, ULONG *destination_unicode_length); +UINT _fx_unicode_name_get_extended(FX_MEDIA *media_ptr, CHAR *source_short_name, + UCHAR *destination_unicode_name, ULONG *destination_unicode_length, ULONG unicode_name_buffer_length); +UINT _fx_unicode_file_rename(FX_MEDIA *media_ptr, UCHAR *old_unicode_name, ULONG old_unicode_length, + UCHAR *new_unicode_name, ULONG new_unicode_length, CHAR *new_short_name); +UINT _fx_unicode_short_name_get(FX_MEDIA *media_ptr, UCHAR *source_unicode_name, ULONG source_unicode_length, + CHAR *destination_short_name); +UINT _fx_unicode_short_name_get_extended(FX_MEDIA *media_ptr, UCHAR *source_unicode_name, ULONG source_unicode_length, + CHAR *destination_short_name, ULONG short_name_buffer_length); + +UINT _fxe_unicode_directory_create(FX_MEDIA *media_ptr, UCHAR *source_unicode_name, ULONG source_unicode_length, + CHAR *short_name); +UINT _fxe_unicode_directory_rename(FX_MEDIA *media_ptr, UCHAR *old_unicode_name, ULONG old_unicode_length, + UCHAR *new_unicode_name, ULONG new_unicode_length, CHAR *new_short_name); +UINT _fxe_unicode_file_create(FX_MEDIA *media_ptr, UCHAR *source_unicode_name, ULONG source_unicode_length, + CHAR *short_name); +UINT _fxe_unicode_file_rename(FX_MEDIA *media_ptr, UCHAR *old_unicode_name, ULONG old_unicode_length, + UCHAR *new_unicode_name, ULONG new_unicode_length, CHAR *new_short_name); +UINT _fxe_unicode_name_get(FX_MEDIA *media_ptr, CHAR *source_short_name, + UCHAR *destination_unicode_name, ULONG *destination_unicode_length); +UINT _fxe_unicode_name_get_extended(FX_MEDIA *media_ptr, CHAR *source_short_name, + UCHAR *destination_unicode_name, ULONG *destination_unicode_length, ULONG unicode_name_buffer_length); +UINT _fxe_unicode_short_name_get(FX_MEDIA *media_ptr, UCHAR *source_unicode_name, ULONG source_unicode_length, + CHAR *destination_short_name); +UINT _fxe_unicode_short_name_get_extended(FX_MEDIA *media_ptr, UCHAR *source_unicode_name, ULONG source_unicode_length, + CHAR *destination_short_name, ULONG short_name_buffer_length); + +/* Define the internal Unicode component function prototypes. */ + +UINT _fx_unicode_directory_search(FX_MEDIA *media_ptr, FX_DIR_ENTRY *entry_ptr, + UCHAR *short_name, ULONG short_name_buffer_length, + UCHAR *unicode_name, ULONG *unicode_name_length, ULONG unicode_name_buffer_length); +UINT _fx_unicode_directory_entry_read(FX_MEDIA *media_ptr, FX_DIR_ENTRY *source_dir, + ULONG *entry_ptr, FX_DIR_ENTRY *destination_ptr, + UCHAR *unicode_name, ULONG *unicode_size); +UINT _fx_unicode_directory_entry_change(FX_MEDIA *media_ptr, FX_DIR_ENTRY *entry_ptr, UCHAR *unicode_name, ULONG unicode_name_length); + + +#endif + diff --git a/common/inc/fx_user_sample.h b/common/inc/fx_user_sample.h new file mode 100644 index 0000000..6f42151 --- /dev/null +++ b/common/inc/fx_user_sample.h @@ -0,0 +1,236 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** User Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* fx_user.h PORTABLE C */ +/* 6.1.5 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains user defines for configuring FileX in specific */ +/* ways. This file will have an effect only if the application and */ +/* FileX library are built with FX_INCLUDE_USER_DEFINE_FILE defined. */ +/* Note that all the defines in this file may also be made on the */ +/* command line when building FileX library and application objects. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), and */ +/* added product constants */ +/* to enable code */ +/* size optimization, */ +/* resulting in version 6.1 */ +/* 03-02-2021 William E. Lamie Modified comment(s), and */ +/* added standalone support, */ +/* resulting in version 6.1.5 */ +/* */ +/**************************************************************************/ + +#ifndef FX_USER_H +#define FX_USER_H + + +/* Define various build options for the FileX port. The application should either make changes + here by commenting or un-commenting the conditional compilation defined OR supply the defines + though the compiler's equivalent of the -D option. */ + + +/* Override various options with default values already assigned in fx_api.h or fx_port.h. Please + also refer to fx_port.h for descriptions on each of these options. */ + + +/* Defines the maximum size of long file names supported by FileX. The default value is 33. The + minimum value is 13 and the maximum value is 256. */ + +/*#define FX_MAX_LONG_NAME_LEN 256 */ +/*#define FX_MAX_LAST_NAME_LEN 256 */ /* Must be as large or larger than FX_MAX_LONG_NAME_LEN */ + + +/* Defines the maximum number of logical sectors that can be cached by FileX. The cache memory + supplied to FileX at fx_media_open determines how many sectors can actually be cached. */ + +/*#define FX_MAX_SECTOR_CACHE 256 */ /* Minimum value is 2, all other values must be power of 2. */ + + +/* Defines the size in bytes of the bit map used to update the secondary FAT sectors. The larger the value the + less unnecessary secondary FAT sector writes. */ + +/*#define FX_FAT_MAP_SIZE 128 */ /* Minimum value is 1, no maximum value. */ + + +/* Defines the number of entries in the FAT cache. */ + +/*#define FX_MAX_FAT_CACHE 16 */ /* Minimum value is 8, all values must be a power of 2. */ + + +/* Defines the number of seconds the time parameters are updated in FileX. */ + +/*#define FX_UPDATE_RATE_IN_SECONDS 10 */ + + +/* Defines the number of ThreadX timer ticks required to achieve the update rate specified by + FX_UPDATE_RATE_IN_SECONDS defined previously. By default, the ThreadX timer tick is 10ms, + so the default value for this constant is 1000. */ + +/*#define FX_UPDATE_RATE_IN_TICKS 1000 */ + + +/* Defined, FileX is built without update to the time parameters. */ + +/*#define FX_NO_TIMER */ + + +/* Defined, FileX does not update already opened files. */ + +/*#define FX_DONT_UPDATE_OPEN_FILES */ + + +/* Defined, the file search cache optimization is disabled. */ + +/*#define FX_MEDIA_DISABLE_SEARCH_CACHE */ + + +/* Defined, the direct read sector update of cache is disabled. */ + +/*#define FX_DISABLE_DIRECT_DATA_READ_CACHE_FILL */ + + +/* Defined, gathering of media statistics is disabled. */ + +/*#define FX_MEDIA_STATISTICS_DISABLE */ + + +/* Defined, legacy single open logic for the same file is enabled. */ + +/*#define FX_SINGLE_OPEN_LEGACY */ + + +/* Defined, renaming inherits path information. */ + +/*#define FX_RENAME_PATH_INHERIT */ + + +/* Defined, local path logic is disabled. */ + +/*#define FX_NO_LOCAL_PATH */ + + +/* Define FileX internal protection macros. If FX_SINGLE_THREAD is defined, + these protection macros are effectively disabled. However, for multi-thread + uses, the macros are setup to utilize a ThreadX mutex for multiple thread + access control into an open media. */ + +/* #define FX_SINGLE_THREAD */ + + +/* Defined, Filex will be used in standalone mode (without ThreadX) */ + +/* #define FX_STANDALONE_ENABLE */ + + +/* Defined, data sector write requests are flushed immediately to the driver. */ + +/*#define FX_FAULT_TOLERANT_DATA */ + + +/* Defined, system sector write requests (including FAT and directory entry requests) + are flushed immediately to the driver. */ + +/*#define FX_FAULT_TOLERANT */ + + +/* Defined, enables 64-bits sector addresses used in I/O driver. */ + +/*#define FX_DRIVER_USE_64BIT_LBA */ + + +/* Defined, enables FileX fault tolerant service. */ + +/*#define FX_ENABLE_FAULT_TOLERANT */ + + +/* Define byte offset in boot sector where the cluster number of the Fault Tolerant Log file is. + Note that this field (byte 116 to 119) is marked as reserved by FAT 12/16/32 specification. */ + +/*#define FX_FAULT_TOLERANT_BOOT_INDEX 116 */ + +/* Below FX_DISABLE_XXX macros can be used for code size optimization required for memory + critical aplications */ + +/* Defined, error checking is disabled. */ + +/*#define FX_DISABLE_ERROR_CHECKING */ + + +/* Defined, cache is disabled. */ + +/*#define FX_DISABLE_CACHE */ + + +/* Defined, file close is disabled. */ + +/*#define FX_DISABLE_FILE_CLOSE */ + + +/* Defined, fast open is disabled. */ + +/*#define FX_DISABLE_FAST_OPEN */ + + +/* Defined, force memory operations are disabled. */ + +/*#define FX_DISABLE_FORCE_MEMORY_OPERATION */ + + +/* Defined, build options is disabled. */ + +/*#define FX_DISABLE_BUILD_OPTIONS */ + + +/* Defined, one line function is disabled. */ + +/*#define FX_DISABLE_ONE_LINE_FUNCTION */ + + +/* Defined, FAT entry refresh is disabled. */ + +/*#define FX_DIABLE_FAT_ENTRY_REFRESH */ + + +/* Defined, consecutive detect is disabled. */ + +/*#define FX_DISABLE_CONSECUTIVE_DETECT */ + + +#endif + diff --git a/common/inc/fx_utility.h b/common/inc/fx_utility.h new file mode 100644 index 0000000..8f09bbc --- /dev/null +++ b/common/inc/fx_utility.h @@ -0,0 +1,82 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* fx_utility.h PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file defines the FileX Utility component constants, data */ +/* definitions, and external references. It is assumed that fx_api.h */ +/* (and fx_port.h) have already been included. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ + +#ifndef FX_UTILITY_H +#define FX_UTILITY_H + + +/* Define the internal Utility component function prototypes. */ + +UINT _fx_utility_16_unsigned_read(UCHAR *source_ptr); +VOID _fx_utility_16_unsigned_write(UCHAR *dest_ptr, UINT value); +ULONG _fx_utility_32_unsigned_read(UCHAR *source_ptr); +VOID _fx_utility_32_unsigned_write(UCHAR *dest_ptr, ULONG value); +ULONG64 _fx_utility_64_unsigned_read(UCHAR *source_ptr); +VOID _fx_utility_64_unsigned_write(UCHAR *dest_ptr, ULONG64 value); +VOID _fx_utility_memory_copy(UCHAR *source_ptr, UCHAR *dest_ptr, ULONG size); +VOID _fx_utility_memory_set(UCHAR *dest_ptr, UCHAR value, ULONG size); +FX_CACHED_SECTOR + *_fx_utility_logical_sector_cache_entry_read(FX_MEDIA *media_ptr, ULONG64 logical_sector, FX_CACHED_SECTOR **previous_cache_entry); +UINT _fx_utility_logical_sector_read(FX_MEDIA *media_ptr, ULONG64 logical_sector, + VOID *buffer_ptr, ULONG sectors, UCHAR sector_type); +UINT _fx_utility_logical_sector_write(FX_MEDIA *media_ptr, ULONG64 logical_sector, + VOID *buffer_ptr, ULONG sectors, UCHAR sector_type); +UINT _fx_utility_logical_sector_flush(FX_MEDIA *media_ptr, ULONG64 starting_sector, ULONG64 sectors, UINT invalidate); +UINT _fx_utility_FAT_entry_read(FX_MEDIA *media_ptr, ULONG cluster, ULONG *entry_ptr); +UINT _fx_utility_FAT_entry_write(FX_MEDIA *media_ptr, ULONG cluster, ULONG next_cluster); +UINT _fx_utility_FAT_flush(FX_MEDIA *media_ptr); +UINT _fx_utility_FAT_map_flush(FX_MEDIA *media_ptr); +ULONG _fx_utility_FAT_sector_get(FX_MEDIA *media_ptr, ULONG cluster); +UINT _fx_utility_string_length_get(CHAR *string, UINT max_length); + + + + +#endif + diff --git a/common/src/fx_directory_attributes_read.c b/common/src/fx_directory_attributes_read.c new file mode 100644 index 0000000..3b4dfa6 --- /dev/null +++ b/common/src/fx_directory_attributes_read.c @@ -0,0 +1,156 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_file.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_attributes_read PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function first attempts to find the specified directory. If */ +/* found, the attribute read request is valid and the directory entry */ +/* will be read in order to pickup the directory's attributes. */ +/* Otherwise, if the directory is not found, the appropriate error */ +/* code is returned to the caller. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* directory_name Directory name pointer */ +/* attributes_ptr Return attributes pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_search Search for the file name in */ +/* the directory structure */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_attributes_read(FX_MEDIA *media_ptr, CHAR *directory_name, UINT *attributes_ptr) +{ + +UINT status; +FX_DIR_ENTRY dir_entry; + +#ifdef TX_ENABLE_EVENT_TRACE +TX_TRACE_BUFFER_ENTRY *trace_event; +ULONG trace_timestamp; +#endif + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_directory_attributes_reads++; +#endif + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Clear the short name string. */ + dir_entry.fx_dir_entry_short_name[0] = 0; + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_DIRECTORY_ATTRIBUTES_READ, media_ptr, directory_name, 0, 0, FX_TRACE_DIRECTORY_EVENTS, &trace_event, &trace_timestamp) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Search the system for the supplied file name. */ + status = _fx_directory_search(media_ptr, directory_name, &dir_entry, FX_NULL, FX_NULL); + + /* Determine if the search was successful. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + /* Check to make sure the found entry is a file. */ + if ((dir_entry.fx_dir_entry_attributes & (UCHAR)(FX_DIRECTORY)) == 0) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the not a file error code. */ + return(FX_NOT_DIRECTORY); + } + + /* Place the current attributes into the destination. */ + *attributes_ptr = (UINT)dir_entry.fx_dir_entry_attributes; + + /* Update the trace event with the attributes read. */ + FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_DIRECTORY_ATTRIBUTES_READ, 0, 0, dir_entry.fx_dir_entry_attributes, 0) + + /* Release media protection. */ + FX_UNPROTECT + + /* File attribute read is complete, return successful status. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_directory_attributes_set.c b/common/src/fx_directory_attributes_set.c new file mode 100644 index 0000000..b71e771 --- /dev/null +++ b/common/src/fx_directory_attributes_set.c @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_file.h" +#include "fx_utility.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_attributes_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function first attempts to find the specified directory. If */ +/* found, the attribute set request is valid and the directory will */ +/* be modified with the new attributes. Otherwise, if the directory */ +/* is not found, the appropriate error code is returned to the caller. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* directory_name Directory name pointer */ +/* attributes New dir attributes */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_entry_write Write the new directory entry */ +/* _fx_directory_search Search for the file name in */ +/* the directory structure */ +/* _fx_fault_tolerant_transaction_start Start fault tolerant */ +/* transaction */ +/* _fx_fault_tolerant_transaction_end End fault tolerant transaction*/ +/* _fx_fault_tolerant_recover Recover FAT chain */ +/* _fx_fault_tolerant_reset_log_file Reset the log file */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_attributes_set(FX_MEDIA *media_ptr, CHAR *directory_name, UINT attributes) +{ + +UINT status; +FX_DIR_ENTRY dir_entry; + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_directory_attributes_sets++; +#endif + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Clear the short name string. */ + dir_entry.fx_dir_entry_short_name[0] = 0; + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_DIRECTORY_ATTRIBUTES_SET, media_ptr, directory_name, attributes, 0, FX_TRACE_DIRECTORY_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Check for write protect at the media level (set by driver). */ + if (media_ptr -> fx_media_driver_write_protect) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return write protect error. */ + return(FX_WRITE_PROTECT); + } + + /* Search the system for the supplied file name. */ + status = _fx_directory_search(media_ptr, directory_name, &dir_entry, FX_NULL, FX_NULL); + + /* Determine if the search was successful. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + /* Check to make sure the found entry is a directory. */ + if ((dir_entry.fx_dir_entry_attributes & (UCHAR)(FX_DIRECTORY)) == 0) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the not a file error code. */ + return(FX_NOT_DIRECTORY); + } + + /* Place the new attributes in the directory entry. Make sure that the + directory bit of the attributes is never changed. */ + dir_entry.fx_dir_entry_attributes = (UCHAR)(attributes | FX_DIRECTORY); + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Start transaction. */ + _fx_fault_tolerant_transaction_start(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Now write out the directory entry. */ + + status = _fx_directory_entry_write(media_ptr, &dir_entry); + + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Check for a bad status. If the return status is not FX_SUCCESS, finish + the fault tolerant transaction without flushing the logs. */ + if (status != FX_SUCCESS) + { + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + } + else + { + + /* End transaction. */ + status = _fx_fault_tolerant_transaction_end(media_ptr); + } + +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* File attribute set is complete, return status. */ + return(status); +} + diff --git a/common/src/fx_directory_create.c b/common/src/fx_directory_create.c new file mode 100644 index 0000000..178a39a --- /dev/null +++ b/common/src/fx_directory_create.c @@ -0,0 +1,711 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_file.h" +#include "fx_utility.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_create PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function first attempts to find the specified directory. */ +/* If found, the create request is invalid and an error is returned */ +/* to the caller. After the file name verification is made, a search */ +/* for a free directory entry will be made. If nothing is available, */ +/* an error will be returned to the caller. Otherwise, if all is */ +/* okay, an empty directory will be created. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* directory_name Directory name */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_entry_write Write the new directory entry */ +/* _fx_directory_name_extract Pickup next part of name */ +/* _fx_directory_search Search for the file name in */ +/* the directory structure */ +/* _fx_directory_free_search Search for a free directory */ +/* entry */ +/* _fx_utility_FAT_flush Flush written FAT entries */ +/* _fx_utility_FAT_entry_read Read a FAT entry */ +/* _fx_utility_FAT_entry_write Write a FAT entry */ +/* _fx_utility_logical_sector_flush Flush the written log sector */ +/* _fx_utility_logical_sector_read Read logical sector */ +/* _fx_fault_tolerant_transaction_start Start fault tolerant */ +/* transaction */ +/* _fx_fault_tolerant_transaction_end End fault tolerant transaction*/ +/* _fx_fault_tolerant_recover Recover FAT chain */ +/* _fx_fault_tolerant_reset_log_file Reset the log file */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_create(FX_MEDIA *media_ptr, CHAR *directory_name) +{ + +UINT status; +ULONG FAT_index; +ULONG FAT_value; +UINT sectors; +ULONG total_clusters; +CHAR *work_ptr; +ULONG64 logical_sector; +ULONG i; +FX_DIR_ENTRY dir_entry; +FX_DIR_ENTRY sub_dir_entry; +FX_DIR_ENTRY search_directory; + +FX_INT_SAVE_AREA + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_directory_creates++; +#endif + + /* Determine if the supplied name is less than the maximum supported name size. The + maximum name (FX_MAX_LONG_NAME_LEN) is defined in fx_api.h. */ + i = 0; + work_ptr = (CHAR *)directory_name; + while (*work_ptr && (i < FX_MAX_LONG_NAME_LEN)) + { + + /* Determine if the character designates a new path. */ + if ((*work_ptr == '\\') || (*work_ptr == '/')) + { + /* Yes, reset the name size. */ + i = 0; + } + /* Check for leading spaces. */ + else if ((*work_ptr != ' ') || (i != 0)) + { + + /* No leading spaces, increment the name size. */ + i++; + } + + /* Move to the next character. */ + work_ptr++; + } + + /* Determine if the supplied name is valid. */ + if ((i == 0) || (i >= FX_MAX_LONG_NAME_LEN)) + { + + /* Return an invalid name value. */ + return(FX_INVALID_NAME); + } + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Setup search directory pointer to another media name buffer. */ + search_directory.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN * 2; + + /* Clear the short name string. */ + dir_entry.fx_dir_entry_short_name[0] = 0; + + /* Clear the search short name string. */ + search_directory.fx_dir_entry_short_name[0] = 0; + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_DIRECTORY_CREATE, media_ptr, directory_name, 0, 0, FX_TRACE_DIRECTORY_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Check for write protect at the media level (set by driver). */ + if (media_ptr -> fx_media_driver_write_protect) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return write protect error. */ + return(FX_WRITE_PROTECT); + } + + /* Make sure there is at least one cluster remaining for the new file. */ + if (!media_ptr -> fx_media_available_clusters) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return a no-space error. */ + return(FX_NO_MORE_SPACE); + } + + /* Search the system for the supplied directory name. */ + status = _fx_directory_search(media_ptr, directory_name, &dir_entry, &search_directory, &work_ptr); + + /* Determine if the search was successful. */ + if (status == FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Directory found - Return the error code. */ + return(FX_ALREADY_CREATED); + } + + /* Determine if there is anything left after the name. */ + if (_fx_directory_name_extract(work_ptr, &dir_entry.fx_dir_entry_name[0])) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Extra information after the file name, return an invalid name + error. */ + return(FX_INVALID_PATH); + } + + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Start transaction. */ + _fx_fault_tolerant_transaction_start(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Find a free slot for the new directory. */ + status = _fx_directory_free_search(media_ptr, &search_directory, &dir_entry); + + /* Determine if the search was successful. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + /* Now allocate a cluster for our new sub-directory entry. */ + + FAT_index = media_ptr -> fx_media_cluster_search_start; + total_clusters = media_ptr -> fx_media_total_clusters; + + /* Loop to find the first available cluster. */ + do + { + + /* Make sure we don't go past the FAT table. */ + if (!total_clusters) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Something is wrong with the media - the desired clusters were + not found in the FAT table. */ + return(FX_NO_MORE_SPACE); + } + + /* Read FAT entry. */ + status = _fx_utility_FAT_entry_read(media_ptr, FAT_index, &FAT_value); + + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + /* Decrement the total cluster count. */ + total_clusters--; + + /* Determine if the FAT entry is free. */ + if (FAT_value == FX_FREE_CLUSTER) + { + + /* Move cluster search pointer forward. */ + media_ptr -> fx_media_cluster_search_start = FAT_index + 1; + + /* Determine if this needs to be wrapped. */ + if (media_ptr -> fx_media_cluster_search_start >= (media_ptr -> fx_media_total_clusters + FX_FAT_ENTRY_START)) + { + + /* Wrap the search to the beginning FAT entry. */ + media_ptr -> fx_media_cluster_search_start = FX_FAT_ENTRY_START; + } + + /* Break this loop. */ + break; + } + else + { + + /* FAT entry is not free... Advance the FAT index. */ + FAT_index++; + + /* Determine if we need to wrap the FAT index around. */ + if (FAT_index >= (media_ptr -> fx_media_total_clusters + FX_FAT_ENTRY_START)) + { + + /* Wrap the search to the beginning FAT entry. */ + FAT_index = FX_FAT_ENTRY_START; + } + } + } while (FX_TRUE); + + /* Decrease the number of available clusters for the media. */ + media_ptr -> fx_media_available_clusters--; + + /* Defer the writing of the FAT entry until the directory's first sector + has been properly written. If a power loss occurs prior to writing + the FAT entry, it will not result in a lost cluster. */ + + /* Populate the directory entry. */ + + /* Isolate the file name. */ + _fx_directory_name_extract(work_ptr, &dir_entry.fx_dir_entry_name[0]); + + /* Lockout interrupts for time/date access. */ + FX_DISABLE_INTS + + /* Set time and date stamps. */ + dir_entry.fx_dir_entry_time = _fx_system_time; + dir_entry.fx_dir_entry_date = _fx_system_date; + + /* Restore interrupts. */ + FX_RESTORE_INTS + + /* Set the attributes for the file. */ + dir_entry.fx_dir_entry_attributes = FX_DIRECTORY; + + + /* Set file size to 0. */ + dir_entry.fx_dir_entry_file_size = 0; + + + /* Set the cluster to EOF. */ + dir_entry.fx_dir_entry_cluster = FAT_index; + + /* Is there a leading dot? */ + if (dir_entry.fx_dir_entry_name[0] == '.') + { + + /* Yes, toggle the hidden attribute bit. */ + dir_entry.fx_dir_entry_attributes |= FX_HIDDEN; + } + + /* In previous versions, the new directory was written here. It + is now at the bottom of the file - after the FAT and the initial + sub-directory is written out. This makes the directory create + more fault tolerant. */ + + /* Calculate the first sector of the sub-directory file. */ + logical_sector = ((ULONG) media_ptr -> fx_media_data_sector_start) + + (((ULONG64) FAT_index - FX_FAT_ENTRY_START) * + ((ULONG) media_ptr -> fx_media_sectors_per_cluster)); + + /* Pickup the number of sectors for the initial sub-directory cluster. */ + sectors = media_ptr -> fx_media_sectors_per_cluster; + + /* Read the first logical sector associated with the allocated + cluster. */ + status = _fx_utility_logical_sector_read(media_ptr, logical_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR); + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return error code. */ + return(status); + } + + /* Clear the entire first sector of the new sub-directory cluster. */ + work_ptr = (CHAR *)media_ptr -> fx_media_memory_buffer; + i = 0; + while (i < media_ptr -> fx_media_bytes_per_sector) + { + + /* Clear 4 bytes. */ + *((ULONG *)work_ptr) = (ULONG)0; + + /* Increment pointer. */ + work_ptr = work_ptr + sizeof(ULONG); + + /* Increment counter. */ + i = i + (ULONG)sizeof(ULONG); + } + +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled == FX_TRUE) + { + + /* Write back. */ + status = _fx_utility_logical_sector_write(media_ptr, logical_sector, + media_ptr -> fx_media_memory_buffer, (ULONG)1, FX_DIRECTORY_SECTOR); + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return error code. */ + return(status); + } + + /* Force flush the internal logical sector cache. */ + status = _fx_utility_logical_sector_flush(media_ptr, logical_sector, sectors, FX_TRUE); + + /* Determine if the write was successful. */ + if (status != FX_SUCCESS) + { + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Determine if there are more sectors to clear in the first cluster of the new + sub-directory. */ + if (sectors > 1) + { + + /* Yes, invalidate all cached sectors that are contained in the newly allocated first + cluster of the directory. */ + +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled == FX_FALSE) +#endif /* FX_ENABLE_FAULT_TOLERANT */ + { + + /* Flush the internal logical sector cache. */ + status = _fx_utility_logical_sector_flush(media_ptr, logical_sector + 1, ((ULONG64)(sectors - 1)), FX_TRUE); + + /* Determine if the write was successful. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + } + + /* Clear all additional sectors of new sub-directory. */ + sectors--; + while (sectors) + { + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of driver write sector(s) requests. */ + media_ptr -> fx_media_driver_write_requests++; +#endif + + /* Build Write request to the driver. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_WRITE; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = media_ptr -> fx_media_memory_buffer; +#ifdef FX_DRIVER_USE_64BIT_LBA + media_ptr -> fx_media_driver_logical_sector = logical_sector + ((ULONG)sectors); +#else + media_ptr -> fx_media_driver_logical_sector = (ULONG)logical_sector + ((ULONG)sectors); +#endif + media_ptr -> fx_media_driver_sectors = 1; + media_ptr -> fx_media_driver_sector_type = FX_DIRECTORY_SECTOR; + + /* Set the system write flag since we are writing a directory sector. */ + media_ptr -> fx_media_driver_system_write = FX_TRUE; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, ((ULONG)logical_sector) + ((ULONG)sectors), 1, media_ptr -> fx_media_memory_buffer, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Invoke the driver to write the sector. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Clear the system write flag. */ + media_ptr -> fx_media_driver_system_write = FX_FALSE; + + /* Determine if an error occurred. */ + if (media_ptr -> fx_media_driver_status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return error code. */ + return(media_ptr -> fx_media_driver_status); + } + + /* Decrease the number of sectors to clear. */ + sectors--; + } + } + + + /* Now setup the first sector with the initial sub-directory information. */ + + /* Copy the base directory entry to the sub-directory entry. */ + sub_dir_entry = dir_entry; + + /* Setup pointer to media name buffer. */ + sub_dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + (FX_MAX_LONG_NAME_LEN * 3); + + /* Set the directory entry name to all blanks. */ + work_ptr = &sub_dir_entry.fx_dir_entry_name[0]; + for (i = 0; i < (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE); i++) + { + *work_ptr++ = ' '; + } + + sub_dir_entry.fx_dir_entry_long_name_present = 0; + + /* Now build the "." directory entry. */ + sub_dir_entry.fx_dir_entry_name[0] = '.'; + sub_dir_entry.fx_dir_entry_name[1] = 0; + sub_dir_entry.fx_dir_entry_log_sector = logical_sector; + sub_dir_entry.fx_dir_entry_byte_offset = 0; + + /* Write the directory's first entry. */ + status = _fx_directory_entry_write(media_ptr, &sub_dir_entry); + + /* Check for error condition. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return error status. */ + return(status); + } + + /* Now build the ".." directory entry. */ + + /* Determine if the search directory is the root. */ + if (search_directory.fx_dir_entry_name[0]) + { + + /* Previous directory is not the root directory. */ + + /* Copy into the working directory entry. */ + sub_dir_entry = search_directory; + + /* Copy the date and time from the actual sub-directory. */ + sub_dir_entry.fx_dir_entry_time = dir_entry.fx_dir_entry_time; + sub_dir_entry.fx_dir_entry_date = dir_entry.fx_dir_entry_date; + + /* Adjust pointer to media name buffer. */ + sub_dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + (FX_MAX_LONG_NAME_LEN * 3); + + /* Change the name to ".." */ + work_ptr = &sub_dir_entry.fx_dir_entry_name[0]; + for (i = 0; i < (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE); i++) + { + *work_ptr++ = ' '; + } + + sub_dir_entry.fx_dir_entry_name[0] = '.'; + sub_dir_entry.fx_dir_entry_name[1] = '.'; + sub_dir_entry.fx_dir_entry_name[2] = 0; + + sub_dir_entry.fx_dir_entry_long_name_present = 0; + + /* Set file size to 0. */ + sub_dir_entry.fx_dir_entry_file_size = 0; + + /* Change the logical sector for this entry. */ + sub_dir_entry.fx_dir_entry_log_sector = logical_sector; + } + else + { + + /* Just modify the current directory since the parent + directory is the root. */ + sub_dir_entry.fx_dir_entry_name[1] = '.'; + sub_dir_entry.fx_dir_entry_name[2] = 0; + + /* Clear the cluster to indicate the root directory. */ + sub_dir_entry.fx_dir_entry_cluster = 0; + } + + /* Setup the byte offset. */ + sub_dir_entry.fx_dir_entry_byte_offset = FX_DIR_ENTRY_SIZE; + + /* Write the directory's second entry. */ + status = _fx_directory_entry_write(media_ptr, &sub_dir_entry); + + /* Check for error condition. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return error status. */ + return(status); + } + + /* Write an EOF in the found FAT entry. */ + status = _fx_utility_FAT_entry_write(media_ptr, FAT_index, media_ptr -> fx_media_fat_last); + + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + +#ifdef FX_FAULT_TOLERANT + + /* Flush the cached individual FAT entries */ + _fx_utility_FAT_flush(media_ptr); +#endif + + /* Now write out the new directory entry. */ + + status = _fx_directory_entry_write(media_ptr, &dir_entry); + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + /* End transaction. */ + status = _fx_fault_tolerant_transaction_end(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Directory create is complete, return status. */ + return(status); +} + diff --git a/common/src/fx_directory_default_get.c b/common/src/fx_directory_default_get.c new file mode 100644 index 0000000..bc7c224 --- /dev/null +++ b/common/src/fx_directory_default_get.c @@ -0,0 +1,106 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_default_get PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the pointer of the last path provided to the */ +/* fx_directory_default_set function. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* return_path_name Destination string pointer */ +/* address */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_default_get(FX_MEDIA *media_ptr, CHAR **return_path_name) +{ + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_directory_default_gets++; +#endif + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_DIRECTORY_DEFAULT_GET, media_ptr, return_path_name, 0, 0, FX_TRACE_DIRECTORY_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Return the last pointer supplied to the set default directory function. */ + *return_path_name = media_ptr -> fx_media_default_path.fx_path_string; + + /* Release media protection. */ + FX_UNPROTECT + + /* Return successful status. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_directory_default_get_copy.c b/common/src/fx_directory_default_get_copy.c new file mode 100644 index 0000000..fe41c10 --- /dev/null +++ b/common/src/fx_directory_default_get_copy.c @@ -0,0 +1,114 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" +#include "fx_utility.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_default_get_copy PORTABLE C */ +/* 6.1.6 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function copies the last path provided to the */ +/* fx_directory_default_set function into the specified buffer. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* return_path_name_buffer Destination buffer for name */ +/* return_path_name_buffer_size Size of buffer pointed to by */ +/* return_path_name_buffer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), verified */ +/* memcpy usage, */ +/* resulting in version 6.1 */ +/* 04-02-2021 William E. Lamie Modified comment(s), verified */ +/* memcpy usage, */ +/* resulting in version 6.1.6 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_default_get_copy(FX_MEDIA *media_ptr, CHAR *return_path_name_buffer, UINT return_path_name_buffer_size) +{ + +UINT status; +CHAR *return_path_name; +UINT path_name_length_with_null_terminator; + + + /* Get the pointer to the path. */ + status = _fx_directory_default_get(media_ptr, &return_path_name); + if (status == FX_SUCCESS) + { + + /* Get the length of the path. */ + path_name_length_with_null_terminator = _fx_utility_string_length_get(return_path_name, FX_MAXIMUM_PATH) + 1; + + /* Can it fit in the user's buffer? */ + if (path_name_length_with_null_terminator <= return_path_name_buffer_size) + { + + /* Copy the path name into the user's buffer. */ + _fx_utility_memory_copy((UCHAR *) return_path_name, (UCHAR *) return_path_name_buffer, path_name_length_with_null_terminator); /* Use case of memcpy is verified. */ + } + else + { + + /* Buffer is too small. Return error. */ + return(FX_BUFFER_ERROR); + } + + } + + /* Return successful status. */ + return(status); +} + diff --git a/common/src/fx_directory_default_set.c b/common/src/fx_directory_default_set.c new file mode 100644 index 0000000..49df3d2 --- /dev/null +++ b/common/src/fx_directory_default_set.c @@ -0,0 +1,422 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_file.h" +#include "fx_utility.h" +#include "fx_directory.h" + +#ifndef FX_NO_LOCAL_PATH +FX_LOCAL_PATH_SETUP +#endif + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_default_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the default directory of the media to the path */ +/* specified by the caller. If this path is not found, an error code */ +/* is returned. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* new_path_name New path to set current */ +/* working directory to */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_search Search for the directory name */ +/* in the directory structure */ +/* _fx_utility_absolute_path_get Get absolute path */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_default_set(FX_MEDIA *media_ptr, CHAR *new_path_name) +{ + +UINT status; +FX_DIR_ENTRY dir_entry; +CHAR *path_string_ptr; +UINT path_string_capacity; +FX_PATH *path_ptr; +UINT i; + + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_directory_default_sets++; +#endif + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Clear the short name string. */ + dir_entry.fx_dir_entry_short_name[0] = 0; + + /* Clear the long name string. */ + dir_entry.fx_dir_entry_name[0] = (CHAR)0; + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_DIRECTORY_DEFAULT_SET, media_ptr, new_path_name, 0, 0, FX_TRACE_DIRECTORY_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Setup the path string pointer to the start of the current path. */ + path_string_ptr = &(media_ptr -> fx_media_default_path.fx_path_string[0]); + + + /* Look for a root directory selection. */ + if ((!new_path_name) || (((new_path_name[0] == '\\') || (new_path_name[0] == '/')) && (new_path_name[1] == (CHAR)0))) + { + + /* Set the media current working directory to the root. */ + media_ptr -> fx_media_default_path.fx_path_directory.fx_dir_entry_name[0] = (CHAR) 0; + media_ptr -> fx_media_default_path.fx_path_string[0] = (CHAR) 0; + media_ptr -> fx_media_default_path.fx_path_string[FX_MAXIMUM_PATH - 2] = (CHAR) 0; + } + else + { + + /* Search the system for the supplied path and directory name. */ + status = _fx_directory_search(media_ptr, new_path_name, &dir_entry, FX_NULL, FX_NULL); + + /* Determine if the search failed or if the entry found is not a + directory. */ + if ((status != FX_SUCCESS) || (!(dir_entry.fx_dir_entry_attributes & FX_DIRECTORY))) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Invalid Path - Return the error code. */ + return(FX_INVALID_PATH); + } + + /* Now update the current path string. */ + + /* Setup the path string's capacity. */ + path_string_capacity = FX_MAXIMUM_PATH - 1; + + /* Determine if the new path is relative from the current path. */ + if ((new_path_name[0] != '\\') && (new_path_name[0] != '/')) + { + + /* Yes, a relative path was found. */ + + /* First check for a local path pointer stored in the thread control block. + This is only available in ThreadX Version 4 and above. */ +#ifndef FX_NO_LOCAL_PATH + if (_tx_thread_current_ptr -> tx_thread_filex_ptr) + { + + CHAR *saved_name_ptr; + + /* First, save the name pointer of the default path. */ + saved_name_ptr = media_ptr -> fx_media_default_path.fx_path_directory.fx_dir_entry_name; + + /* Setup the default path pointer to the local path. */ + path_ptr = (FX_PATH *)_tx_thread_current_ptr -> tx_thread_filex_ptr; + + /* Copy the local path to the default path. */ + media_ptr -> fx_media_default_path = *path_ptr; + + /* Restore the name pointer of the default path. */ + media_ptr -> fx_media_default_path.fx_path_directory.fx_dir_entry_name = saved_name_ptr; + + /* Set the path pointer to the default pointer. */ + path_ptr = &media_ptr -> fx_media_default_path; + } + else + { + + /* Setup the default path to the global media path. */ + path_ptr = &media_ptr -> fx_media_default_path; + } +#else + + /* Setup the default path to the global media path. */ + path_ptr = &media_ptr -> fx_media_default_path; +#endif + + /* First, check the current path for string overflow. If this is set, + don't attempt to update the current path with relative information. + The path won't be valid again until a complete path is given. */ + if (path_ptr -> fx_path_string[FX_MAXIMUM_PATH - 2] == '*') + { + + /* Yes, don't update the string, just finish the path set processing. */ + + /* Determine if we are at the root directory. */ + if (!dir_entry.fx_dir_entry_cluster) + { + /* Set the current directory back to the root directory. */ + path_ptr -> fx_path_directory.fx_dir_entry_name[0] = (CHAR) 0; + + /* Set name to NULL. */ + dir_entry.fx_dir_entry_name[0] = (CHAR) 0; + + /* Clear the current path string. */ + path_ptr -> fx_path_string[0] = (CHAR)0; + + /* Clear the overflow flag in the current path string... just in + case! */ + path_ptr -> fx_path_string[FX_MAXIMUM_PATH - 2] = (CHAR)0; + } + + /* Copy the new directory entry into the media control block. */ + path_ptr -> fx_path_directory = dir_entry; + + /* Copy the name into the path name buffer. */ + for (i = 0; dir_entry.fx_dir_entry_name[i]; i++) + { + path_ptr -> fx_path_name_buffer[i] = dir_entry.fx_dir_entry_name[i]; + } + + /* Reassign the pointer. */ + path_ptr -> fx_path_directory.fx_dir_entry_name = path_ptr -> fx_path_name_buffer; + + /* Release media protection. */ + FX_UNPROTECT + + /* Default directory set is complete, return status. */ + return(FX_SUCCESS); + } + + /* Move the current path starting pointer to the end of the current + path string. */ + while ((path_string_capacity) && (*path_string_ptr != FX_NULL)) + { + path_string_ptr++; + path_string_capacity--; + } + + /* If room, place the \ character in the path string. */ + if (path_string_capacity) + { + + /* There is room, place the directory marker in the string. */ + *path_string_ptr++ = '\\'; + path_string_capacity--; + } + } + else + { + + /* Setup the default path pointer. */ + + /* First check for a local path pointer stored in the thread control block. + This is only available in ThreadX Version 4 and above. */ +#ifndef FX_NO_LOCAL_PATH + if (_tx_thread_current_ptr -> tx_thread_filex_ptr) + { + + CHAR *saved_name_ptr; + + /* First, save the name pointer of the default path. */ + saved_name_ptr = media_ptr -> fx_media_default_path.fx_path_directory.fx_dir_entry_name; + + /* Setup the default path pointer to the local path. */ + path_ptr = (FX_PATH *)_tx_thread_current_ptr -> tx_thread_filex_ptr; + + /* Copy the local path to the default path. */ + media_ptr -> fx_media_default_path = *path_ptr; + + /* Restore the name pointer of the default path. */ + media_ptr -> fx_media_default_path.fx_path_directory.fx_dir_entry_name = saved_name_ptr; + + /* Set the path pointer to the default pointer. */ + path_ptr = &media_ptr -> fx_media_default_path; + } + else + { + + /* Setup the default path to the global media path. */ + path_ptr = &media_ptr -> fx_media_default_path; + } +#else + + /* Setup the default path to the global media path. */ + path_ptr = &media_ptr -> fx_media_default_path; +#endif + + /* Complete path name given. Check to see if we need to clear an + overflow character from a previous current path string update. */ + if (path_ptr -> fx_path_string[FX_MAXIMUM_PATH - 2] == '*') + { + path_ptr -> fx_path_string[FX_MAXIMUM_PATH - 2] = (CHAR)0; + } + } + + /* Copy what we can into the current path. */ + while (*new_path_name) + { + + /* Determine if there is a ".." character sequence that specifies the + previous path. */ + if ((*new_path_name == '.') && (*(new_path_name + 1) == '.')) + { + + /* Yes, a backward path is found. The current path pointer + must be moved back to just after the previous \ character. */ + + /* Skip the current \0 that is at the end of the current path. */ + path_string_capacity = path_string_capacity + 2; + path_string_ptr = path_string_ptr - 2; + + while (path_string_capacity <= (FX_MAXIMUM_PATH - 1)) + { + + /* Move the current path pointer backwards until + a \ character is found. */ + + if ((*path_string_ptr == '\\') || (*path_string_ptr == '/')) + { + + /* Yes, we have successfully backed up one directory. */ + break; + } + + /* Backup another character. */ + path_string_capacity++; + path_string_ptr--; + } + + /* Adjust the new directory pointer past the .. characters */ + new_path_name = new_path_name + 2; + } + else + { + + /* Normal characters that need to be copied into the current path. */ + + if (path_string_capacity) + { + + /* Copy character from the new path into the current path string. */ + *path_string_ptr++ = *new_path_name++; + + path_string_capacity--; + } + else + { + + /* No more room in the current path string! */ + break; + } + } + } + + /* Determine if there is still room in the current path string. */ + if (path_string_capacity) + { + + /* Yes, there is still room, place a NULL character at the + end of the path. */ + *path_string_ptr = (CHAR)FX_NULL; + } + else + { + + /* No more room. Determine if the entire path was successfully + copied into the current path. */ + if (*new_path_name) + { + + /* No, we couldn't fit the entire path. Place a "*" character + at the end to indicate that we had overflow. Note that + the path is kept just the the directory default get call, so + the new default path is valid. */ + path_ptr -> fx_path_string[FX_MAXIMUM_PATH - 2] = '*'; + } + } + + /* Determine if we are at the root directory. */ + if (!dir_entry.fx_dir_entry_cluster) + { + + /* Set the current directory back to the root directory. */ + dir_entry.fx_dir_entry_name[0] = (CHAR)0; + path_ptr -> fx_path_name_buffer[0] = (CHAR)0; + } + + /* Copy the new directory entry into the media control block. */ + path_ptr -> fx_path_directory = dir_entry; + + /* Copy the name. */ + for (i = 0; dir_entry.fx_dir_entry_name[i]; i++) + { + path_ptr -> fx_path_name_buffer[i] = dir_entry.fx_dir_entry_name[i]; + } + + /* Reassign the pointer. */ + path_ptr -> fx_path_directory.fx_dir_entry_name = path_ptr -> fx_path_name_buffer; + } + + /* Release media protection. */ + FX_UNPROTECT + + /* Default directory set is complete, return status. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_directory_delete.c b/common/src/fx_directory_delete.c new file mode 100644 index 0000000..d1b7ae8 --- /dev/null +++ b/common/src/fx_directory_delete.c @@ -0,0 +1,451 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_file.h" +#include "fx_utility.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_delete PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function first attempts to find the specified directory. */ +/* If found, the directory is examined to make sure it is empty. If */ +/* the directory is not empty, an error code is returned to the */ +/* caller. Otherwise, the directory will be deleted and its clusters */ +/* will be made available. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* directory_name Directory name to delete */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_entry_read Read a directory entry */ +/* _fx_directory_entry_write Write the new directory entry */ +/* _fx_directory_search Search for the file name in */ +/* the directory structure */ +/* _fx_utility_logical_sector_flush Flush the written log sector */ +/* _fx_utility_FAT_entry_read Read FAT entries to calculate */ +/* the sub-directory size */ +/* _fx_utility_FAT_entry_write Write FAT entry */ +/* _fx_utility_FAT_flush Flush FAT cache */ +/* _fx_fault_tolerant_transaction_start Start fault tolerant */ +/* transaction */ +/* _fx_fault_tolerant_transaction_end End fault tolerant transaction*/ +/* _fx_fault_tolerant_recover Recover FAT chain */ +/* _fx_fault_tolerant_reset_log_file Reset the log file */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_delete(FX_MEDIA *media_ptr, CHAR *directory_name) +{ + +UINT status; +ULONG cluster, next_cluster; +ULONG i, directory_size; +FX_DIR_ENTRY dir_entry; +FX_DIR_ENTRY search_directory; +FX_DIR_ENTRY search_entry; + + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_directory_deletes++; +#endif + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Setup search pointer to media name buffer. */ + search_directory.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN * 2; + + /* Setup search entry pointer to media name buffer. */ + search_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN * 3; + + /* Clear the short name string of the three file names that will be worked with. */ + dir_entry.fx_dir_entry_short_name[0] = 0; + search_directory.fx_dir_entry_short_name[0] = 0; + search_entry.fx_dir_entry_short_name[0] = 0; + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_DIRECTORY_DELETE, media_ptr, directory_name, 0, 0, FX_TRACE_DIRECTORY_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Start transaction. */ + _fx_fault_tolerant_transaction_start(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Check for write protect at the media level (set by driver). */ + if (media_ptr -> fx_media_driver_write_protect) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return write protect error. */ + return(FX_WRITE_PROTECT); + } + + /* Search the system for the supplied directory name. */ + status = _fx_directory_search(media_ptr, directory_name, &dir_entry, FX_NULL, FX_NULL); + + /* Determine if the search was successful. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + /* Check to make sure the found entry is a directory. */ + if (!(dir_entry.fx_dir_entry_attributes & (UCHAR)(FX_DIRECTORY))) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the not a directory error code. */ + return(FX_NOT_DIRECTORY); + } + + /* Check if the entry is read only */ + if (dir_entry.fx_dir_entry_attributes & (UCHAR)(FX_READ_ONLY)) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the not a directory error code. */ + return(FX_WRITE_PROTECT); + } + + /* Copy the directory entry to the search directory structure for + looking at the specified sub-directory contents. */ + search_directory = dir_entry; + + /* Ensure that the search directory's last search cluster is cleared. */ + search_directory.fx_dir_entry_last_search_cluster = 0; + + + /* Calculate the directory size by counting the allocated + clusters for it. */ + i = 0; + cluster = search_directory.fx_dir_entry_cluster; + while (cluster < media_ptr -> fx_media_fat_reserved) + { + + /* Increment the cluster count. */ + i++; + + /* Read the next FAT entry. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &next_cluster); + + /* Check the return status. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + if ((cluster < FX_FAT_ENTRY_START) || (cluster == next_cluster) || (i > media_ptr -> fx_media_total_clusters)) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(FX_FAT_READ_ERROR); + } + + cluster = next_cluster; + } + + /* Now we can calculate the directory size. */ + directory_size = (((ULONG)media_ptr -> fx_media_bytes_per_sector) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster) * i) / + (ULONG)FX_DIR_ENTRY_SIZE; + + /* Also save this in the directory entry so we don't have to + calculate it later. */ + search_directory.fx_dir_entry_file_size = directory_size; + + /* Make sure the new name is not in the current directory. */ + /* The first two entries are skipped because they are just part of the sub-directory. */ + i = 2; + + do + { + + /* Read an entry from the directory. */ + status = _fx_directory_entry_read(media_ptr, &search_directory, &i, &search_entry); + + /* Check for error status. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return error condition. */ + return(status); + } + + /* Determine if this is the last directory entry. */ + if (search_entry.fx_dir_entry_name[0] == FX_DIR_ENTRY_DONE) + { + break; + } + + + /* Determine if this is an empty entry. */ + if ((UCHAR)search_entry.fx_dir_entry_name[0] != (UCHAR)FX_DIR_ENTRY_FREE) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return error status. */ + return(FX_DIR_NOT_EMPTY); + } + + i++; + } while (i < directory_size); + + /* At this point, we are going to delete the empty directory. */ + +#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE + + /* Invalidate the directory search saved information. */ + media_ptr -> fx_media_last_found_name[0] = FX_NULL; +#endif + + /* Mark the sub-directory entry as available. */ + dir_entry.fx_dir_entry_name[0] = (CHAR)FX_DIR_ENTRY_FREE; + dir_entry.fx_dir_entry_short_name[0] = (CHAR)FX_DIR_ENTRY_FREE; + + /* Now write out the directory entry. */ + status = _fx_directory_entry_write(media_ptr, &dir_entry); + + /* Determine if the write was successful. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + + /* Walk through the directory's clusters and release them. */ + cluster = search_directory.fx_dir_entry_cluster; + + while (cluster < media_ptr -> fx_media_fat_reserved) + { + + /* Increment the cluster count. */ + i++; + + + /* Read the next FAT entry. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &next_cluster); + + /* Check the return status. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + if ((cluster < FX_FAT_ENTRY_START) || (cluster == next_cluster) || (i > media_ptr -> fx_media_total_clusters)) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(FX_FAT_READ_ERROR); + } + + /* Release the current cluster. */ + + status = _fx_utility_FAT_entry_write(media_ptr, cluster, FX_FREE_CLUSTER); + + /* Check the return status. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + /* Increment the number of available clusters for the media. */ + media_ptr -> fx_media_available_clusters++; + + /* Copy next cluster to current cluster. */ + cluster = next_cluster; + } + +#ifdef FX_FAULT_TOLERANT + + /* Flush the cached individual FAT entries */ + _fx_utility_FAT_flush(media_ptr); +#endif + + /* Flush the logical sector cache. */ + status = _fx_utility_logical_sector_flush(media_ptr, ((ULONG64) 1), (ULONG64)(media_ptr -> fx_media_sectors_per_FAT), FX_FALSE); + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + /* End transaction. */ + status = _fx_fault_tolerant_transaction_end(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Directory delete is complete, return status. */ + return(status); +} + diff --git a/common/src/fx_directory_entry_read.c b/common/src/fx_directory_entry_read.c new file mode 100644 index 0000000..e0194ba --- /dev/null +++ b/common/src/fx_directory_entry_read.c @@ -0,0 +1,722 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_entry_read PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads the supplied directory entry from the supplied */ +/* source directory. If the supplied directory entry is NULL, then */ +/* the root directory is assumed. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* source_dir Source directory entry */ +/* entry_ptr Directory entry number */ +/* destination_ptr Pointer to destination for */ +/* the directory entry */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* *entry_ptr should point to the 8:3 entry if it is a long name */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_FAT_entry_read Read a FAT entry */ +/* _fx_utility_logical_sector_read Read directory sector */ +/* _fx_utility_16_unsigned_read Read a UINT from memory */ +/* _fx_utility_32_unsigned_read Read a ULONG from memory */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_entry_read(FX_MEDIA *media_ptr, FX_DIR_ENTRY *source_dir, + ULONG *entry_ptr, FX_DIR_ENTRY *destination_ptr) +{ + +UINT i, j, card, dotflag, get_short_name; +UINT number_of_lfns; +UINT status; +ULONG cluster, next_cluster = 0; +UINT relative_cluster; +UINT relative_sector; +ULONG logical_sector; +ULONG byte_offset; +ULONG bytes_per_cluster; +UCHAR *read_ptr; +CHAR *short_name_ptr; +ULONG entry = *entry_ptr; + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of directory entry read requests. */ + media_ptr -> fx_media_directory_entry_reads++; +#endif + + /* Extended port-specific processing macro, which is by default defined to white space. */ + FX_DIRECTORY_ENTRY_READ_EXTENSION + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_DIR_ENTRY_READ, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Calculate the byte offset of this directory entry. */ + byte_offset = entry * FX_DIR_ENTRY_SIZE; + + /* Determine if a sub-directory or FAT32 root directory is specified. */ + if ((source_dir) || (media_ptr -> fx_media_32_bit_FAT)) + { + + /* Yes, a sub-directory is present. */ + + /* Calculate the number of bytes per cluster. */ + bytes_per_cluster = ((ULONG)media_ptr -> fx_media_bytes_per_sector) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster); + + /* Check for invalid value. */ + if (bytes_per_cluster == 0) + { + + /* Invalid media, return error. */ + return(FX_MEDIA_INVALID); + } + + /* Now determine the relative cluster in the sub-directory file. */ + relative_cluster = (UINT)(byte_offset / bytes_per_cluster); + + /* Calculate the byte offset within the cluster. */ + byte_offset = byte_offset % bytes_per_cluster; + + /* Now figure out the relative sector within the cluster. */ + relative_sector = (UINT)(byte_offset / ((ULONG)media_ptr -> fx_media_bytes_per_sector)); + + /* Read the directory sector into the internal memory buffer. */ + + /* Determine if there is a sub-directory. */ + if (source_dir) + { + + /* Determine if this source directory has valid information from the previous call. */ + if ((source_dir -> fx_dir_entry_last_search_cluster) && + (source_dir -> fx_dir_entry_last_search_relative_cluster <= relative_cluster) && + (source_dir -> fx_dir_entry_last_search_log_sector == source_dir -> fx_dir_entry_log_sector) && + (source_dir -> fx_dir_entry_last_search_byte_offset == source_dir -> fx_dir_entry_byte_offset)) + { + + /* Use the previous information to start the search. */ + cluster = source_dir -> fx_dir_entry_last_search_cluster; + + /* Setup the relative cluster index to the saved relative cluster. */ + i = source_dir -> fx_dir_entry_last_search_relative_cluster; + + /* Clear the search cluster. It will be updated prior to successful return. */ + source_dir -> fx_dir_entry_last_search_cluster = 0; + } + else + { + + /* Nothing from the previous directory read, just setup the starting cluster to the + beginning of the sub-directory. */ + cluster = source_dir -> fx_dir_entry_cluster; + + /* Setup the relative cluster index to zero. */ + i = 0; + } + } + else + { + + /* No, setup the starting cluster to the FAT32 root cluster. */ + cluster = media_ptr -> fx_media_root_cluster_32; + + /* Setup the relative cluster index to zero. */ + i = 0; + } + + /* Loop to position to the appropriate cluster. */ + while (i < relative_cluster) + { + + /* Check the value of the new cluster - it must be a valid cluster number + or something is really wrong! */ + if ((cluster < FX_FAT_ENTRY_START) || (cluster >= media_ptr -> fx_media_fat_reserved)) + { + + /* Send error message back to caller. */ + return(FX_FILE_CORRUPT); + } + + /* Read the next cluster. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &next_cluster); + + /* There is a potential for loop, but hardly anything can be done */ + + /* Check for I/O error. */ + if (status != FX_SUCCESS) + { + + /* Return error code. */ + return(status); + } + + /* Setup the actual cluster. */ + cluster = next_cluster; + + /* Increment the relative cluster number. */ + i++; + } + + /* At this point, the directory data sector needs to be read. */ + logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG)cluster - FX_FAT_ENTRY_START) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) + + relative_sector; + + /* Read the logical directory sector. */ + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) logical_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR); + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + + /* Return error code. */ + return(status); + } + + /* Calculate the byte offset within this sector. */ + byte_offset = byte_offset % media_ptr -> fx_media_bytes_per_sector; + } + else + { + + /* Read the entry from the root directory. */ + + /* Determine which sector the requested root directory entry is in. */ + logical_sector = (byte_offset / media_ptr -> fx_media_bytes_per_sector) + + (ULONG)media_ptr -> fx_media_root_sector_start; + + /* Read the logical directory sector. */ + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) logical_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR); + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + + /* Return error code. */ + return(status); + } + + /* Set the cluster and relative variables (not used in this case) to avoid any compiler + warnings. */ + relative_cluster = relative_sector = cluster = 0; + + /* Now calculate the byte offset into this sector. */ + byte_offset = byte_offset - + ((logical_sector - (ULONG)media_ptr -> fx_media_root_sector_start) * + media_ptr -> fx_media_bytes_per_sector); + } + + /* Setup a pointer into the buffer. */ + read_ptr = (UCHAR *)media_ptr -> fx_media_memory_buffer + (UINT)byte_offset; + + /* Save the logical sector and byte offset in the returned directory entry. */ + destination_ptr -> fx_dir_entry_log_sector = logical_sector; + destination_ptr -> fx_dir_entry_byte_offset = byte_offset; + + /* Clear the short file name information. */ + destination_ptr -> fx_dir_entry_long_name_shorted = 0; + destination_ptr -> fx_dir_entry_short_name[0] = 0; + + /* Setup short name pointer. */ + short_name_ptr = destination_ptr -> fx_dir_entry_name; + + /* Check if long file name exists. */ + get_short_name = 0; + if ((*(read_ptr + 11) == (UCHAR)FX_LONG_NAME) && (*read_ptr != (UCHAR)FX_DIR_ENTRY_FREE)) + { + + /* Collate the long name. */ + + /* Pickup the file name length. */ + i = (((UINT)(*read_ptr & (UCHAR)0x1f) - 1) * FX_LONG_NAME_ENTRY_LEN) & 0xFFFFFFFF; + + /* Save the number of LFN entries. */ + number_of_lfns = (UINT)(*read_ptr & (UCHAR)0x1f); + + /* Check the file name size. */ + if (i >= (FX_MAX_LONG_NAME_LEN - 1)) + { + + /* Name is too big, shorten it. */ + get_short_name = 1; + destination_ptr -> fx_dir_entry_long_name_shorted = (UINT)(*read_ptr & (UCHAR)0x1f); + } + else + { + + /* Size of name is fine, save pointer to short file name. */ + short_name_ptr = destination_ptr -> fx_dir_entry_short_name; + + /* Loop to make sure the long file name is NULL terminated. */ + j = i + FX_LONG_NAME_ENTRY_LEN + 1; + do + { + /* Place a NULL in the long name. */ + destination_ptr -> fx_dir_entry_name[i] = 0; + + /* Position to the next entry. */ + i++; + } while ((i < j) && (i < FX_MAX_LONG_NAME_LEN)); + } + + /* Loop to pickup the rest of the name. */ + do + { + + /* Get the lower 5 bit containing the cardinality. */ + card = (UINT)(*read_ptr & (UCHAR)0x1f) - 1; + + /* For simplicity no checksum or cardinality checking is done */ + if (get_short_name == 0) + { + + /* Loop to pickup name. */ + for (i = 1, j = 0; i < FX_DIR_ENTRY_SIZE; i += 2) + { + + if ((i == 11) || (i == 26)) + { + continue; + } + + /* i = 12, 27 is not generated due to +=2 */ + if (i == 13) + { + i = 12; + continue; /* this time next unicode is byte offset 14*/ + } + + /* Determine if there is an actual unicode character present. */ + if (read_ptr[i + 1]) + { + + /* Extended byte is non-zero, make sure both bytes of the unicode entry are not + all ones, since this is a normal case. */ + if ((read_ptr[i + 1] != (UCHAR)0xFF) || (read_ptr[i] != (UCHAR)0xFF)) + { + + /* Name is an actual unicode name, shorten it. */ + get_short_name = 1; + + /* Save the number of directory entries the LFN has. This will be + used later when updating the 8.3 portion of the LFN. */ + destination_ptr -> fx_dir_entry_long_name_shorted = number_of_lfns; + + /* Setup short name pointer. */ + short_name_ptr = destination_ptr -> fx_dir_entry_name; + } + } + + /* Determine if the character is NULL. */ + if ((read_ptr[i] == FX_NULL) || (read_ptr[i] == (UCHAR)0xFF)) + { + continue; + } + + /* Determine if the name is too big. */ + if ((card * 13 + j) >= (FX_MAX_LONG_NAME_LEN - 1)) + { + + /* Name is actually too big, shorten it. */ + get_short_name = 1; + + /* Save the number of directory entries the LFN has. This will be + used later when updating the 8.3 portion of the LFN. */ + destination_ptr -> fx_dir_entry_long_name_shorted = number_of_lfns; + + /* Also reposition the short name pointer. */ + short_name_ptr = destination_ptr -> fx_dir_entry_name; + + break; + } + + /* Each entry contains 13 unicode and first byte ASCII, second byte is extended. */ + destination_ptr -> fx_dir_entry_name[13 * card + j] = (CHAR)read_ptr[i]; + + j++; + } + } + + /* Determine if a new sector needs to be read. */ + if (byte_offset + FX_DIR_ENTRY_SIZE >= media_ptr -> fx_media_bytes_per_sector) + { + + /* Determine if a sub-directory or FAT32 root directory is specified. */ + if ((source_dir) || (media_ptr -> fx_media_32_bit_FAT)) + { + + /* Determine the next sector of the directory entry. */ + if (relative_sector < (media_ptr -> fx_media_sectors_per_cluster - 1)) + { + + /* More sectors in this cluster. */ + + /* Simply increment the logical sector. */ + logical_sector++; + + /* Increment the relative sector. */ + relative_sector++; + } + else + { + + /* We need to move to the next cluster. */ + + /* Pickup the next cluster. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &next_cluster); + + /* Check for I/O error. */ + if (status != FX_SUCCESS) + { + + /* Return error code. */ + return(status); + } + + /* Copy next cluster to the current cluster. */ + cluster = next_cluster; + + /* Check the value of the new cluster - it must be a valid cluster number + or something is really wrong! */ + if ((cluster < FX_FAT_ENTRY_START) || (cluster >= media_ptr -> fx_media_fat_reserved)) + { + + /* Send error message back to caller. */ + return(FX_FILE_CORRUPT); + } + + /* Now increment the relative cluster. */ + relative_cluster++; + + /* Setup the relative sector (this is zero for subsequent cluster. */ + relative_sector = 0; + + /* Calculate the next logical sector. */ + logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG)cluster - FX_FAT_ENTRY_START) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)); + } + } + else + { + + /* Non-FAT 32 root directory. */ + + /* Advance to the next sector. */ + logical_sector++; + + /* Determine if the logical sector is valid. */ + if (logical_sector >= (ULONG)(media_ptr -> fx_media_root_sector_start + media_ptr -> fx_media_root_sectors)) + { + + /* Trying to read past root directory - send error message back to caller. */ + return(FX_FILE_CORRUPT); + } + } + + /* Read the new sector. */ + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) logical_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR); + + /* Check I/O status. */ + if (status != FX_SUCCESS) + { + return(status); + } + + /* Set the byte offset to 0 for new sector. */ + byte_offset = 0; + } + else + { + + /* Calculate the new byte offset. */ + byte_offset += FX_DIR_ENTRY_SIZE; + } + + /* Calculate the next read pointer. */ + read_ptr = (UCHAR *)media_ptr -> fx_media_memory_buffer + (UINT) byte_offset; + + /* Move to the next entry. */ + entry++; + } while (card > 0); + + /* Set flag indicating long file name is present. */ + destination_ptr -> fx_dir_entry_long_name_present = 1; + } + else + { + /* No long file name is present. */ + get_short_name = 1; + } + + /* Determine if we need to clear the long name flag. */ + if (get_short_name == 1) + { + + /* Clear the long name flag. */ + destination_ptr -> fx_dir_entry_long_name_present = 0; + } + + /* Pickup the short file name. */ + short_name_ptr[0] = 0; + dotflag = 0; + for (i = 0, j = 0; i < (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE); i++) + { + + /* Check for a NULL. */ + if ((CHAR)read_ptr[i] == 0) + { + break; + } + + /* Check for a dot. This happens for the first two directory entries, no + extra dot is needed. */ + if ((CHAR)read_ptr[i] == '.') + { + dotflag = 2; + } + + /* Check for a space. */ + if ((CHAR)read_ptr[i] == ' ') + { + /* Put a dot if a character comes after space. */ + if (dotflag == 0) + { + dotflag = 1; + } + continue; + } + + /* Check for the main short file name size. */ + if (i == FX_DIR_NAME_SIZE) + { + /* Check to see if we need to insert a dot. */ + if (dotflag == 0) + { + dotflag = 1; + } + } + + /* Check to see if we need to add a dot. */ + if (dotflag == 1) + { + /* Add dot to short file name. */ + short_name_ptr[j++] = '.'; + dotflag = 2; /* no more dot for spaces */ + } + + /* Copy a character. */ + short_name_ptr[j] = (CHAR)read_ptr[i]; + + /* Increment size. */ + j++; + } + + /* Determine if a long file name is present and its associated short file + name is actually free. */ + if ((destination_ptr -> fx_dir_entry_long_name_present) && (((UCHAR)short_name_ptr[0]) == (UCHAR)FX_DIR_ENTRY_FREE)) + { + + /* Yes, the short file name is really free even though long file name entries directly precede it. + In this case, simply place the free directory marker at the front of the long file name. */ + destination_ptr -> fx_dir_entry_name[0] = (CHAR)FX_DIR_ENTRY_FREE; + short_name_ptr[0] = (CHAR)0; + } + + /* Determine if the short name pointer is NULL while the read pointer is + non-NULL. */ + if ((short_name_ptr[0] == 0) && (read_ptr[0] == ' ')) + { + + /* This condition can occur with an all blank volume name. Simply + copy the volume name to the short name in this case. */ + for (j = 0; j < (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE); j++) + { + + /* Copy a byte of the volume name. */ + short_name_ptr[j] = (CHAR)read_ptr[j]; + } + } + + /* Set end of string to null. */ + short_name_ptr[j] = 0; + + /* Load up the destination directory entry. */ + read_ptr += (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE); + + /* Copy the attribute into the destination. */ + destination_ptr -> fx_dir_entry_attributes = *read_ptr++; + + /* Pickup the reserved byte. */ + destination_ptr -> fx_dir_entry_reserved = *read_ptr++; + + /* Check for an undocumented NT file name feature for optimizing the storage + of all lower case file names that otherwise are valid 8.3 file names. The + following reserved bit definitions are present: + + BIT3 - set if 8.3 is all in lower case and no extended filename. + BIT4 - set for file, clear for directory entry if no extended filename. + + This is true for all NT systems. Prior to NT follows MSDOS FAT documentation and + is set to 0x00, all bits cleared. Therefore if BIT3 is set force lowercase. */ + if ((get_short_name) && (destination_ptr -> fx_dir_entry_reserved & 0x08)) + { + + /* Microsoft undocumented NT file name feature... convert short name to lower + case. */ + for (j = 0; j <= (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE) && (short_name_ptr[j] != 0x00); j++) + { + + /* Determine if an upper case character is present. */ + if ((short_name_ptr[j] >= 'A') && (short_name_ptr[j] <= 'Z')) + { + + /* Yes, an upper case character is present. Force it to lower case. */ + short_name_ptr[j] = (CHAR)(short_name_ptr[j] + 32); + } + } + } + + /* Pickup the created time in milliseconds. */ + destination_ptr -> fx_dir_entry_created_time_ms = *read_ptr++; + + /* Pickup the created time. */ + destination_ptr -> fx_dir_entry_created_time = _fx_utility_16_unsigned_read(read_ptr); + read_ptr = read_ptr + 2; /* Always 2 bytes */ + + /* Pickup the created date. */ + destination_ptr -> fx_dir_entry_created_date = _fx_utility_16_unsigned_read(read_ptr); + read_ptr = read_ptr + 2; /* Always 2 bytes */ + + /* Pickup the last accessed date. */ + destination_ptr -> fx_dir_entry_last_accessed_date = _fx_utility_16_unsigned_read(read_ptr); + read_ptr = read_ptr + 2; /* Always 2 bytes */ + + /* read the upper 2 bytes of starting cluster - required only for 32 bit FAT */ + if (media_ptr -> fx_media_32_bit_FAT) + { + + /* FAT32 only. */ + destination_ptr -> fx_dir_entry_cluster = _fx_utility_16_unsigned_read(read_ptr); + destination_ptr -> fx_dir_entry_cluster <<= 16; + } + else + { + /* Not required for non FAT32. */ + destination_ptr -> fx_dir_entry_cluster = 0; + } + + /* Advance the read pointer. */ + read_ptr = read_ptr + 2; /* Always 2 bytes */ + + /* Copy the time into the destination. */ + destination_ptr -> fx_dir_entry_time = _fx_utility_16_unsigned_read(read_ptr); + read_ptr = read_ptr + 2; /* Always 2 bytes */ + + /* Copy the date into the destination. */ + destination_ptr -> fx_dir_entry_date = _fx_utility_16_unsigned_read(read_ptr); + read_ptr = read_ptr + 2; /* Always 2 bytes */ + + /* Copy the starting cluster into the destination. */ + destination_ptr -> fx_dir_entry_cluster += _fx_utility_16_unsigned_read(read_ptr); + read_ptr = read_ptr + 2; /* Always 2 bytes */ + + /* Copy the file size into the destination. */ + destination_ptr -> fx_dir_entry_file_size = _fx_utility_32_unsigned_read(read_ptr); + + /* Clear the destination search specific fields. */ + destination_ptr -> fx_dir_entry_last_search_cluster = 0; + destination_ptr -> fx_dir_entry_last_search_relative_cluster = 0; + destination_ptr -> fx_dir_entry_last_search_log_sector = 0; + destination_ptr -> fx_dir_entry_last_search_byte_offset = 0; + + /* Remember the entry number. */ + destination_ptr -> fx_dir_entry_number = entry; + + /* Return entry number. */ + *entry_ptr = entry; + + /* Determine if we should remember the last cluster and relative cluster. */ + if (source_dir) + { + + /* Yes, remember the last cluster and relative cluster for a subsequent call + to read a directory entry. */ + source_dir -> fx_dir_entry_last_search_cluster = cluster; + source_dir -> fx_dir_entry_last_search_relative_cluster = relative_cluster; + + /* Also remember several other items that are unique to the directory... just to verify that the + search information can be used. */ + source_dir -> fx_dir_entry_last_search_log_sector = source_dir -> fx_dir_entry_log_sector; + source_dir -> fx_dir_entry_last_search_byte_offset = source_dir -> fx_dir_entry_byte_offset; + } + + /* Return success to the caller. */ + return(FX_SUCCESS); +} + + + diff --git a/common/src/fx_directory_entry_write.c b/common/src/fx_directory_entry_write.c new file mode 100644 index 0000000..f4b87fb --- /dev/null +++ b/common/src/fx_directory_entry_write.c @@ -0,0 +1,1152 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_utility.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_entry_write PORTABLE C */ +/* 6.1.5 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes the supplied directory entry to the specified */ +/* logical sector and offset. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* entry_ptr Pointer to directory entry */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_FAT_entry_read Read a new FAT entry */ +/* _fx_utility_logical_sector_read Read directory sector */ +/* _fx_utility_logical_sector_write Write directory sector */ +/* _fx_utility_16_unsigned_write Write a UINT from memory */ +/* _fx_utility_32_unsigned_write Write a ULONG from memory */ +/* _fx_fault_tolerant_add_dir_log Add directory redo log */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* 03-02-2021 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1.5 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_entry_write(FX_MEDIA *media_ptr, FX_DIR_ENTRY *entry_ptr) +{ + +UCHAR *work_ptr, *sector_base_ptr; +UINT status, temp, entry, delete_flag; +UINT i, j, k, l, card, len, dotfound, dotpos, match; +UCHAR checksum, eof_marker; +CHAR alpha; +CHAR shortname[FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE + 1]; +ULONG logical_sector, relative_sector; +ULONG byte_offset; +ULONG cluster, next_cluster; + + +#ifdef FX_ENABLE_FAULT_TOLERANT +UCHAR *changed_ptr; +UINT changed_size; +ULONG changed_offset; +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of directory entry write requests. */ + media_ptr -> fx_media_directory_entry_writes++; +#endif + + /* Extended port-specific processing macro, which is by default defined to white space. */ + FX_DIRECTORY_ENTRY_WRITE_EXTENSION + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_DIR_ENTRY_WRITE, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Determine if this is entry is being deleted. */ + if (((UCHAR)entry_ptr -> fx_dir_entry_name[0] == (UCHAR)FX_DIR_ENTRY_FREE) && + ((UCHAR)entry_ptr -> fx_dir_entry_short_name[0] == (UCHAR)FX_DIR_ENTRY_FREE)) + { + + /* Yes, this is a request to delete the entry. Set the flag to remember this. */ + delete_flag = FX_TRUE; + + /* Null the short file name. */ + entry_ptr -> fx_dir_entry_short_name[0] = 0; + } + else + { + + /* Not a deleted entry. Set the flag to false. */ + delete_flag = FX_FALSE; + } + + /* Pickup the byte offset of the entry. */ + byte_offset = entry_ptr -> fx_dir_entry_byte_offset; + + /* Pickup the logical sector of the entry. */ + logical_sector = (ULONG)entry_ptr -> fx_dir_entry_log_sector; + + /* Figure out where what cluster we are in. */ + if (logical_sector >= (ULONG)(media_ptr -> fx_media_data_sector_start)) + { + + /* Calculate the cluster that this logical sector is in. */ + cluster = (logical_sector - media_ptr -> fx_media_data_sector_start) / (media_ptr -> fx_media_sectors_per_cluster) + FX_FAT_ENTRY_START; + + /* Calculate the relative cluster. */ + relative_sector = logical_sector - (((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG)cluster - FX_FAT_ENTRY_START) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster))); + } + else + { + + /* Clear the cluster and the relative sector. */ + cluster = 0; + relative_sector = 0; + } + + /* Read the logical directory sector. */ + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) entry_ptr -> fx_dir_entry_log_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR); + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + + /* Setup a pointer into the buffer. */ + sector_base_ptr = (UCHAR *)media_ptr -> fx_media_memory_buffer; + work_ptr = sector_base_ptr + (UINT)entry_ptr -> fx_dir_entry_byte_offset; + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Initialize data for fault tolerant. */ + changed_ptr = work_ptr; + changed_size = 0; + changed_offset = entry_ptr -> fx_dir_entry_byte_offset; +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Determine if a long file name is present. */ + if (entry_ptr -> fx_dir_entry_long_name_present) + { + + /* Yes, long name is present - prepare short name and write out this name. */ + for (len = 0, i = 0, dotpos = 0, dotfound = 0; entry_ptr -> fx_dir_entry_name[len]; len++) + { + + /* Check for a dot. */ + if (entry_ptr -> fx_dir_entry_name[len] == '.') + { + + /* Check for leading dot. */ + if (len == 0) + { + /* Yes, this is a leading dot. */ + continue; + } + + /* Yes, a dot is present. From this position the extension will + be written. */ + dotfound = i; + dotpos = len + 1; + continue; + } + + /* Check for non-space and within the short file name length. */ + if ((entry_ptr -> fx_dir_entry_name[len] != ' ') && (i < 8)) + { + + /* Copy characters into the short file name area. */ + shortname[i] = entry_ptr -> fx_dir_entry_name[len]; + i++; + } + } + + /* Fill remaining short file name with spaces. */ + for (j = i; j < FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE; j++) + { + shortname[j] = ' '; + } + + /* Determine if a dot was encountered. */ + if (dotpos) + { + + /* Process relative to the dot position. */ + if (entry_ptr -> fx_dir_entry_name[dotpos]) + { + shortname[8] = entry_ptr -> fx_dir_entry_name[dotpos++]; + } + if (entry_ptr -> fx_dir_entry_name[dotpos]) + { + shortname[9] = entry_ptr -> fx_dir_entry_name[dotpos++]; + } + if (entry_ptr -> fx_dir_entry_name[dotpos]) + { + shortname[10] = entry_ptr -> fx_dir_entry_name[dotpos++]; + } + + /* Determine if additional spaces are needed. */ + i = dotfound; + + for (; dotfound <= 7; dotfound++) + { + /* Add space... */ + shortname[dotfound] = ' '; + } + } + + /* Each entry contains 13 unicode entries. Calculate the remainder. */ + if (len % 13 == 0) + { + card = len / 13; + } + else + { + card = len / 13 + 1; + } + + /* Default the name match to true. */ + match = FX_TRUE; + + /* Loop through the newly derived short name and the original name and look + for a non-matching character. */ + l = 0; + k = 0; + while (k < FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE) + { + + /* Determine if a space is detected in the short name. If so, + advance to the extension index. */ + if (shortname[k] == ' ') + { + + /* The first pad space was detected. First, check for a name + without an extension. */ + if (entry_ptr -> fx_dir_entry_name[l] == FX_NULL) + { + + /* All is okay, get out of the loop! */ + break; + } + + /* Now check for a period in the long name... if not, there is a non-match! */ + if (entry_ptr -> fx_dir_entry_name[l] != '.') + { + + /* Set the match flag to false and exit the loop. */ + match = FX_FALSE; + break; + } + + /* Otherwise move short file name index to the extension area and + increment the long file name index. */ + k = 8; + l++; + + /* Restart the loop at the top. */ + continue; + } + + /* Check for the dot for the 8.3 match... it is no longer in the + shortname but possibly still present in the long name. */ + if ((k == 8) && (entry_ptr -> fx_dir_entry_name[l] == '.')) + { + + /* Yes, handle the implicit dot in the shortname by + positioning past it in the long name. */ + l++; + } + + /* Do the names match? */ + if (shortname[k] != entry_ptr -> fx_dir_entry_name[l]) + { + + /* No, the names do not match, set the match flag to false and + exit the loop. */ + match = FX_FALSE; + break; + } + + /* Move the indices forward. */ + k++; + l++; + } + + /* Check if there is a dot in the name, but no extension in the short name. In this case, + we should create a mangled short name. */ + if ((dotpos) && (shortname[8] == ' ')) + { + + /* Something left.. the names do not match! */ + match = FX_FALSE; + } + + /* One final check to make sure there is nothing left on the long file name. */ + if (entry_ptr -> fx_dir_entry_name[l]) + { + + /* Something left.. the names do not match! */ + match = FX_FALSE; + } + + /* Determine if the derived short name matches exactly the long file name. If so + we don't need to mangle the name with a numeric value based on its entry. */ + if (match == FX_FALSE) + { + + /* Name does not match, create a mangled name. */ + + /* Generate short file name from LFN. */ + entry = entry_ptr -> fx_dir_entry_number; + + /* Name suffice is between 000 and FFFF in hex, calculate this short file + name's numeric component. */ + entry = entry % 0x10000; + + /* Build short name of the format xxx~NNNN.ext. */ + if (i > 3) + { + i = 3; + } + shortname[i++] = '~'; + + /* Loop to build the numeric part of the name. */ + for (l = 0; l < 4; l++) + { + + /* Shift down the entry number based on the numeric position. */ + if (l == 0) + { + temp = ((entry >> 12) & 0xf); + } + else if (l == 1) + { + temp = ((entry >> 8) & 0xf); + } + else if (l == 2) + { + temp = ((entry >> 4) & 0xf); + } + else + { + temp = ((entry) & 0xf); + } + + /* Now build hex value. */ + if (temp > 9) + shortname[i++] = (CHAR)('A' + (temp - 10)); + else + shortname[i++] = (CHAR)('0' + temp); + } + } + + /* Set end of short string to NULL. */ + shortname[11] = 0; + + /* Determine if the first character of the short file name is the directory free + value. If so, it must be changed. */ + if (((UCHAR)shortname[0] == (UCHAR)FX_DIR_ENTRY_FREE) && (delete_flag == FX_FALSE)) + { + + /* Change to 0x8F to be compatible with what DOS does. */ + shortname[0] = (CHAR)0x8F; + } + + /* Loop to convert the new short file name to upper case. */ + for (i = 0; i < (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE); i++) + { + + /* Pickup shortname character. */ + alpha = shortname[i]; + + /* Determine if character is lower case. */ + if ((alpha >= 'a') && (alpha <= 'z')) + { + + /* Store the character - converted to upper case. */ + alpha = (CHAR)(alpha - ((CHAR)0x20)); + } + + /* Now store the short name character. */ + shortname[i] = alpha; + } + + /* Determine if there already is a short name and we are not deleting the entry. */ + if (entry_ptr -> fx_dir_entry_short_name[0] != 0) + { + + /* Yes, override the calculated shortname with the original 8.3 name. */ + + /* Clear the short file name area. */ + for (i = 0; i < FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE; i++) + { + shortname[i] = ' '; + } + + /* Loop to copy the original short file name. */ + for (i = 0, j = 0; j < FX_DIR_NAME_SIZE; i++, j++) + { + + /* Check for end of copy conditions. */ + if ((UCHAR)entry_ptr -> fx_dir_entry_short_name[i] == '.') + { + break; + } + if ((UCHAR)entry_ptr -> fx_dir_entry_short_name[i] == 0) + { + break; + } + + /* Pickup the character. */ + alpha = entry_ptr -> fx_dir_entry_short_name[i]; + + /* Copy file name character. */ + shortname[j] = alpha; + } + + /* Determine if there is anything left in the short file name. */ + if ((UCHAR)entry_ptr -> fx_dir_entry_short_name[i] != 0) + { + + /* Pickup remaining characters. */ + for (i++, j = FX_DIR_NAME_SIZE; j < FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE; i++, j++) + { + + /* If NULL is encountered, stop the copying. */ + if ((UCHAR)entry_ptr -> fx_dir_entry_short_name[i] == 0) + { + break; + } + + /* Pickup the character. */ + alpha = entry_ptr -> fx_dir_entry_short_name[i]; + + /* Copy file name character. */ + shortname[j] = alpha; + } + } + + /* Loop to make sure the short name is upper case. */ + for (j = 0; j < (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE); j++) + { + + /* Pickup the character. */ + alpha = shortname[j]; + + /* Determine if character is lower case. */ + if ((alpha >= 'a') && (alpha <= 'z')) + { + + /* Store the character - converted to upper case. */ + alpha = (CHAR)(alpha - ((CHAR)0x20)); + } + + /* Copy file name character. */ + shortname[j] = alpha; + } + + /* Determine if the first character of the short file name is the directory free + value. If so, it must be changed. */ + if (((UCHAR)shortname[0]) == ((UCHAR)FX_DIR_ENTRY_FREE)) + { + + /* Change to 0x8F to be compatible with what DOS does. */ + shortname[0] = (CHAR)0x8F; + } + } + + /* Loop to calculate the checksum. */ + for (i = checksum = 0; i < FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE; i++) + { + + /* Calculate the checksum. */ + checksum = (UCHAR)((UCHAR)(((checksum & 1) << 7) | ((checksum & (UCHAR)0xfe) >> 1)) + shortname[i]); + } + + /* Set the last entry mark. */ + work_ptr[0] = (UCHAR)(0x40 | card); + + /* Loop to process remainder of long file name entry. */ + while (card > 0) + { + + /* Clear eof marker. */ + eof_marker = 0; + + /* Determine if the entry is free. */ + if ((UCHAR)shortname[0] == (UCHAR)FX_DIR_ENTRY_FREE) + { + /* Yes, place delete marker. */ + work_ptr[0] = (UCHAR)FX_DIR_ENTRY_FREE; + } + + /* Setup various long file name fields. */ + work_ptr[11] = FX_LONG_NAME; + work_ptr[12] = 0; + work_ptr[13] = checksum; + work_ptr[26] = 0; + work_ptr[27] = 0; + + /* Loop through file name fields. */ + for (i = 1, j = 13 * (card - 1); i < FX_DIR_ENTRY_SIZE; i += 2) + { + + /* Process relative to specific fields. */ + if ((i == 11) || (i == 26)) + { + continue; + } + + if (i == 13) + { + i = 12; + continue; + } + + /* Determine if the EOF marker is present. */ + if (eof_marker) + { + + work_ptr[i] = eof_marker; + work_ptr[i + 1] = eof_marker; + } + else + { + work_ptr[i] = (UCHAR)entry_ptr -> fx_dir_entry_name[j]; + work_ptr[i + 1] = 0; + } + + if (entry_ptr -> fx_dir_entry_name[j] == 0) + { + + /* end of name, pad with 0xff. */ + eof_marker = (UCHAR)0xff; + } + + j++; + } + + /* Move to the next directory entry. */ + work_ptr += FX_DIR_ENTRY_SIZE; + byte_offset += FX_DIR_ENTRY_SIZE; + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Update changed_size. */ + changed_size += FX_DIR_ENTRY_SIZE; +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Determine if the entry overlaps into the next sector. */ + if (byte_offset >= media_ptr -> fx_media_bytes_per_sector) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled) + { + + /* Redirect this request to log file. */ + status = _fx_fault_tolerant_add_dir_log(media_ptr, logical_sector, changed_offset, changed_ptr, changed_size); + } + else + { +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Write current logical sector out. */ + status = _fx_utility_logical_sector_write(media_ptr, (ULONG64) logical_sector, + sector_base_ptr, ((ULONG) 1), FX_DIRECTORY_SECTOR); +#ifdef FX_ENABLE_FAULT_TOLERANT + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + + /* Determine if we are in the root directory. */ + if (logical_sector >= (ULONG)(media_ptr -> fx_media_data_sector_start)) + { + + /* Determine the next sector of the directory entry. */ + if (relative_sector < (media_ptr -> fx_media_sectors_per_cluster - 1)) + { + + /* More sectors in this cluster. */ + + /* Simply increment the logical sector. */ + logical_sector++; + + /* Increment the relative sector. */ + relative_sector++; + } + else + { + + /* We need to move to the next cluster. */ + + /* Pickup the next cluster. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &next_cluster); + + /* Check for I/O error. */ + if (status != FX_SUCCESS) + { + + /* Return error code. */ + return(status); + } + + /* Copy next cluster to the current cluster. */ + cluster = next_cluster; + + /* Check the value of the new cluster - it must be a valid cluster number + or something is really wrong! */ + if ((cluster < FX_FAT_ENTRY_START) || (cluster >= media_ptr -> fx_media_fat_reserved)) + { + + /* Send error message back to caller. */ + return(FX_FILE_CORRUPT); + } + + /* Setup the relative sector (this is zero for subsequent cluster. */ + relative_sector = 0; + + /* Calculate the next logical sector. */ + logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG)cluster - FX_FAT_ENTRY_START) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)); + } + } + else + { + + /* Increment the logical sector. */ + logical_sector++; + + /* Determine if the logical sector is valid. */ + if (logical_sector >= (ULONG)(media_ptr -> fx_media_data_sector_start)) + { + + /* We have exceeded the root directory. */ + + /* Send error message back to caller. */ + return(FX_FILE_CORRUPT); + } + } + + /* Read the sector. */ + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) logical_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR); + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + + /* Setup logical sector. */ + sector_base_ptr = media_ptr -> fx_media_memory_buffer; + + /* Setup a fresh byte offset. */ + byte_offset = 0; + + /* Setup a new pointer into the buffer. */ + work_ptr = sector_base_ptr; + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Initialize data for fault tolerant. */ + changed_ptr = work_ptr; + changed_size = 0; + changed_offset = 0; +#endif /* FX_ENABLE_FAULT_TOLERANT */ + } + + /* Decrement loop control. */ + card--; + work_ptr[0] = (UCHAR)card; + } + + /* Determine if there is a short name. */ + if (entry_ptr -> fx_dir_entry_short_name[0] == 0) + { + + /* Loop to copy the new short file name. */ + for (i = 0; i < (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE); i++) + { + + /* Pickup shortname character. */ + alpha = shortname[i]; + + /* Now store the short name character. */ + *work_ptr++ = (UCHAR)alpha; + } + } + else + { + + /* Clear the short file name area. */ + for (i = 0; i < FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE; i++) + { + work_ptr[i] = ' '; + } + + /* Loop to copy the old short file name. */ + for (i = 0, j = 0; j < FX_DIR_NAME_SIZE; i++, j++) + { + + /* Check for end of copy conditions. */ + if ((UCHAR)entry_ptr -> fx_dir_entry_short_name[i] == '.') + { + break; + } + if ((UCHAR)entry_ptr -> fx_dir_entry_short_name[i] == 0) + { + break; + } + + /* Copy file name character. */ + work_ptr[j] = (UCHAR)entry_ptr -> fx_dir_entry_short_name[i]; + } + + /* Determine if there is anything left in the short file name. */ + if ((UCHAR)entry_ptr -> fx_dir_entry_short_name[i] != 0) + { + + /* Pickup remaining characters. */ + for (i++, j = FX_DIR_NAME_SIZE; j < FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE; i++, j++) + { + + /* If NULL is encountered, stop the copying. */ + if ((UCHAR)entry_ptr -> fx_dir_entry_short_name[i] == 0) + { + break; + } + + /* Copy file name character. */ + work_ptr[j] = (UCHAR)entry_ptr -> fx_dir_entry_short_name[i]; + } + } + + /* Adjust the work pointer accordingly. */ + work_ptr += (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE); + } + } + else + { + + /* Determine if long name was shorted. */ + if (entry_ptr -> fx_dir_entry_long_name_shorted > 0) + { + + /* Check for a valid short name. */ + if ((UCHAR)(0x40 | entry_ptr -> fx_dir_entry_long_name_shorted) == (UCHAR)(*work_ptr)) + { + + /* Loop through the file name. */ + for (j = 0; j < entry_ptr -> fx_dir_entry_long_name_shorted; j++) + { + + /* Check for a free entry to be written. */ + if ((UCHAR)entry_ptr -> fx_dir_entry_name[0] == (UCHAR)FX_DIR_ENTRY_FREE) + { + /* Delete long parts. */ + work_ptr[0] = (UCHAR)FX_DIR_ENTRY_FREE; + } + + /* Setup pointers for the name write. */ + work_ptr += FX_DIR_ENTRY_SIZE; + byte_offset += FX_DIR_ENTRY_SIZE; + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Update changed_size. */ + changed_size += FX_DIR_ENTRY_SIZE; +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Determine if the write is within the current sector. */ + if (byte_offset >= media_ptr -> fx_media_bytes_per_sector) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled) + { + + /* Redirect this request to log file. */ + status = _fx_fault_tolerant_add_dir_log(media_ptr, (ULONG64) logical_sector, changed_offset, changed_ptr, changed_size); + } + else + { +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Write the current sector out. */ + status = _fx_utility_logical_sector_write(media_ptr, (ULONG64) logical_sector, + sector_base_ptr, ((ULONG) 1), FX_DIRECTORY_SECTOR); +#ifdef FX_ENABLE_FAULT_TOLERANT + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + + /* Determine if we are in the root directory. */ + if (logical_sector >= (ULONG)(media_ptr -> fx_media_data_sector_start)) + { + + /* Determine the next sector of the directory entry. */ + if (relative_sector < (media_ptr -> fx_media_sectors_per_cluster - 1)) + { + + /* More sectors in this cluster. */ + + /* Simply increment the logical sector. */ + logical_sector++; + + /* Increment the relative sector. */ + relative_sector++; + } + else + { + + /* We need to move to the next cluster. */ + + /* Pickup the next cluster. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &next_cluster); + + /* Check for I/O error. */ + if (status != FX_SUCCESS) + { + + /* Return error code. */ + return(status); + } + + /* Copy next cluster to the current cluster. */ + cluster = next_cluster; + + /* Check the value of the new cluster - it must be a valid cluster number + or something is really wrong! */ + if ((cluster < FX_FAT_ENTRY_START) || (cluster >= media_ptr -> fx_media_fat_reserved)) + { + + /* Send error message back to caller. */ + return(FX_FILE_CORRUPT); + } + + /* Setup the relative sector (this is zero for subsequent cluster. */ + relative_sector = 0; + + /* Calculate the next logical sector. */ + logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG)cluster - FX_FAT_ENTRY_START) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)); + } + } + else + { + + /* Increment the logical sector. */ + logical_sector++; + + /* Determine if the logical sector is valid. */ + if (logical_sector >= (ULONG)(media_ptr -> fx_media_data_sector_start)) + { + + /* We have exceeded the root directory. */ + + /* Send error message back to caller. */ + return(FX_FILE_CORRUPT); + } + } + + /* Read the next logical sector. */ + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) logical_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR); + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + + /* Move to the next sector buffer. */ + sector_base_ptr = media_ptr -> fx_media_memory_buffer; + + /* Setup new buffer pointers. */ + byte_offset = 0; + work_ptr = sector_base_ptr; + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Initialize data for fault tolerant. */ + changed_ptr = work_ptr; + changed_size = 0; + changed_offset = 0; +#endif /* FX_ENABLE_FAULT_TOLERANT */ + } + } + } + } + + /* This is an 8.3 name. First clear the directory name. */ + for (j = 0; j < FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE; j++) + { + work_ptr[j] = ' '; + } + + /* Copy leading dots in case of first two entries of a directory. */ + for (i = 0; (UCHAR)entry_ptr -> fx_dir_entry_name[i] == '.'; i++) + { + work_ptr[i] = '.'; + } + + /* Determine if there are more characters to copy. */ + if ((UCHAR)entry_ptr -> fx_dir_entry_name[i] != 0) + { + + /* Copy directory name. */ + for (i = 0, j = 0; j < FX_DIR_NAME_SIZE; i++, j++) + { + + /* Check for end of copy conditions. */ + if ((UCHAR)entry_ptr -> fx_dir_entry_name[i] == '.') + { + break; + } + if ((UCHAR)entry_ptr -> fx_dir_entry_name[i] == 0) + { + break; + } + + /* Pickup shortname character. */ + alpha = entry_ptr -> fx_dir_entry_name[i]; + + /* Determine if character is lower case. */ + if ((alpha >= 'a') && (alpha <= 'z')) + { + + /* Store the character - converted to upper case. */ + alpha = (CHAR)(alpha - ((CHAR)0x20)); + } + + /* Copy a name character. */ + work_ptr[j] = (UCHAR)alpha; + } + } + + /* Determine if there are more characters in the name. */ + if ((UCHAR)entry_ptr -> fx_dir_entry_name[i] != 0) + { + + /* Loop to copy the remainder of the name. */ + for (i++, j = FX_DIR_NAME_SIZE; j < FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE; i++, j++) + { + + /* Check for end of copy conditions. */ + if ((UCHAR)entry_ptr -> fx_dir_entry_name[i] == 0) + { + break; + } + + /* Pickup shortname character. */ + alpha = entry_ptr -> fx_dir_entry_name[i]; + + /* Determine if character is lower case. */ + if ((alpha >= 'a') && (alpha <= 'z')) + { + + /* Store the character - converted to upper case. */ + alpha = (CHAR)(alpha - ((CHAR)0x20)); + } + + /* Copy a name character. */ + work_ptr[j] = (UCHAR)alpha; + } + } + + /* Move to the next entry. */ + work_ptr += (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE); + } + + /* Write out the 8.3 part of the name. */ + + /* Copy the attribute into the destination. */ + *work_ptr++ = entry_ptr -> fx_dir_entry_attributes; + + /* Copy the reserved byte. */ + *work_ptr++ = entry_ptr -> fx_dir_entry_reserved; + + /* Copy the created time in milliseconds. */ + *work_ptr++ = entry_ptr -> fx_dir_entry_created_time_ms; + + /* Copy the created time. */ + _fx_utility_16_unsigned_write(work_ptr, entry_ptr -> fx_dir_entry_created_time); + work_ptr = work_ptr + 2; /* Always 2 bytes */ + + /* Copy the created date. */ + _fx_utility_16_unsigned_write(work_ptr, entry_ptr -> fx_dir_entry_created_date); + work_ptr = work_ptr + 2; /* Always 2 bytes */ + + /* Copy the last accessed date. */ + _fx_utility_16_unsigned_write(work_ptr, entry_ptr -> fx_dir_entry_last_accessed_date); + work_ptr = work_ptr + 2; /* Always 2 bytes */ + + /* Determine if a FAT32 entry is present. */ + if (media_ptr -> fx_media_32_bit_FAT) + { + + /* Yes, FAT32 is present, store upper half of cluster. */ + temp = (entry_ptr -> fx_dir_entry_cluster >> 16); + _fx_utility_16_unsigned_write(work_ptr, temp); + } + else + { + + /* No, FAT16 or FAT12 is present, just write a 0 for + the upper half of the cluster. */ + _fx_utility_16_unsigned_write(work_ptr, 0); + } + + /* Advance the entry pointer. */ + work_ptr = work_ptr + 2; /* Always 2 bytes */ + + /* Copy the time into the destination. */ + _fx_utility_16_unsigned_write(work_ptr, entry_ptr -> fx_dir_entry_time); + work_ptr = work_ptr + 2; /* Always 2 bytes */ + + /* Copy the date into the destination. */ + _fx_utility_16_unsigned_write(work_ptr, entry_ptr -> fx_dir_entry_date); + work_ptr = work_ptr + 2; /* Always 2 bytes */ + + /* Copy the starting cluster into the destination. */ + _fx_utility_16_unsigned_write(work_ptr, (UINT)entry_ptr -> fx_dir_entry_cluster); + work_ptr = work_ptr + 2; /* Always 2 bytes */ + + /* Copy the file size into the destination. */ + _fx_utility_32_unsigned_write(work_ptr, (ULONG)entry_ptr -> fx_dir_entry_file_size); + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Update changed_size. */ + changed_size += FX_DIR_ENTRY_SIZE; + + if (media_ptr -> fx_media_fault_tolerant_enabled && + (media_ptr -> fx_media_fault_tolerant_state & FX_FAULT_TOLERANT_STATE_STARTED)) + { + + /* Redirect this request to log file. */ + status = _fx_fault_tolerant_add_dir_log(media_ptr, (ULONG64) logical_sector, changed_offset, changed_ptr, changed_size); + } + else + { +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Write the directory sector to the media. */ + status = _fx_utility_logical_sector_write(media_ptr, (ULONG64) logical_sector, + sector_base_ptr, ((ULONG) 1), FX_DIRECTORY_SECTOR); +#ifdef FX_ENABLE_FAULT_TOLERANT + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + +#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE + + /* Determine if there is a previously found directory entry in the directory + search cache. */ + if (media_ptr -> fx_media_last_found_name[0]) + { + + /* Determine if the cached search directory entry matches the directory entry being + written. */ + if ((entry_ptr -> fx_dir_entry_log_sector == media_ptr -> fx_media_last_found_entry.fx_dir_entry_log_sector) && + (entry_ptr -> fx_dir_entry_byte_offset == media_ptr -> fx_media_last_found_entry.fx_dir_entry_byte_offset)) + { + + /* Yes, this entry is the same as the one currently in the directory search cache. + Update various fields in the directory search cache with the information being + written now. */ + media_ptr -> fx_media_last_found_entry.fx_dir_entry_cluster = entry_ptr -> fx_dir_entry_cluster; + media_ptr -> fx_media_last_found_entry.fx_dir_entry_file_size = entry_ptr -> fx_dir_entry_file_size; + media_ptr -> fx_media_last_found_entry.fx_dir_entry_attributes = entry_ptr -> fx_dir_entry_attributes; + media_ptr -> fx_media_last_found_entry.fx_dir_entry_time = entry_ptr -> fx_dir_entry_time; + media_ptr -> fx_media_last_found_entry.fx_dir_entry_date = entry_ptr -> fx_dir_entry_date; + media_ptr -> fx_media_last_found_entry.fx_dir_entry_reserved = entry_ptr -> fx_dir_entry_reserved; + media_ptr -> fx_media_last_found_entry.fx_dir_entry_created_time_ms = entry_ptr -> fx_dir_entry_created_time_ms; + media_ptr -> fx_media_last_found_entry.fx_dir_entry_created_time = entry_ptr -> fx_dir_entry_created_time; + media_ptr -> fx_media_last_found_entry.fx_dir_entry_created_date = entry_ptr -> fx_dir_entry_created_date; + } + } +#endif + + /* Return success to the caller. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_directory_first_entry_find.c b/common/src/fx_directory_first_entry_find.c new file mode 100644 index 0000000..230dff9 --- /dev/null +++ b/common/src/fx_directory_first_entry_find.c @@ -0,0 +1,136 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_utility.h" + +#ifndef FX_NO_LOCAL_PATH +FX_LOCAL_PATH_SETUP +#endif + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_first_entry_find PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the first directory entry of the current */ +/* working directory. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* directory_name Destination for directory */ +/* name */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_next_entry_find Find next directory entry */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_first_entry_find(FX_MEDIA *media_ptr, CHAR *directory_name) +{ + +UINT status; + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_directory_first_entry_finds++; +#endif + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_DIRECTORY_FIRST_ENTRY_FIND, media_ptr, directory_name, 0, 0, FX_TRACE_DIRECTORY_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Determine if a local path is in effect at this point. */ +#ifndef FX_NO_LOCAL_PATH + if (_tx_thread_current_ptr -> tx_thread_filex_ptr) + { + + /* Yes, there is a local path. Set the current entry to zero. */ + ((FX_PATH *)_tx_thread_current_ptr -> tx_thread_filex_ptr) -> fx_path_current_entry = 0; + } + else + { + + /* Use global default directory. Set the current entry to 0 in + order to pickup the first entry. */ + media_ptr -> fx_media_default_path.fx_path_current_entry = 0; + } +#else + + /* Set the current entry to 0 in order to pickup the first entry. */ + media_ptr -> fx_media_default_path.fx_path_current_entry = 0; +#endif + + /* Release media protection. */ + FX_UNPROTECT + + /* Call the next directory entry to pickup the first entry. */ + status = _fx_directory_next_entry_find(media_ptr, directory_name); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fx_directory_first_full_entry_find.c b/common/src/fx_directory_first_full_entry_find.c new file mode 100644 index 0000000..b6d8eb5 --- /dev/null +++ b/common/src/fx_directory_first_full_entry_find.c @@ -0,0 +1,146 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_utility.h" + +#ifndef FX_NO_LOCAL_PATH +FX_LOCAL_PATH_SETUP +#endif + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_first_full_entry_find PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the first directory entry of the current */ +/* working directory and selected information about the entry. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* directory_name Destination for directory */ +/* name */ +/* attributes Destination for attributes */ +/* size Destination for size */ +/* year Destination for year */ +/* month Destination for month */ +/* day Destination for day */ +/* hour Destination for hour */ +/* minute Destination for minute */ +/* second Destination for second */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_next_full_entry_find Find next full directory entry*/ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_first_full_entry_find(FX_MEDIA *media_ptr, + CHAR *directory_name, UINT *attributes, ULONG *size, + UINT *year, UINT *month, UINT *day, UINT *hour, UINT *minute, UINT *second) +{ + +UINT status; + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_directory_first_full_entry_finds++; +#endif + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_DIRECTORY_FIRST_FULL_ENTRY_FIND, media_ptr, directory_name, 0, 0, FX_TRACE_DIRECTORY_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Determine if a local path is in effect at this point. */ +#ifndef FX_NO_LOCAL_PATH + if (_tx_thread_current_ptr -> tx_thread_filex_ptr) + { + + /* Yes, there is a local path. Set the current entry to zero. */ + ((FX_PATH *)_tx_thread_current_ptr -> tx_thread_filex_ptr) -> fx_path_current_entry = 0; + } + else + { + + /* Use global default directory. Set the current entry to 0 in + order to pickup the first entry. */ + media_ptr -> fx_media_default_path.fx_path_current_entry = 0; + } +#else + + /* Set the current entry to 0 in order to pickup the first entry. */ + media_ptr -> fx_media_default_path.fx_path_current_entry = 0; +#endif + + /* Release media protection. */ + FX_UNPROTECT + + /* Call the next directory full entry service to pickup the first entry. */ + status = _fx_directory_next_full_entry_find(media_ptr, directory_name, attributes, + size, year, month, day, hour, minute, second); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fx_directory_free_search.c b/common/src/fx_directory_free_search.c new file mode 100644 index 0000000..7fa8a17 --- /dev/null +++ b/common/src/fx_directory_free_search.c @@ -0,0 +1,770 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_free_search PORTABLE C */ +/* 6.1.12 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function searches the media for a free directory entry. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* directory_ptr Pointer to directory to */ +/* search in */ +/* entry_ptr Pointer to directory entry */ +/* record */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_entry_read Read entries from directory */ +/* _fx_directory_entry_write Write entries to directory */ +/* _fx_utility_FAT_entry_read Read a FAT entry */ +/* _fx_utility_FAT_entry_write Write a FAT entry */ +/* _fx_utility_FAT_flush Flush written FAT entries */ +/* _fx_utility_logical_sector_flush Flush logical sector cache */ +/* _fx_utility_logical_sector_read Read logical sector */ +/* _fx_utility_logical_sector_write Write logical sector */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* 07-29-2022 Bhupendra Naphade Modified comment(s), */ +/* updated available cluster */ +/* check for sub directory, */ +/* resulting in version 6.1.12 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_free_search(FX_MEDIA *media_ptr, FX_DIR_ENTRY *directory_ptr, FX_DIR_ENTRY *entry_ptr) +{ + +ULONG i, j; +UCHAR *work_ptr; +UINT status, total_entries; +ULONG entry_sector, entry_offset; +ULONG FAT_index, FAT_value; +ULONG cluster, total_clusters, clusters_needed; +ULONG first_new_cluster, last_cluster, clusters; +ULONG directory_index; +ULONG directory_entries; +ULONG logical_sector; +FX_DIR_ENTRY *search_dir_ptr; +ULONG free_entry_start; +UINT sectors; + +FX_INT_SAVE_AREA + + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of directory free entry search requests. */ + media_ptr -> fx_media_directory_free_searches++; +#endif + + /* Initialize the entry sector values. */ + entry_sector = entry_offset = 0; + + /* Set the long file name flag to false. */ + entry_ptr -> fx_dir_entry_long_name_present = 0; + + /* Are there leading dots? */ + if (entry_ptr -> fx_dir_entry_name[0] == '.') + { + + /* Is there more than 1 dot? */ + if (entry_ptr -> fx_dir_entry_name[1] == '.') + { + /* Yes, consider the name invalid. */ + return(FX_INVALID_NAME); + } + } + + /* Determine if a long file name is present. */ + for (i = 0, j = 0; entry_ptr -> fx_dir_entry_name[i]; i++) + { + + /* Check for upper-case characters. */ + if ((entry_ptr -> fx_dir_entry_name[i] >= 'A') && (entry_ptr -> fx_dir_entry_name[i] <= 'Z')) + { + continue; + } + /* Check for numeric characters. */ + else if ((entry_ptr -> fx_dir_entry_name[i] >= '0') && (entry_ptr -> fx_dir_entry_name[i] <= '9')) + { + continue; + } + /* Check for any lower-case characters. */ + else if ((entry_ptr -> fx_dir_entry_name[i] >= 'a') && (entry_ptr -> fx_dir_entry_name[i] <= 'z')) + { + entry_ptr -> fx_dir_entry_long_name_present = 1; + } + /* Check for a space in the middle of the name. */ + else if (entry_ptr -> fx_dir_entry_name[i] == ' ') + { + entry_ptr -> fx_dir_entry_long_name_present = 1; + } + /* Check for a dot in the name. */ + else if (entry_ptr -> fx_dir_entry_name[i] == '.') + { + /* Determine if this is the first dot detected. */ + if (j == 0) + { + /* First dot, remember where it was. */ + j = i; + + /* Determine if this is a leading dot. */ + if (i == 0) + { + + /* Leading dot detected, treat as a long filename. */ + entry_ptr -> fx_dir_entry_long_name_present = 1; + } + } + else + { + /* Second dot detected, must have a long file name. */ + entry_ptr -> fx_dir_entry_long_name_present = 1; + } + } + /* Check for a special 0xE5 character. */ + else if ((UCHAR)entry_ptr -> fx_dir_entry_name[i] == (UCHAR)0xE5) + { + entry_ptr -> fx_dir_entry_long_name_present = 1; + } + /* Check for code point value greater than 127. */ + else if ((UCHAR)entry_ptr -> fx_dir_entry_name[i] > (UCHAR)127) + { + continue; + } + /* Check for any special characters. */ + else if ((entry_ptr -> fx_dir_entry_name[i] == '~') || + (entry_ptr -> fx_dir_entry_name[i] == '-') || + (entry_ptr -> fx_dir_entry_name[i] == '_') || + (entry_ptr -> fx_dir_entry_name[i] == '}') || + (entry_ptr -> fx_dir_entry_name[i] == '{') || + (entry_ptr -> fx_dir_entry_name[i] == '(') || + (entry_ptr -> fx_dir_entry_name[i] == ')') || + (entry_ptr -> fx_dir_entry_name[i] == '`') || + (entry_ptr -> fx_dir_entry_name[i] == '\'') || + (entry_ptr -> fx_dir_entry_name[i] == '!') || + (entry_ptr -> fx_dir_entry_name[i] == '#') || + (entry_ptr -> fx_dir_entry_name[i] == '$') || + (entry_ptr -> fx_dir_entry_name[i] == '&') || + (entry_ptr -> fx_dir_entry_name[i] == '@') || + (entry_ptr -> fx_dir_entry_name[i] == '^') || + (entry_ptr -> fx_dir_entry_name[i] == '%')) + { + continue; + } + /* Check for long filename special characters. */ + else if ((entry_ptr -> fx_dir_entry_name[i] == '+') || + (entry_ptr -> fx_dir_entry_name[i] == ',') || + (entry_ptr -> fx_dir_entry_name[i] == ';') || + (entry_ptr -> fx_dir_entry_name[i] == '=') || + (entry_ptr -> fx_dir_entry_name[i] == '[') || + (entry_ptr -> fx_dir_entry_name[i] == ']')) + { + entry_ptr -> fx_dir_entry_long_name_present = 1; + } + /* Something is wrong with the supplied name. */ + else + { + return(FX_INVALID_NAME); + } + } + + /* Determine if a dot was found. */ + if (j != 0) + { + + /* Yes, Determine if the extension exceeds a 3 character extension. */ + if ((i - j) > 4) + { + + /* Yes, long file name is present. */ + entry_ptr -> fx_dir_entry_long_name_present = 1; + } + } + + /* Calculate the total entries needed. */ + if ((i <= 12) && (entry_ptr -> fx_dir_entry_long_name_present == 0)) + { + + /* Initialize the total entries to 1. */ + total_entries = 1; + + /* Check for special instance of long file name. */ + if ((j >= 9) || ((i - j) >= 9)) + { + + /* The dot is after 8 character or there is no dot and the name + is greater than 8 character. */ + entry_ptr -> fx_dir_entry_long_name_present = 1; + total_entries = 2; + } + } + else + { + + /* Long file name is present, calculate how many entries are needed + to represent it. */ + if (i % 13 == 0) + { + /* Exact fit, just add one for the 8.3 short name. */ + total_entries = i / 13 + 1; + } + else + { + /* Non-exact fit, add two for 8.3 short name and overlap. */ + total_entries = i / 13 + 2; + } + } + + /* Determine if the search is in the root directory or in a + sub-directory. Note: the directory search function clears the + first character of the name for the root directory. */ + if (directory_ptr -> fx_dir_entry_name[0]) + { + + /* Search for a free entry in a sub-directory. */ + + /* Pickup the number of entries in this directory. This was placed + into the unused file size field. */ + directory_entries = (ULONG)directory_ptr -> fx_dir_entry_file_size; + + /* Point the search directory pointer to this entry. */ + search_dir_ptr = directory_ptr; + + /* Ensure that the search directory's last search cluster is cleared. */ + search_dir_ptr -> fx_dir_entry_last_search_cluster = 0; + + /* Set the initial index to 2, since the first two directory entries are + always allocated. */ + directory_index = 2; + } + else + { + + /* Find a free entry in the root directory. */ + + /* Setup the number of directory entries. */ + directory_entries = (ULONG)media_ptr -> fx_media_root_directory_entries; + + /* Set the search pointer to NULL since we are working off of the + root directory. */ + search_dir_ptr = FX_NULL; + + /* Set the initial index to 0, since the first entry of the root directory is valid. */ + directory_index = 0; + } + + /* Loop through entries in the search directory. Yes, this is a + linear search! */ + free_entry_start = directory_entries; + do + { + + /* Read an entry from the directory. */ + status = _fx_directory_entry_read(media_ptr, search_dir_ptr, &directory_index, entry_ptr); + + /* Check for error status. */ + if (status != FX_SUCCESS) + { + return(status); + } + + /* Determine if this is an empty entry. */ + if ((((UCHAR)entry_ptr -> fx_dir_entry_name[0] == (UCHAR)FX_DIR_ENTRY_FREE) && (entry_ptr -> fx_dir_entry_short_name[0] == 0)) || + ((UCHAR)entry_ptr -> fx_dir_entry_name[0] == (UCHAR)FX_DIR_ENTRY_DONE)) + { + + /* Determine how many entries are needed. */ + if (total_entries > 1) + { + + /* Multiple entries are needed for long file names. Mark this + entry as free. */ + if (entry_ptr -> fx_dir_entry_name[0] == FX_DIR_ENTRY_DONE) + { + + entry_ptr -> fx_dir_entry_long_name_present = 0; + entry_ptr -> fx_dir_entry_name[0] = (CHAR)FX_DIR_ENTRY_FREE; + entry_ptr -> fx_dir_entry_name[1] = (CHAR)0; + + /* Write out the directory entry. */ + status = _fx_directory_entry_write(media_ptr, entry_ptr); + if(status != FX_SUCCESS) + { + return(status); + } + + /* Note that for long names we need to avoid holes in the middle, + i.e. entries must be logically contiguous. */ + } + } + + /* Determine if we are at the first free entry. */ + if (free_entry_start == directory_entries) + { + + /* Remember the start of the free entry. */ + free_entry_start = directory_index; + entry_sector = (ULONG)entry_ptr -> fx_dir_entry_log_sector; + entry_offset = entry_ptr -> fx_dir_entry_byte_offset; + } + + /* Determine if there are enough free entries to satisfy the request. */ + if ((directory_index - free_entry_start + 1) >= total_entries) + { + + /* Found an empty slot. Most pertinent information is already + in the entry structure. */ + + /* Setup the the sector and the offset. */ + entry_ptr -> fx_dir_entry_log_sector = entry_sector; + entry_ptr -> fx_dir_entry_byte_offset = entry_offset; + + /* Initialize the additional directory entries. */ + entry_ptr -> fx_dir_entry_reserved = 0; + entry_ptr -> fx_dir_entry_created_time_ms = 0; + + /* Lockout interrupts for time/date access. */ + FX_DISABLE_INTS + + entry_ptr -> fx_dir_entry_created_time = _fx_system_time; + entry_ptr -> fx_dir_entry_created_date = _fx_system_date; + entry_ptr -> fx_dir_entry_last_accessed_date = _fx_system_date; + + /* Restore interrupts. */ + FX_RESTORE_INTS + + /* Determine if a long file name is present. */ + if (total_entries == 1) + { + entry_ptr -> fx_dir_entry_long_name_present = 0; + } + else + { + entry_ptr -> fx_dir_entry_long_name_present = 1; + } + + /* Return a successful completion. */ + return(FX_SUCCESS); + } + } + else + { + + /* Reset the free entry start. */ + free_entry_start = directory_entries; + } + + /* Move to the next entry. */ + directory_index++; + + /* Determine if we have exceeded the number of entries in the current directory. */ + if (directory_index >= directory_entries) + { + + /* Calculate how many sectors we need for the new directory entry. */ + sectors = ((total_entries * FX_DIR_ENTRY_SIZE) + (media_ptr -> fx_media_bytes_per_sector - 1))/ + media_ptr -> fx_media_bytes_per_sector; + + /* Now calculate how many clusters we need for the new directory entry. */ + clusters_needed = (sectors + (media_ptr -> fx_media_sectors_per_cluster - 1)) / media_ptr -> fx_media_sectors_per_cluster; + + /* Not enough empty entries were found. If the specified directory is a sub-directory, + attempt to allocate another cluster to it. */ + if (((search_dir_ptr) || (media_ptr -> fx_media_32_bit_FAT)) && (media_ptr -> fx_media_available_clusters >= clusters_needed)) + { + + /* Search for the additional clusters we need. */ + first_new_cluster = 0; + total_clusters = media_ptr -> fx_media_total_clusters; + last_cluster = 0; + FAT_index = media_ptr -> fx_media_cluster_search_start; + clusters = clusters_needed; + + /* Loop to find the needed clusters. */ + while (clusters) + { + + /* Decrease the cluster count. */ + clusters--; + + /* Loop to find the first available cluster. */ + do + { + + /* Make sure we stop looking after one pass through the FAT table. */ + if (!total_clusters) + { + + /* Something is wrong with the media - the desired clusters were + not found in the FAT table. */ + return(FX_NO_MORE_SPACE); + } + + /* Read FAT entry. */ + status = _fx_utility_FAT_entry_read(media_ptr, FAT_index, &FAT_value); + + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + + /* Return the bad status. */ + return(status); + } + + /* Decrement the total cluster count. */ + total_clusters--; + + /* Determine if the FAT entry is free. */ + if (FAT_value == FX_FREE_CLUSTER) + { + + /* Move cluster search pointer forward. */ + media_ptr -> fx_media_cluster_search_start = FAT_index + 1; + + /* Determine if this needs to be wrapped. */ + if (media_ptr -> fx_media_cluster_search_start >= (media_ptr -> fx_media_total_clusters + FX_FAT_ENTRY_START)) + { + + /* Wrap the search to the beginning FAT entry. */ + media_ptr -> fx_media_cluster_search_start = FX_FAT_ENTRY_START; + } + + /* Break this loop. */ + break; + } + else + { + + /* FAT entry is not free... Advance the FAT index. */ + FAT_index++; + + /* Determine if we need to wrap the FAT index around. */ + if (FAT_index >= (media_ptr -> fx_media_total_clusters + FX_FAT_ENTRY_START)) + { + + /* Wrap the search to the beginning FAT entry. */ + FAT_index = FX_FAT_ENTRY_START; + } + } + } while (FX_TRUE); + + /* We found an available cluster. We now need to clear all of entries in + each of the cluster's sectors. */ + + /* Calculate the logical sector of this cluster. */ + logical_sector = ((ULONG) media_ptr -> fx_media_data_sector_start) + + ((((ULONG) FAT_index) - FX_FAT_ENTRY_START) * + ((ULONG) media_ptr -> fx_media_sectors_per_cluster)); + + /* Pickup the number of sectors for the next directory cluster. */ + sectors = media_ptr -> fx_media_sectors_per_cluster; + + /* Read the logical sector just for cache reasons. */ + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) logical_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR); + + /* Check the return value. */ + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + + /* Clear the entire first sector of the new sub-directory cluster. */ + work_ptr = (UCHAR *)media_ptr -> fx_media_memory_buffer; + i = 0; + while (i < media_ptr -> fx_media_bytes_per_sector) + { + + /* Clear 4 bytes. */ + *((ULONG *)work_ptr) = (ULONG)0; + + /* Increment pointer. */ + work_ptr = work_ptr + sizeof(ULONG); + + /* Increment counter. */ + i = i + (ULONG)sizeof(ULONG); + } + + /* Write the logical sector to ensure the zeros are written. */ + status = _fx_utility_logical_sector_write(media_ptr, (ULONG64) logical_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR); + + /* Determine if the write was successful. */ + if (status != FX_SUCCESS) + { + + /* Return the error code. */ + return(status); + } + + /* Determine if there are more sectors to clear in the first cluster of the new + sub-directory. */ + if (sectors > 1) + { + + /* Yes, invalidate all cached sectors that are contained in the newly allocated first + cluster of the directory. */ + + /* Flush the internal logical sector cache. */ + status = _fx_utility_logical_sector_flush(media_ptr, (ULONG64) (logical_sector + 1), (ULONG64) (sectors - 1), FX_TRUE); + + /* Determine if the flush was successful. */ + if (status != FX_SUCCESS) + { + + /* Return the error code. */ + return(status); + } + + /* Clear all additional sectors of new sub-directory. */ + sectors--; + while (sectors) + { + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of driver write sector(s) requests. */ + media_ptr -> fx_media_driver_write_requests++; +#endif + + /* Build Write request to the driver. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_WRITE; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = media_ptr -> fx_media_memory_buffer; + media_ptr -> fx_media_driver_logical_sector = (ULONG)logical_sector + ((ULONG)sectors); + media_ptr -> fx_media_driver_sectors = 1; + media_ptr -> fx_media_driver_sector_type = FX_DIRECTORY_SECTOR; + + /* Set the system write flag since we are writing a directory sector. */ + media_ptr -> fx_media_driver_system_write = FX_TRUE; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, ((ULONG)logical_sector) + ((ULONG)sectors), 1, media_ptr -> fx_media_memory_buffer, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Invoke the driver to write the sector. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Clear the system write flag. */ + media_ptr -> fx_media_driver_system_write = FX_FALSE; + + /* Determine if an error occurred. */ + if (media_ptr -> fx_media_driver_status != FX_SUCCESS) + { + + /* Return error code. */ + return(media_ptr -> fx_media_driver_status); + } + + /* Decrease the number of sectors to clear. */ + sectors--; + } + } + + /* Determine if we have found the first new cluster yet. */ + if (first_new_cluster == 0) + { + + /* Remember the first new cluster. */ + first_new_cluster = FAT_index; + } + + /* Check for a valid last cluster to link. */ + if (last_cluster) + { + + /* Normal condition - link the last cluster with the new + found cluster. */ + status = _fx_utility_FAT_entry_write(media_ptr, last_cluster, FAT_index); + + /* Check for a bad FAT write status. */ + if (status != FX_SUCCESS) + { + + /* Return the bad status. */ + return(status); + } + } + + /* Otherwise, remember the new FAT index as the last. */ + last_cluster = FAT_index; + + /* Move to the next FAT entry. */ + FAT_index = media_ptr -> fx_media_cluster_search_start; + } + + /* Place an end-of-file marker on the last cluster. */ + status = _fx_utility_FAT_entry_write(media_ptr, last_cluster, media_ptr -> fx_media_fat_last); + + /* Check for a bad FAT write status. */ + if (status != FX_SUCCESS) + { + + /* Return the bad status. */ + return(status); + } + +#ifdef FX_FAULT_TOLERANT + + /* Ensure the new FAT chain is properly written to the media. */ + + /* Flush the cached individual FAT entries */ + _fx_utility_FAT_flush(media_ptr); +#endif + + /* Now the new cluster needs to be linked to the sub-directory. */ + if (search_dir_ptr) + { + cluster = search_dir_ptr -> fx_dir_entry_cluster; + } + else + { + cluster = media_ptr -> fx_media_root_cluster_32; + } + + /* Initialize loop variables. */ + last_cluster = 0; + i = 0; + + /* Follow the link of FAT entries. */ + while (cluster < media_ptr -> fx_media_fat_reserved) + { + + /* Read the current cluster entry from the FAT. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &FAT_value); + i++; + + /* Check the return value. */ + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + + /* Determine if the FAT read was invalid. */ + if ((cluster < FX_FAT_ENTRY_START) || (cluster == FAT_value) || (i > media_ptr -> fx_media_total_clusters)) + { + + /* Return the bad status. */ + return(FX_FAT_READ_ERROR); + } + + /* Save the last valid cluster. */ + last_cluster = cluster; + + /* Setup for the next cluster. */ + cluster = FAT_value; + } + + /* Decrease the available clusters in the media. */ + media_ptr -> fx_media_available_clusters = media_ptr -> fx_media_available_clusters - clusters_needed; + + /* Increase the number of directory entries. */ + directory_entries = directory_entries + ((clusters_needed * media_ptr -> fx_media_sectors_per_cluster) * media_ptr -> fx_media_bytes_per_sector) / FX_DIR_ENTRY_SIZE; + + /* Determine if we need to reset the free entry start since we changed the + number of directory entries. If the last entry was not free, then we + should definitely reset the free entry start. */ + if (!(((UCHAR)entry_ptr -> fx_dir_entry_name[0] == (UCHAR) FX_DIR_ENTRY_FREE) && (entry_ptr -> fx_dir_entry_short_name[0] == 0))) + { + + /* Reset the free entry start to indicate we haven't found a starting free entry yet. */ + free_entry_start = directory_entries; + } + + /* Update the directory size field. */ + directory_ptr -> fx_dir_entry_file_size = directory_entries; + + /* Defer the update of the FAT entry and the last cluster of the current + directory entry until after the new cluster is initialized and written out. */ + + /* Determine if a FAT32 is present. */ + if ((media_ptr -> fx_media_32_bit_FAT) && (search_dir_ptr == FX_NULL)) + { + + /* Change root directory entry count - FAT32 has a variable sized root directory. */ + media_ptr -> fx_media_root_directory_entries = directory_entries; + } + + /* At this point, link up the last cluster with the new cluster. */ + status = _fx_utility_FAT_entry_write(media_ptr, last_cluster, first_new_cluster); + + /* Check the return value. */ + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + +#ifdef FX_FAULT_TOLERANT + + /* Flush the cached individual FAT entries */ + _fx_utility_FAT_flush(media_ptr); +#endif + } + } + } while (directory_index < directory_entries); + + /* Return FX_NO_MORE_SPACE status to the caller. */ + return(FX_NO_MORE_SPACE); +} + diff --git a/common/src/fx_directory_information_get.c b/common/src/fx_directory_information_get.c new file mode 100644 index 0000000..b9a4635 --- /dev/null +++ b/common/src/fx_directory_information_get.c @@ -0,0 +1,235 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_file.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_information_get PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function first attempts to find the specified directory name. */ +/* If found, the complete file parameters are placed in all non-NULL */ +/* return parameters. If the file name is not found, the appropriate */ +/* error code is returned to the caller. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* directory_name Directory name pointer */ +/* attributes Pointer to attributes */ +/* size Pointer to size */ +/* year Pointer to year */ +/* month Pointer to month */ +/* day Pointer to day */ +/* hour Pointer to hour */ +/* minute Pointer to minute */ +/* second Pointer to second */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_search Search for the file name in */ +/* the directory structure */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), verified */ +/* memcpy usage, */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_information_get(FX_MEDIA *media_ptr, CHAR *directory_name, + UINT *attributes, ULONG *size, + UINT *year, UINT *month, UINT *day, + UINT *hour, UINT *minute, UINT *second) +{ + +UINT status; +FX_DIR_ENTRY dir_entry; +ULONG open_count; +FX_FILE *search_ptr; + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_directory_information_gets++; +#endif + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Clear the short name string. */ + dir_entry.fx_dir_entry_short_name[0] = 0; + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_DIRECTORY_INFORMATION_GET, media_ptr, directory_name, 0, 0, FX_TRACE_DIRECTORY_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Search the system for the supplied directory name. */ + status = _fx_directory_search(media_ptr, directory_name, &dir_entry, FX_NULL, FX_NULL); + + /* Determine if the search was successful. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + /* Check the list of open files for others open for writing. */ + open_count = media_ptr -> fx_media_opened_file_count; + search_ptr = media_ptr -> fx_media_opened_file_list; + while (open_count) + { + + /* Look at each opened file to see if the same file is opened + for writing. */ + if ((search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector == dir_entry.fx_dir_entry_log_sector) && + (search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset == dir_entry.fx_dir_entry_byte_offset) && + (search_ptr -> fx_file_open_mode)) + { + + /* The file has been opened for writing by a previous call. Use the information used by + the writer instead of what is currently on the media. */ + _fx_utility_memory_copy((UCHAR *)&search_ptr -> fx_file_dir_entry, (UCHAR *)&dir_entry, sizeof(FX_DIR_ENTRY)); /* Use case of memcpy is verified. */ + break; + } + + /* Adjust the pointer and decrement the search count. */ + search_ptr = search_ptr -> fx_file_opened_next; + open_count--; + } + + /* Check to see if attributes are required. */ + if (attributes) + { + + /* Pickup the attributes. */ + *attributes = dir_entry.fx_dir_entry_attributes; + } + + /* Check to see if the size is required. */ + if (size) + { + + /* Pickup the size. */ + *size = (ULONG)dir_entry.fx_dir_entry_file_size; + } + + /* Check to see if the year is required. */ + if (year) + { + + /* Pickup the year. */ + *year = ((dir_entry.fx_dir_entry_date >> FX_YEAR_SHIFT) & FX_YEAR_MASK) + + FX_BASE_YEAR; + } + + /* Check to see if the month is required. */ + if (month) + { + + /* Pickup the month. */ + *month = (dir_entry.fx_dir_entry_date >> FX_MONTH_SHIFT) & FX_MONTH_MASK; + } + + /* Check to see if the day is required. */ + if (day) + { + + /* Pickup the day. */ + *day = dir_entry.fx_dir_entry_date & FX_DAY_MASK; + } + + /* Check to see if the hour is required. */ + if (hour) + { + + /* Pickup the hour. */ + *hour = (dir_entry.fx_dir_entry_time >> FX_HOUR_SHIFT) & FX_HOUR_MASK; + } + + /* Check to see if the minute is required. */ + if (minute) + { + + /* Pickup the minute. */ + *minute = (dir_entry.fx_dir_entry_time >> FX_MINUTE_SHIFT) & FX_MINUTE_MASK; + } + + /* Check to see if the second is required. */ + if (second) + { + + /* Pickup the second. */ + *second = (dir_entry.fx_dir_entry_time & FX_SECOND_MASK) * 2; + } + + /* Release media protection. */ + FX_UNPROTECT + + /* Directory information get is complete, return successful status. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_directory_local_path_clear.c b/common/src/fx_directory_local_path_clear.c new file mode 100644 index 0000000..0d22b6e --- /dev/null +++ b/common/src/fx_directory_local_path_clear.c @@ -0,0 +1,114 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_file.h" +#include "fx_utility.h" +#include "fx_directory.h" + +#ifndef FX_NO_LOCAL_PATH +FX_LOCAL_PATH_SETUP +#endif + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_local_path_clear PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function clears the local default directory of the media so */ +/* any path relative operations will start using the media's global */ +/* default path again. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_local_path_clear(FX_MEDIA *media_ptr) +{ + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_directory_local_path_clears++; +#endif + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + +#ifdef FX_NO_LOCAL_PATH + + /* Error, return to caller. */ + return(FX_NOT_IMPLEMENTED); +#else + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_DIRECTORY_LOCAL_PATH_CLEAR, media_ptr, 0, 0, 0, FX_TRACE_DIRECTORY_EVENTS, 0, 0) + + /* Clear this thread's local path pointer. */ + _tx_thread_current_ptr -> tx_thread_filex_ptr = FX_NULL; + + /* Local default directory clear is complete, return status. */ + return(FX_SUCCESS); +#endif +} + diff --git a/common/src/fx_directory_local_path_get.c b/common/src/fx_directory_local_path_get.c new file mode 100644 index 0000000..31b0973 --- /dev/null +++ b/common/src/fx_directory_local_path_get.c @@ -0,0 +1,129 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_file.h" +#include "fx_utility.h" +#include "fx_directory.h" + +#ifndef FX_NO_LOCAL_PATH +FX_LOCAL_PATH_SETUP +#endif + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_local_path_get PORTABLE C */ +/* 6.1.5 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns a pointer to the local default directory */ +/* for this thread. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* return_path_name Return local path name */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* 03-02-2021 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1.5 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_local_path_get(FX_MEDIA *media_ptr, CHAR **return_path_name) +{ + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_directory_local_path_gets++; +#endif + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + +#ifdef FX_NO_LOCAL_PATH + + FX_PARAMETER_NOT_USED(return_path_name); + + /* Error, return to caller. */ + return(FX_NOT_IMPLEMENTED); +#else + + /* Determine if there is a local path. */ + if (_tx_thread_current_ptr -> tx_thread_filex_ptr) + { + + /* Return a pointer to the current local directory path string. */ + *return_path_name = ((FX_LOCAL_PATH *)_tx_thread_current_ptr -> tx_thread_filex_ptr) -> fx_path_string; + } + else + { + + /* Just set the return value to FX_NULL to indicate there is no + local path setup. */ + *return_path_name = FX_NULL; + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_DIRECTORY_LOCAL_PATH_GET, media_ptr, return_path_name, 0, 0, FX_TRACE_DIRECTORY_EVENTS, 0, 0) + + /* Local default directory has been returned, return status. */ + return(FX_SUCCESS); +#endif +} + diff --git a/common/src/fx_directory_local_path_get_copy.c b/common/src/fx_directory_local_path_get_copy.c new file mode 100644 index 0000000..da523fa --- /dev/null +++ b/common/src/fx_directory_local_path_get_copy.c @@ -0,0 +1,123 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_file.h" +#include "fx_utility.h" +#include "fx_directory.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_local_path_get_copy PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function copies the local default directory for this thread */ +/* into the specified buffer. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* return_path_name_buffer Destination buffer for name */ +/* return_path_name_buffer_size Size of buffer pointed to by */ +/* return_path_name_buffer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), verified */ +/* memcpy usage, */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_local_path_get_copy(FX_MEDIA *media_ptr, CHAR *return_path_name_buffer, UINT return_path_name_buffer_size) +{ + +UINT status; +CHAR *return_path_name; +UINT path_name_length_with_null_terminator; + + + /* Get the pointer to the path. */ + status = _fx_directory_local_path_get(media_ptr, &return_path_name); + if (status == FX_SUCCESS) + { + + /* Was a path set? */ + if (return_path_name != FX_NULL) + { + + /* Get the length of the path. */ + path_name_length_with_null_terminator = _fx_utility_string_length_get(return_path_name, FX_MAXIMUM_PATH) + 1; + + /* Can it fit in the user's buffer? */ + if (path_name_length_with_null_terminator <= return_path_name_buffer_size) + { + + /* Copy the path name into the user's buffer. */ + _fx_utility_memory_copy((UCHAR *) return_path_name, (UCHAR *) return_path_name_buffer, path_name_length_with_null_terminator); /* Use case of memcpy is verified. */ + } + else + { + + /* Buffer is too small. Return error. */ + return(FX_BUFFER_ERROR); + } + } + else + { + + /* Set zero-length string. */ + return_path_name_buffer[0] = '\0'; + } + } + + /* Return status. */ + return(status); +} + diff --git a/common/src/fx_directory_local_path_restore.c b/common/src/fx_directory_local_path_restore.c new file mode 100644 index 0000000..66bcff6 --- /dev/null +++ b/common/src/fx_directory_local_path_restore.c @@ -0,0 +1,117 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_file.h" +#include "fx_utility.h" +#include "fx_directory.h" + +#ifndef FX_NO_LOCAL_PATH +FX_LOCAL_PATH_SETUP +#endif + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_local_path_restore PORTABLE C */ +/* 6.1.5 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the local default directory of the media to */ +/* the previous path specified by the caller. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* local_path_ptr Local path control block ptr */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* 03-02-2021 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1.5 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_local_path_restore(FX_MEDIA *media_ptr, FX_LOCAL_PATH *local_path_ptr) +{ + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_directory_local_path_restores++; +#endif + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + +#ifdef FX_NO_LOCAL_PATH + + FX_PARAMETER_NOT_USED(local_path_ptr); + + /* Error, return to caller. */ + return(FX_NOT_IMPLEMENTED); +#else + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_DIRECTORY_LOCAL_PATH_RESTORE, media_ptr, local_path_ptr, 0, 0, FX_TRACE_DIRECTORY_EVENTS, 0, 0) + + /* Setup thread control block to use this local path pointer. */ + _tx_thread_current_ptr -> tx_thread_filex_ptr = (VOID *)local_path_ptr; + + /* Default directory restore is complete, return status. */ + return(FX_SUCCESS); +#endif +} + diff --git a/common/src/fx_directory_local_path_set.c b/common/src/fx_directory_local_path_set.c new file mode 100644 index 0000000..5a09035 --- /dev/null +++ b/common/src/fx_directory_local_path_set.c @@ -0,0 +1,451 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_file.h" +#include "fx_utility.h" +#include "fx_directory.h" + +#ifndef FX_NO_LOCAL_PATH +FX_LOCAL_PATH_SETUP +#endif + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_local_path_set PORTABLE C */ +/* 6.1.5 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the local default directory of the media to the */ +/* path specified by the caller. If this path is not found, an error */ +/* code is returned. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* local_path Local path control block ptr */ +/* new_path_name New path to set current local */ +/* working directory to */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_search Search for the directory name */ +/* in the directory structure */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* 03-02-2021 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1.5 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_local_path_set(FX_MEDIA *media_ptr, FX_LOCAL_PATH *local_path_ptr, CHAR *new_path_name) +{ + +#ifndef FX_NO_LOCAL_PATH +UINT status; +CHAR *path_string_ptr; +UINT path_string_capacity; +FX_PATH *path_ptr; +UINT i, j; +#endif +FX_DIR_ENTRY dir_entry; + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_directory_local_path_sets++; +#endif + + /* Setup pointer to a name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Setup the local path name buffer pointer. */ + local_path_ptr -> fx_path_directory.fx_dir_entry_name = local_path_ptr -> fx_path_name_buffer; + + /* Clear the short name string. */ + dir_entry.fx_dir_entry_short_name[0] = (CHAR)0; + + /* Clear the long name string. */ + dir_entry.fx_dir_entry_name[0] = (CHAR)0; + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + +#ifdef FX_NO_LOCAL_PATH + + FX_PARAMETER_NOT_USED(new_path_name); + + /* Error, return to caller. */ + return(FX_NOT_IMPLEMENTED); +#else + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_DIRECTORY_LOCAL_PATH_SET, media_ptr, local_path_ptr, new_path_name, 0, FX_TRACE_DIRECTORY_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Look for a root directory selection. */ + if ((!new_path_name) || (((new_path_name[0] == '\\') || (new_path_name[0] == '/')) && (new_path_name[1] == (CHAR)0))) + { + + /* Set the default local directory to the root. */ + local_path_ptr -> fx_path_directory.fx_dir_entry_name[0] = (CHAR)0; + local_path_ptr -> fx_path_string[0] = (CHAR)0; + local_path_ptr -> fx_path_string[FX_MAXIMUM_PATH - 2] = (CHAR)0; + + /* Setup thread control block to use this local path pointer. */ + _tx_thread_current_ptr -> tx_thread_filex_ptr = (VOID *)local_path_ptr; + } + else + { + + /* Search the system for the supplied path and directory name. */ + status = _fx_directory_search(media_ptr, new_path_name, &dir_entry, FX_NULL, FX_NULL); + + /* Determine if the search failed or if the entry found is not a + directory. */ + if ((status != FX_SUCCESS) || (!(dir_entry.fx_dir_entry_attributes & FX_DIRECTORY))) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Invalid Path - Return the error code. */ + return(FX_INVALID_PATH); + } + + /* Now update the current path string. */ + + /* Setup the path string pointer to the start of the current path. */ + path_string_ptr = &(local_path_ptr -> fx_path_string[0]); + + /* Setup the path string's capacity. */ + path_string_capacity = FX_MAXIMUM_PATH - 1; + + /* Determine if the new path is relative from the current path. */ + if ((new_path_name[0] != '\\') && (new_path_name[0] != '/')) + { + + /* Yes, a relative path was found. */ + + /* Setup the default path pointer to the local path. */ + path_ptr = local_path_ptr; + + /* Determine if the local path is different than the current local path. */ + if (local_path_ptr != (FX_LOCAL_PATH *)_tx_thread_current_ptr -> tx_thread_filex_ptr) + { + + /* Yes, there is a difference. */ + + /* Should we copy from the default media path or from the previous default + path. */ + if (_tx_thread_current_ptr -> tx_thread_filex_ptr) + { + + /* There is a local path so copy the relative path info from it. */ + i = 0; + do + { + + /* Copy from previous local to new local path. */ + local_path_ptr -> fx_path_string[i] = + ((FX_LOCAL_PATH *)_tx_thread_current_ptr -> tx_thread_filex_ptr) -> fx_path_string[i]; + + /* Determine if we are done. */ + if (local_path_ptr -> fx_path_string[i] == 0) + { + + /* Are we not at the end of the string? */ + if (i < (FX_MAXIMUM_PATH - 1)) + { + + /* Yes, break the loop. */ + break; + } + } + + /* Move to the next character. */ + i++; + + } while (i < FX_MAXIMUM_PATH); + } + else + { + + /* No local path, so copy the relative path information from the media + default. */ + i = 0; + do + { + + /* Copy from the media default to new local path. */ + local_path_ptr -> fx_path_string[i] = + media_ptr -> fx_media_default_path.fx_path_string[i]; + + /* Determine if we are done. */ + if (local_path_ptr -> fx_path_string[i] == 0) + { + + /* Are we not at the end of the string? */ + if (i < (FX_MAXIMUM_PATH - 1)) + { + + /* Yes, break the loop. */ + break; + } + } + + /* Move to the next character. */ + i++; + + } while (i < FX_MAXIMUM_PATH); + } + } + + /* First, check the current path for string overflow. If this is set, + don't attempt to update the current path with relative information. + The path won't be valid again until a complete path is given. */ + if (path_ptr -> fx_path_string[FX_MAXIMUM_PATH - 2] == '*') + { + + /* Yes, don't update the string, just finish the path set processing. */ + + /* Determine if we are at the root directory. */ + if (!dir_entry.fx_dir_entry_cluster) + { + /* Set the current directory back to the root directory. */ + dir_entry.fx_dir_entry_name[0] = (CHAR)0; + + /* Clear the current path string. */ + path_ptr -> fx_path_string[0] = (CHAR)0; + + /* Clear the overflow flag in the current path string... just in + case! */ + path_ptr -> fx_path_string[FX_MAXIMUM_PATH - 2] = (CHAR)0; + } + + /* Copy the new directory entry into the media control block. */ + path_ptr -> fx_path_directory = dir_entry; + + /* Reset the local path name buffer pointer, since it was clobbered earlier. */ + local_path_ptr -> fx_path_directory.fx_dir_entry_name = local_path_ptr -> fx_path_name_buffer; + + /* Copy the directory name entry to the local path name area. */ + for (j = 0; j < FX_MAX_LONG_NAME_LEN; j++) + { + + /* Copy the name buffer. */ + local_path_ptr -> fx_path_directory.fx_dir_entry_name[j] = dir_entry.fx_dir_entry_name[j]; + } + + /* Setup thread control block to use this local path pointer. */ + _tx_thread_current_ptr -> tx_thread_filex_ptr = (VOID *)local_path_ptr; + + /* Release media protection. */ + FX_UNPROTECT + + /* Default directory set is complete, return status. */ + return(FX_SUCCESS); + } + + /* Move the current path starting pointer to the end of the current + path string. */ + while ((path_string_capacity) && (*path_string_ptr != FX_NULL)) + { + path_string_ptr++; + path_string_capacity--; + } + + /* If room, place the \ character in the path string. */ + if (path_string_capacity) + { + + /* There is room, place the directory marker in the string. */ + *path_string_ptr++ = '\\'; + path_string_capacity--; + } + } + else + { + + /* Setup the default path pointer. */ + + /* Setup the default path pointer to the local path. */ + path_ptr = local_path_ptr; + + /* Complete path name given. Check to see if we need to clear an + overflow character from a previous current path string update. */ + if (path_ptr -> fx_path_string[FX_MAXIMUM_PATH - 2] == '*') + { + path_ptr -> fx_path_string[FX_MAXIMUM_PATH - 2] = (CHAR)0; + } + } + + /* Copy what we can into the current path. */ + while (*new_path_name) + { + + /* Determine if there is a ".." character sequence that specifies the + previous path. */ + if ((*new_path_name == '.') && (*(new_path_name + 1) == '.')) + { + + /* Yes, a backward path is found. The current path pointer + must be moved back to just after the previous \ character. */ + + /* Skip the current \0 that is at the end of the current path. */ + path_string_capacity = path_string_capacity + 2; + path_string_ptr = path_string_ptr - 2; + + while (path_string_capacity <= (FX_MAXIMUM_PATH - 1)) + { + + /* Move the current path pointer backwards until + a \ character is found. */ + + if ((*path_string_ptr == '\\') || (*path_string_ptr == '/')) + { + + /* Yes, we have successfully backed up one directory. */ + break; + } + + /* Backup another character. */ + path_string_capacity++; + path_string_ptr--; + } + + /* Adjust the new directory pointer past the .. characters */ + new_path_name = new_path_name + 2; + } + else + { + + /* Normal characters that need to be copied into the current path. */ + + if (path_string_capacity) + { + + /* Copy character from the new path into the current path string. */ + *path_string_ptr++ = *new_path_name++; + + path_string_capacity--; + } + else + { + + /* No more room in the current path string! */ + break; + } + } + } + + /* Determine if there is still room in the current path string. */ + if (path_string_capacity) + { + + /* Yes, there is still room, place a NULL character at the + end of the path. */ + *path_string_ptr = (CHAR)FX_NULL; + } + else + { + + /* No more room. Determine if the entire path was successfully + copied into the current path. */ + if (*new_path_name) + { + + /* No, we couldn't fit the entire path. Place a "*" character + at the end to indicate that we had overflow. Note that + the path is kept just the the directory default get call, so + the new default path is valid. */ + path_ptr -> fx_path_string[FX_MAXIMUM_PATH - 2] = '*'; + } + } + + /* Determine if we are at the root directory. */ + if (!dir_entry.fx_dir_entry_cluster) + { + /* Set the current directory back to the root directory. */ + dir_entry.fx_dir_entry_name[0] = (CHAR)0; + local_path_ptr -> fx_path_name_buffer[0] = (CHAR)0; + } + + /* Copy the new directory entry into the media control block. */ + path_ptr -> fx_path_directory = dir_entry; + + /* Reset the local path name buffer pointer, since it was clobbered earlier. */ + local_path_ptr -> fx_path_directory.fx_dir_entry_name = local_path_ptr -> fx_path_name_buffer; + + /* Copy the directory name entry to the local path name area. */ + for (j = 0; j < FX_MAX_LONG_NAME_LEN; j++) + { + + /* Copy the name buffer. */ + local_path_ptr -> fx_path_directory.fx_dir_entry_name[j] = dir_entry.fx_dir_entry_name[j]; + } + + /* Setup thread control block to use this local path pointer. */ + _tx_thread_current_ptr -> tx_thread_filex_ptr = (VOID *)local_path_ptr; + } + + /* Release media protection. */ + FX_UNPROTECT + + /* Default directory set is complete, return status. */ + return(FX_SUCCESS); +#endif +} + diff --git a/common/src/fx_directory_long_name_get.c b/common/src/fx_directory_long_name_get.c new file mode 100644 index 0000000..49e4f31 --- /dev/null +++ b/common/src/fx_directory_long_name_get.c @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_long_name_get PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the long file name via the supplied short file */ +/* name. If there is no long file name, the short file name will be */ +/* returned. */ +/* */ +/* Note, this API is deprecated as fx_directory_long_name_get_extended */ +/* should be used. The maximum written size to long_file_name could be */ +/* FX_MAX_LONG_NAME_LEN. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* short_file_name Pointer to short (8.3) name */ +/* long_file_name Pointer to long (max 255) name*/ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_long_name_get_extended Actual long name get service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_long_name_get(FX_MEDIA *media_ptr, CHAR *short_file_name, CHAR *long_file_name) +{ + + /* Call the extended version with maximum long name length. */ + return(_fx_directory_long_name_get_extended(media_ptr, short_file_name, long_file_name, FX_MAX_LONG_NAME_LEN)); +} + diff --git a/common/src/fx_directory_long_name_get_extended.c b/common/src/fx_directory_long_name_get_extended.c new file mode 100644 index 0000000..b780565 --- /dev/null +++ b/common/src/fx_directory_long_name_get_extended.c @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_long_name_get_extended PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the long file name via the supplied short file */ +/* name. If there is no long file name, the short file name will be */ +/* returned. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* short_file_name Pointer to short (8.3) name */ +/* long_file_name Pointer to long (max 255) name*/ +/* long_file_name_buffer_length Buffer length for long name */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_search Search for the file name in */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_long_name_get_extended(FX_MEDIA *media_ptr, CHAR *short_file_name, CHAR *long_file_name, UINT long_file_name_buffer_length) +{ + +UINT status; +UINT i; +FX_DIR_ENTRY dir_entry; + + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Clear the short name string. */ + dir_entry.fx_dir_entry_short_name[0] = 0; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_DIRECTORY_LONG_NAME_GET, media_ptr, short_file_name, long_file_name, 0, FX_TRACE_DIRECTORY_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Search the system for the supplied short directory name. */ + status = _fx_directory_search(media_ptr, short_file_name, &dir_entry, FX_NULL, FX_NULL); + + /* Determine if the search was successful. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + /* Copy the long name portion into the destination string. */ + i = 0; + while ((i < (FX_MAX_LONG_NAME_LEN - 1)) && (dir_entry.fx_dir_entry_name[i]) && (i < (long_file_name_buffer_length - 1))) + { + + /* Copy a character of the long name into the destination name. */ + long_file_name[i] = dir_entry.fx_dir_entry_name[i]; + + /* Move to next character. */ + i++; + } + + /* Ensure the long file name is NULL terminated. */ + long_file_name[i] = FX_NULL; + + /* Check if the buffer is too short for the name. */ + if ((i == (long_file_name_buffer_length - 1)) && (dir_entry.fx_dir_entry_name[i])) + { + + /* Buffer too short, return error. */ + status = FX_BUFFER_ERROR; + } + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the completion status. */ + return(status); +} + diff --git a/common/src/fx_directory_name_extract.c b/common/src/fx_directory_name_extract.c new file mode 100644 index 0000000..48ebf4d --- /dev/null +++ b/common/src/fx_directory_name_extract.c @@ -0,0 +1,161 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_name_extract PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function extracts the file name from the supplied input */ +/* string. If there is nothing left after the extracted name, a NULL */ +/* is returned to the caller. Otherwise, if something is left, a */ +/* pointer to it is returned. */ +/* */ +/* INPUT */ +/* */ +/* source_ptr Source string pointer */ +/* dest_ptr Destination string pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Pointer to Next Name (if multiple directories) */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +CHAR *_fx_directory_name_extract(CHAR *source_ptr, CHAR *dest_ptr) +{ + +UINT i; + + + /* Set the destination string to NULL. */ + dest_ptr[0] = 0; + + /* Is a backslash present? */ + if ((*source_ptr == '\\') || (*source_ptr == '/')) + { + + /* Advance the string pointer. */ + source_ptr++; + } + + /* Loop to remove any leading spaces. */ + while (*source_ptr == ' ') + { + + /* Position past leading space. */ + source_ptr++; + } + + /* Loop to extract the name. */ + i = 0; + while (*source_ptr) + { + + /* If another backslash is present, break the loop. */ + if ((*source_ptr == '\\') || (*source_ptr == '/')) + { + break; + } + + /* Long name can be at most 255 characters, but are further limited by the + FX_MAX_LONG_NAME_LEN define. */ + if (i == FX_MAX_LONG_NAME_LEN - 1) + { + break; + } + + /* Store the character. */ + dest_ptr[i] = *source_ptr++; + + /* Increment the character counter. */ + i++; + } + + /* NULL-terminate the string. */ + dest_ptr[i] = 0; + + /* Determine if we can backup to the previous character. */ + if (i) + { + + /* Yes, we can move backwards. */ + i--; + } + + /* Get rid of trailing blanks in the destination string. */ + while (dest_ptr[i] == ' ') + { + + /* Set this entry to NULL. */ + dest_ptr[i] = 0; + + /* Backup to the next character. Since leading spaces have been removed, + we know that the index is always greater than 1. */ + i--; + } + + /* Determine if the source string is now at the end. */ + if (*source_ptr == 0) + { + + /* Yes, return a NULL pointer. */ + source_ptr = FX_NULL; + } + + /* Return the last pointer position in the source. */ + return(source_ptr); +} + diff --git a/common/src/fx_directory_name_test.c b/common/src/fx_directory_name_test.c new file mode 100644 index 0000000..29e3cae --- /dev/null +++ b/common/src/fx_directory_name_test.c @@ -0,0 +1,143 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_file.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_name_test PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function first attempts to find the specified directory name. */ +/* If found, the attributes are checked to see if the entry is a */ +/* directory. If not, the appropriate error code is returned to the */ +/* caller. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* directory_name Directory name pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_search Search for the file name in */ +/* the directory structure */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_name_test(FX_MEDIA *media_ptr, CHAR *directory_name) +{ + +UINT status; +FX_DIR_ENTRY dir_entry; + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_directory_name_tests++; +#endif + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Clear the short name string. */ + dir_entry.fx_dir_entry_short_name[0] = 0; + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_DIRECTORY_NAME_TEST, media_ptr, directory_name, 0, 0, FX_TRACE_DIRECTORY_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Search the system for the supplied directory name. */ + status = _fx_directory_search(media_ptr, directory_name, &dir_entry, FX_NULL, FX_NULL); + + /* Determine if the search was successful. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + /* Check to see if the entry is a directory. */ + if (!(dir_entry.fx_dir_entry_attributes & (UCHAR)FX_DIRECTORY)) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the not a directory error code. */ + return(FX_NOT_DIRECTORY); + } + + /* Release media protection. */ + FX_UNPROTECT + + /* Directory name test is complete, return successful status. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_directory_next_entry_find.c b/common/src/fx_directory_next_entry_find.c new file mode 100644 index 0000000..b0a3944 --- /dev/null +++ b/common/src/fx_directory_next_entry_find.c @@ -0,0 +1,437 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_utility.h" + +#ifndef FX_NO_LOCAL_PATH +FX_LOCAL_PATH_SETUP +#endif + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_next_entry_find PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the name of the next entry in the current */ +/* working directory. The function that returns the first name in the */ +/* current directory must be called prior to this function. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* directory_name Destination for directory */ +/* name */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_entry_read Read entries from root dir */ +/* _fx_utility_FAT_entry_read Read FAT entries to calculate */ +/* the sub-directory size */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code and */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_next_entry_find(FX_MEDIA *media_ptr, CHAR *directory_name) +{ + +ULONG i; +UINT status; +UINT temp_status; +ULONG cluster, next_cluster = 0; +ULONG64 directory_size; +FX_DIR_ENTRY entry; +FX_DIR_ENTRY *search_dir_ptr; +FX_PATH *path_ptr; +#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE +UINT index; +CHAR *path_string_ptr = FX_NULL; +#endif + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_directory_next_entry_finds++; +#endif + + /* Setup pointer to media name buffer. */ + entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Clear the short name string. */ + entry.fx_dir_entry_short_name[0] = 0; + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_DIRECTORY_NEXT_ENTRY_FIND, media_ptr, directory_name, 0, 0, FX_TRACE_DIRECTORY_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* First check for a local path pointer stored in the thread control block. This + is only available in ThreadX Version 4 and above. */ + +#ifndef FX_NO_LOCAL_PATH + + if (_tx_thread_current_ptr -> tx_thread_filex_ptr) + { + + /* Setup the default path pointer. */ + path_ptr = (FX_PATH *)_tx_thread_current_ptr -> tx_thread_filex_ptr; + + /* Determine if we are at the root directory. */ + if (path_ptr -> fx_path_directory.fx_dir_entry_name[0]) + { + + /* No, we are not at the root directory. */ + +#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE + + /* Setup pointer to the path. */ + path_string_ptr = ((FX_PATH *)_tx_thread_current_ptr -> tx_thread_filex_ptr) -> fx_path_string; +#endif + + /* Set the internal pointer to the search directory as well. */ + search_dir_ptr = &(path_ptr -> fx_path_directory); + } + else + { + + /* The current default directory is the root so just set the + search directory pointer to NULL. */ + search_dir_ptr = FX_NULL; + } + } + else +#endif + + /* Set the initial search directory to the current working + directory - if there is one. */ + if (media_ptr -> fx_media_default_path.fx_path_directory.fx_dir_entry_name[0]) + { + + /* Setup the path pointer to the global media path. */ + path_ptr = &media_ptr -> fx_media_default_path; + +#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE + + /* Setup pointer to the path. */ + path_string_ptr = media_ptr -> fx_media_default_path.fx_path_string; +#endif + + /* Set the internal pointer to the search directory as well. */ + search_dir_ptr = &(path_ptr -> fx_path_directory); + } + else + { + + /* Setup the path pointer to the global media path. */ + path_ptr = &media_ptr -> fx_media_default_path; + + /* The current default directory is the root so just set the + search directory pointer to NULL. */ + search_dir_ptr = FX_NULL; + } + + /* Calculate the directory size. */ + if (search_dir_ptr) + { + /* Determine the directory size. */ + if (path_ptr -> fx_path_current_entry != 0) + { + + /* Pickup the previously saved directory size. */ + directory_size = search_dir_ptr -> fx_dir_entry_file_size; + } + else + { + + /* This should only be done on the first time into next directory find. */ + + /* Ensure that the search directory's last search cluster is cleared. */ + search_dir_ptr -> fx_dir_entry_last_search_cluster = 0; + + /* Calculate the directory size by counting the allocated + clusters for it. */ + i = 0; + cluster = search_dir_ptr -> fx_dir_entry_cluster; + while (cluster < media_ptr -> fx_media_fat_reserved) + { + + /* Increment the cluster count. */ + i++; + + /* Read the next FAT entry. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &next_cluster); + + /* Check the return status. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + if ((cluster < FX_FAT_ENTRY_START) || (cluster == next_cluster) || (i > media_ptr -> fx_media_total_clusters)) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(FX_FAT_READ_ERROR); + } + + cluster = next_cluster; + } + + /* Now we can calculate the directory size. */ + directory_size = (((ULONG64)media_ptr -> fx_media_bytes_per_sector) * + ((ULONG64)media_ptr -> fx_media_sectors_per_cluster) * i) / + (ULONG64)FX_DIR_ENTRY_SIZE; + + /* Save how many entries there are in the directory. */ + search_dir_ptr -> fx_dir_entry_file_size = directory_size; + } + } + else + { + + /* Directory size is the number of entries in the root directory. */ + directory_size = (ULONG)media_ptr -> fx_media_root_directory_entries; + } + + /* Preset status with an error return. */ + status = FX_NO_MORE_ENTRIES; + + /* Determine if the current entry is inside of the directory's range. */ + while (path_ptr -> fx_path_current_entry < directory_size) + { + + /* Read an entry from the directory. */ + temp_status = _fx_directory_entry_read(media_ptr, search_dir_ptr, + &(path_ptr -> fx_path_current_entry), &entry); + + /* Check for error status. */ + if (temp_status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return error status. */ + return(temp_status); + } + + /* Check to see if the entry has something in it. */ + if (((UCHAR)entry.fx_dir_entry_name[0] == (UCHAR)FX_DIR_ENTRY_FREE) && (entry.fx_dir_entry_short_name[0] == 0)) + { + + /* Current entry is free, skip to next entry and continue the loop. */ + path_ptr -> fx_path_current_entry++; + continue; + } + else if ((UCHAR)entry.fx_dir_entry_name[0] != (UCHAR)FX_DIR_ENTRY_DONE) + { + + /* A valid directory entry is present. */ + + /* Copy the name into the destination. */ + for (i = 0; entry.fx_dir_entry_name[i]; i++) + { + + *directory_name = entry.fx_dir_entry_name[i]; + directory_name++; + } + + /* Place a NULL at the end of the directory name. */ + *directory_name = (CHAR)0; + + /* Increment the current entry for the media. */ + path_ptr -> fx_path_current_entry++; + +#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE + { + UINT v, j; + + + /* If a subsequent search for the same name is done, it will find it immediately. */ + + /* Set the index of the saved name string. */ + v = 0; + + /* First, build the full path and name. */ + if (path_string_ptr) + { + + /* Copy the path into the destination. */ + while ((v < (FX_MAX_LAST_NAME_LEN - 1)) && (path_string_ptr[v])) + { + + /* Copy one character. */ + media_ptr -> fx_media_last_found_name[v] = path_string_ptr[v]; + + /* Move to next character. */ + v++; + } + } + + /* We know there is room at this point, place a directory separator character. */ + media_ptr -> fx_media_last_found_name[v++] = '/'; + + /* Now append the name to the path. */ + j = 0; + while ((v < FX_MAX_LAST_NAME_LEN) && (entry.fx_dir_entry_name[j])) + { + + /* Copy one character. */ + media_ptr -> fx_media_last_found_name[v] = entry.fx_dir_entry_name[j]; + + /* Move to next character. */ + v++; + j++; + } + + /* Null terminate the last name string. */ + if (v < FX_MAX_LAST_NAME_LEN) + { + + /* Null terminate. */ + media_ptr -> fx_media_last_found_name[v] = FX_NULL; + } + else + { + + /* The string is too big, NULL the string so it won't be used in searching. */ + media_ptr -> fx_media_last_found_name[0] = FX_NULL; + } + + /* Determine if there is a search pointer. */ + if (search_dir_ptr) + { + + /* Yes, there is a search directory pointer so save it! */ + media_ptr -> fx_media_last_found_directory = *search_dir_ptr; + + /* Indicate the search directory is valid. */ + media_ptr -> fx_media_last_found_directory_valid = FX_TRUE; + } + else + { + + /* Indicate the search directory is not valid. */ + media_ptr -> fx_media_last_found_directory_valid = FX_FALSE; + } + + /* Copy the directory entry. */ + media_ptr -> fx_media_last_found_entry = entry; + + /* Setup the last found directory entry to point at the last found internal file name. */ + media_ptr -> fx_media_last_found_entry.fx_dir_entry_name = media_ptr -> fx_media_last_found_file_name; + + /* Copy the actual directory name into the cached directory name. */ + for (index = 0; index < FX_MAX_LONG_NAME_LEN; index++) + { + + /* Copy character into the cached directory name. */ + media_ptr -> fx_media_last_found_file_name[index] = entry.fx_dir_entry_name[index]; + + /* See if we have copied the NULL termination character. */ + if (entry.fx_dir_entry_name[index] == (CHAR)FX_NULL) + { + + /* Check to see if we use the break to get out of the loop. */ + if (v < (FX_MAX_LONG_NAME_LEN - 1)) + { + + /* Yes, not at the end of the string, break. */ + break; + } + } + } + } +#endif + + /* Set return status to success. */ + status = FX_SUCCESS; + + /* Get out of the loop. */ + break; + } + else + { + + /* Set the error code. */ + status = FX_NO_MORE_ENTRIES; + + /* Get out of the loop. */ + break; + } + } + + /* Release media protection. */ + FX_UNPROTECT + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fx_directory_next_full_entry_find.c b/common/src/fx_directory_next_full_entry_find.c new file mode 100644 index 0000000..c98519d --- /dev/null +++ b/common/src/fx_directory_next_full_entry_find.c @@ -0,0 +1,515 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_utility.h" + +#ifndef FX_NO_LOCAL_PATH +FX_LOCAL_PATH_SETUP +#endif + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_next_full_entry_find PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the name of the next entry in the current */ +/* working directory and various information about the name. The */ +/* function that returns the first name in the current directory must */ +/* be called prior to this function. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* directory_name Destination for directory */ +/* name */ +/* attributes Destination for attributes */ +/* size Destination for size */ +/* year Destination for year */ +/* month Destination for month */ +/* day Destination for day */ +/* hour Destination for hour */ +/* minute Destination for minute */ +/* second Destination for second */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_entry_read Read entries from root dir */ +/* _fx_utility_FAT_entry_read Read FAT entries */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code and */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_next_full_entry_find(FX_MEDIA *media_ptr, + CHAR *directory_name, UINT *attributes, ULONG *size, + UINT *year, UINT *month, UINT *day, UINT *hour, UINT *minute, UINT *second) +{ + +ULONG i; +UINT status; +UINT temp_status; +ULONG cluster, next_cluster = 0; +ULONG64 directory_size; +FX_DIR_ENTRY entry; +FX_DIR_ENTRY *search_dir_ptr; +FX_PATH *path_ptr; +#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE +UINT index; +CHAR *path_string_ptr = FX_NULL; +#endif + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_directory_next_full_entry_finds++; +#endif + + /* Setup pointer to media name buffer. */ + entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Clear the short name string. */ + entry.fx_dir_entry_short_name[0] = 0; + + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_DIRECTORY_NEXT_FULL_ENTRY_FIND, media_ptr, directory_name, 0, 0, FX_TRACE_DIRECTORY_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* First check for a local path pointer stored in the thread control block. This + is only available in ThreadX Version 4 and above. */ +#ifndef FX_NO_LOCAL_PATH + if (_tx_thread_current_ptr -> tx_thread_filex_ptr) + { + + /* Setup the default path pointer. */ + path_ptr = (FX_PATH *)_tx_thread_current_ptr -> tx_thread_filex_ptr; + + /* Determine if we are at the root directory. */ + if (path_ptr -> fx_path_directory.fx_dir_entry_name[0]) + { + + /* No, we are not at the root directory. */ + +#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE + + /* Setup pointer to the path. */ + path_string_ptr = ((FX_PATH *)_tx_thread_current_ptr -> tx_thread_filex_ptr) -> fx_path_string; +#endif + + /* Set the internal pointer to the search directory as well. */ + search_dir_ptr = &path_ptr -> fx_path_directory; + } + else + { + + /* The current default directory is the root so just set the + search directory pointer to NULL. */ + search_dir_ptr = FX_NULL; + } + } + else +#endif + + /* Set the initial search directory to the current working + directory - if there is one. */ + if (media_ptr -> fx_media_default_path.fx_path_directory.fx_dir_entry_name[0]) + { + + /* Setup the path pointer to the global media path. */ + path_ptr = &media_ptr -> fx_media_default_path; + +#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE + + /* Setup pointer to the path. */ + path_string_ptr = media_ptr -> fx_media_default_path.fx_path_string; +#endif + + /* Set the internal pointer to the search directory as well. */ + search_dir_ptr = &path_ptr -> fx_path_directory; + } + else + { + + /* Setup the path pointer to the global media path. */ + path_ptr = &media_ptr -> fx_media_default_path; + + /* The current default directory is the root so just set the + search directory pointer to NULL. */ + search_dir_ptr = FX_NULL; + } + + /* Calculate the directory size. */ + if (search_dir_ptr) + { + + + /* Determine the directory size. */ + if (path_ptr -> fx_path_current_entry != 0) + { + + /* Pickup the previously saved directory size. */ + directory_size = search_dir_ptr -> fx_dir_entry_file_size; + } + else + { + + /* This should only be done on the first time into next directory find. */ + + /* Ensure that the search directory's last search cluster is cleared. */ + search_dir_ptr -> fx_dir_entry_last_search_cluster = 0; + + /* Calculate the directory size by counting the allocated + clusters for it. */ + i = 0; + cluster = search_dir_ptr -> fx_dir_entry_cluster; + while (cluster < media_ptr -> fx_media_fat_reserved) + { + + /* Increment the cluster count. */ + i++; + + /* Read the next FAT entry. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &next_cluster); + + + /* Check the return status. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + if ((cluster < FX_FAT_ENTRY_START) || (cluster == next_cluster) || (i > media_ptr -> fx_media_total_clusters)) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(FX_FAT_READ_ERROR); + } + + cluster = next_cluster; + } + + /* Now we can calculate the directory size. */ + directory_size = (((ULONG64) media_ptr -> fx_media_bytes_per_sector) * + ((ULONG64) media_ptr -> fx_media_sectors_per_cluster) * i) + / (ULONG64) FX_DIR_ENTRY_SIZE; + + /* Save how many entries there are in the directory. */ + search_dir_ptr -> fx_dir_entry_file_size = directory_size; + } + } + else + { + + /* Directory size is the number of entries in the root directory. */ + directory_size = (ULONG)media_ptr -> fx_media_root_directory_entries; + } + + /* Preset status with an error return. */ + status = FX_NO_MORE_ENTRIES; + + /* Determine if the current entry is inside of the directory's range. */ + while (path_ptr -> fx_path_current_entry < directory_size) + { + + /* Read an entry from the directory. */ + temp_status = _fx_directory_entry_read(media_ptr, search_dir_ptr, + &(path_ptr -> fx_path_current_entry), &entry); + + /* Check for error status. */ + if (temp_status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return error status. */ + return(temp_status); + } + + + /* Check to see if the entry has something in it. */ + if (((UCHAR)entry.fx_dir_entry_name[0] == (UCHAR)FX_DIR_ENTRY_FREE) && (entry.fx_dir_entry_short_name[0] == 0)) + { + + /* Current entry is free, skip to next entry and continue the loop. */ + path_ptr -> fx_path_current_entry++; + continue; + } + + /* Determine if a valid directory entry is present. */ + else if ((UCHAR)entry.fx_dir_entry_name[0] != (UCHAR)FX_DIR_ENTRY_DONE) + { + + /* A valid directory entry is present. */ + + /* Copy the name into the destination. */ + for (i = 0; entry.fx_dir_entry_name[i]; i++) + { + + *directory_name = entry.fx_dir_entry_name[i]; + directory_name++; + } + + /* Place a NULL at the end of the directory name. */ + *directory_name = (CHAR)0; + + /* Determine if the entry's attributes are required. */ + if (attributes) + { + + /* Pickup the entry's attributes. */ + *attributes = entry.fx_dir_entry_attributes; + } + + /* Determine if the entry's size is required. */ + if (size) + { + + /* Pickup the entry's size. */ + *size = (ULONG)entry.fx_dir_entry_file_size; + } + + /* Determine if the entry's year is required. */ + if (year) + { + + /* Pickup the entry's year. */ + *year = ((entry.fx_dir_entry_date >> FX_YEAR_SHIFT) & FX_YEAR_MASK) + FX_BASE_YEAR; + } + + /* Determine if the entry's month is required. */ + if (month) + { + + /* Pickup the entry's month. */ + *month = (entry.fx_dir_entry_date >> FX_MONTH_SHIFT) & FX_MONTH_MASK; + } + + /* Determine if the entry's day is required. */ + if (day) + { + + /* Pickup the entry's day. */ + *day = entry.fx_dir_entry_date & FX_DAY_MASK; + } + + /* Determine if the entry's hour is required. */ + if (hour) + { + + /* Pickup the entry's hour. */ + *hour = (entry.fx_dir_entry_time >> FX_HOUR_SHIFT) & FX_HOUR_MASK; + } + + /* Determine if the entry's minute is required. */ + if (minute) + { + + /* Pickup the entry's minute. */ + *minute = (entry.fx_dir_entry_time >> FX_MINUTE_SHIFT) & FX_MINUTE_MASK; + } + + /* Determine if the entry's second is required. */ + if (second) + { + + /* Pickup the entry's second. */ + *second = (entry.fx_dir_entry_time & FX_SECOND_MASK) * 2; + } + + /* Increment the current entry for the media. */ + path_ptr -> fx_path_current_entry++; + +#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE + { + UINT v, j; + + + /* If a subsequent search for the same name is done, it will find it immediately. */ + + /* Set the index of the saved name string. */ + v = 0; + + /* First, build the full path and name. */ + if (path_string_ptr) + { + + /* Copy the path into the destination. */ + while ((v < (FX_MAX_LAST_NAME_LEN - 1)) && (path_string_ptr[v])) + { + + /* Copy one character. */ + media_ptr -> fx_media_last_found_name[v] = path_string_ptr[v]; + + /* Move to next character. */ + v++; + } + } + + /* We know there is room at this point, place a directory separator character. */ + media_ptr -> fx_media_last_found_name[v++] = '/'; + + /* Now append the name to the path. */ + j = 0; + while ((v < FX_MAX_LAST_NAME_LEN) && (entry.fx_dir_entry_name[j])) + { + + /* Copy one character. */ + media_ptr -> fx_media_last_found_name[v] = entry.fx_dir_entry_name[j]; + + /* Move to next character. */ + v++; + j++; + } + + /* Null terminate the last name string. */ + if (v < FX_MAX_LAST_NAME_LEN) + { + + /* Null terminate. */ + media_ptr -> fx_media_last_found_name[v] = FX_NULL; + } + else + { + + /* The string is too big, NULL the string so it won't be used in searching. */ + media_ptr -> fx_media_last_found_name[0] = FX_NULL; + } + + /* Determine if there is a search pointer. */ + if (search_dir_ptr) + { + + /* Yes, there is a search directory pointer so save it! */ + media_ptr -> fx_media_last_found_directory = *search_dir_ptr; + + /* Indicate the search directory is valid. */ + media_ptr -> fx_media_last_found_directory_valid = FX_TRUE; + } + else + { + + /* Indicate the search directory is not valid. */ + media_ptr -> fx_media_last_found_directory_valid = FX_FALSE; + } + + /* Copy the directory entry. */ + media_ptr -> fx_media_last_found_entry = entry; + + /* Setup the last found directory entry to point at the last found internal file name. */ + media_ptr -> fx_media_last_found_entry.fx_dir_entry_name = media_ptr -> fx_media_last_found_file_name; + + /* Copy the actual directory name into the cached directory name. */ + for (index = 0; index < FX_MAX_LONG_NAME_LEN; index++) + { + + /* Copy character into the cached directory name. */ + media_ptr -> fx_media_last_found_file_name[index] = entry.fx_dir_entry_name[index]; + + /* See if we have copied the NULL termination character. */ + if (entry.fx_dir_entry_name[index] == (CHAR)FX_NULL) + { + + /* Check to see if we use the break to get out of the loop. */ + if (v < (FX_MAX_LONG_NAME_LEN - 1)) + { + + /* Yes, not at the end of the string, break. */ + break; + } + } + } + } +#endif + + /* Set return status to success. */ + status = FX_SUCCESS; + + /* Get out of the loop. */ + break; + } + else + { + /* Set the error code. */ + status = FX_NO_MORE_ENTRIES; + + /* Get out of the loop. */ + break; + } + } + + /* Release media protection. */ + FX_UNPROTECT + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fx_directory_rename.c b/common/src/fx_directory_rename.c new file mode 100644 index 0000000..01b3226 --- /dev/null +++ b/common/src/fx_directory_rename.c @@ -0,0 +1,486 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_file.h" +#include "fx_utility.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_rename PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function first attempts to find the specified directory. */ +/* If found, the rename request is valid and the directory will be */ +/* changed to the new name. Otherwise, if the directory is not found, */ +/* the appropriate error code is returned to the caller. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* old_directory_name Old file directory pointer */ +/* new_directory_name New file directory pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_entry_write Write the new directory entry */ +/* _fx_directory_free_search Search for a free directory */ +/* entry */ +/* _fx_directory_name_extract Extract directory name */ +/* _fx_directory_search Search for the file name in */ +/* the directory structure */ +/* _fx_fault_tolerant_transaction_start Start fault tolerant */ +/* transaction */ +/* _fx_fault_tolerant_transaction_end End fault tolerant transaction*/ +/* _fx_fault_tolerant_recover Recover FAT chain */ +/* _fx_fault_tolerant_reset_log_file Reset the log file */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_rename(FX_MEDIA *media_ptr, CHAR *old_directory_name, CHAR *new_directory_name) +{ + +UINT status; +FX_DIR_ENTRY old_dir_entry; +FX_DIR_ENTRY new_dir_entry; +FX_DIR_ENTRY search_directory; +CHAR *new_name_ptr; +ULONG i; +CHAR *work_ptr; +CHAR alpha, beta; +#ifdef FX_RENAME_PATH_INHERIT +UINT j; +#endif + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_directory_renames++; +#endif + + /* Setup pointers to media name buffers. */ + old_dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + new_dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN * 2; + search_directory.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN * 3; + + /* Clear the short name strings. */ + old_dir_entry.fx_dir_entry_short_name[0] = 0; + new_dir_entry.fx_dir_entry_short_name[0] = 0; + search_directory.fx_dir_entry_short_name[0] = 0; + + /* Determine if the supplied name is less than the maximum supported name size. The + maximum name (FX_MAX_LONG_NAME_LEN) is defined in fx_api.h. */ + i = 0; + work_ptr = (CHAR *)new_directory_name; + while (*work_ptr && (i < FX_MAX_LONG_NAME_LEN)) + { + + /* Determine if the character designates a new path. */ + if ((*work_ptr == '\\') || (*work_ptr == '/')) + { + /* Yes, reset the name size. */ + i = 0; + } + /* Check for leading spaces. */ + else if ((*work_ptr != ' ') || (i != 0)) + { + + /* No leading spaces, increment the name size. */ + i++; + } + + /* Move to the next character. */ + work_ptr++; + } + + /* Determine if the supplied name is valid. */ + if ((i == 0) || (i >= FX_MAX_LONG_NAME_LEN)) + { + + /* Return an invalid name value. */ + return(FX_INVALID_NAME); + } + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_DIRECTORY_RENAME, media_ptr, old_directory_name, new_directory_name, 0, FX_TRACE_DIRECTORY_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Start transaction. */ + _fx_fault_tolerant_transaction_start(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Check for write protect at the media level (set by driver). */ + if (media_ptr -> fx_media_driver_write_protect) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return write protect error. */ + return(FX_WRITE_PROTECT); + } + + /* Search the system for the supplied directory name. */ + status = _fx_directory_search(media_ptr, old_directory_name, &old_dir_entry, &search_directory, FX_NULL); + + /* Determine if the search was successful. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + /* Check to make sure the found entry is a directory. */ + if (!(old_dir_entry.fx_dir_entry_attributes & (UCHAR)(FX_DIRECTORY))) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the not a directory error code. */ + return(FX_NOT_DIRECTORY); + } + +#ifdef FX_RENAME_PATH_INHERIT + + /* Determine if the source directory name has a path and the target directory name does not. */ + if (((old_directory_name[0] == '/') || (old_directory_name[0] == '\\')) && (new_directory_name[0] != '/') && (new_directory_name[0] != '\\')) + { + + /* In this case, we need to prepend the path of the old directory name to that of the new directory name. */ + + /* Setup pointer to the rename buffer. */ + work_ptr = (CHAR *)media_ptr -> fx_media_rename_buffer; + + /* First, copy the path of the old directory name. */ + i = 0; + j = 0; + while ((old_directory_name[i]) && (i < FX_MAXIMUM_PATH)) + { + + /* Copy a character into the rename buffer. */ + *work_ptr++ = old_directory_name[i]; + + /* Determine if this character is directory separator. */ + if ((old_directory_name[i] == '/') || (old_directory_name[i] == '\\')) + { + + /* Yes, directory separator has been found - remember the index. */ + j = i; + } + + /* Move to next position in the old directory name. */ + i++; + } + + /* At this point, we have the path stored in the rename buffer. */ + + /* Position past the last slash or backslash. */ + j++; + + /* Reset the working pointer to the position after the last directory separator. */ + work_ptr = (CHAR *)&(media_ptr -> fx_media_rename_buffer[j]); + + /* Now copy the new directory name into the rename buffer. */ + i = 0; + while ((new_directory_name[i]) && (j < FX_MAXIMUM_PATH)) + { + + /* Copy a character into the rename buffer. */ + *work_ptr++ = new_directory_name[i]; + + /* Move to next character. */ + i++; + j++; + } + + /* Determine if the path was successfully prepended. */ + if (new_directory_name[i]) + { + + /* No, there was not enough room in the destination buffer. */ + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the invalid path error code. */ + return(FX_INVALID_PATH); + } + + /* Place a NULL at the end of the string. */ + *work_ptr = (CHAR)FX_NULL; + + /* At this point, we have successfully prepended the path in the new directory name, override + the new directory name so it is used from now on. */ + new_directory_name = (CHAR *)media_ptr -> fx_media_rename_buffer; + } +#endif + + /* Search the media for the new directory name - including any supplied path. */ + status = _fx_directory_search(media_ptr, new_directory_name, &new_dir_entry, &search_directory, &new_name_ptr); + + /* Determine if the search found anything. */ + if (status == FX_SUCCESS) + { + + /* Determine if the new name simply has an ASCII case change. If so, simply let the processing + continue. */ + i = 0; + do + { + + /* Pickup an old name and new name character and convert to upper case if necessary. */ + alpha = old_directory_name[i]; + if ((alpha >= 'a') && (alpha <= 'z')) + { + + /* Lower case, convert to upper case! */ + alpha = (CHAR)((INT)alpha - 0x20); + } + beta = new_directory_name[i]; + if ((beta >= 'a') && (beta <= 'z')) + { + + /* Lower case, convert to upper case! */ + beta = (CHAR)((INT)beta - 0x20); + } + + /* Now compare the characters. */ + if ((alpha != beta) || (alpha == 0)) + { + + /* Get out of this loop! */ + break; + } + + /* Move to next character. */ + i++; + } while (i < (FX_MAXIMUM_PATH-1)); + + /* Now determine if the names match. */ + if (alpha != beta) + { + + /* Yes, the directory name already exists in the target location. */ + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(FX_ALREADY_CREATED); + } + } + + /* Make sure the name is valid. */ + if (_fx_directory_name_extract(new_name_ptr, &new_dir_entry.fx_dir_entry_name[0])) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection */ + FX_UNPROTECT + + /* Return the error code */ + return(FX_INVALID_NAME); + } + + /* Look for a free slot in the target directory. */ + status = _fx_directory_free_search(media_ptr, &search_directory, &new_dir_entry); + + /* Was a free slot found? */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* No, release protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + /* Extract the new directory name. */ + _fx_directory_name_extract(new_name_ptr, &new_dir_entry.fx_dir_entry_name[0]); + + /* Determine if the name was a long directory name. */ + if (new_dir_entry.fx_dir_entry_long_name_present) + { + + /* Yes, clear the short directory name to force a new one. */ + new_dir_entry.fx_dir_entry_short_name[0] = 0; + } + + /* Setup new attributes for the new directory entry. */ + new_dir_entry.fx_dir_entry_attributes = old_dir_entry.fx_dir_entry_attributes; + new_dir_entry.fx_dir_entry_cluster = old_dir_entry.fx_dir_entry_cluster; + new_dir_entry.fx_dir_entry_file_size = old_dir_entry.fx_dir_entry_file_size; + + /* Save the reserved field. */ + new_dir_entry.fx_dir_entry_reserved = old_dir_entry.fx_dir_entry_reserved; + + /* Set time and date stamps. */ + new_dir_entry.fx_dir_entry_created_time_ms = old_dir_entry.fx_dir_entry_created_time_ms; + new_dir_entry.fx_dir_entry_created_time = old_dir_entry.fx_dir_entry_created_time; + new_dir_entry.fx_dir_entry_created_date = old_dir_entry.fx_dir_entry_created_date; + new_dir_entry.fx_dir_entry_last_accessed_date = old_dir_entry.fx_dir_entry_last_accessed_date; + new_dir_entry.fx_dir_entry_time = old_dir_entry.fx_dir_entry_time; + new_dir_entry.fx_dir_entry_date = old_dir_entry.fx_dir_entry_date; + + + /* Is there a leading dot? */ + if (new_dir_entry.fx_dir_entry_name[0] == '.') + { + + /* Yes, toggle the hidden attribute bit. */ + new_dir_entry.fx_dir_entry_attributes |= FX_HIDDEN; + } + +#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE + + /* Invalidate the directory cache. */ + media_ptr -> fx_media_last_found_name[0] = FX_NULL; +#endif + + /* Now write out the directory entry. */ + status = _fx_directory_entry_write(media_ptr, &new_dir_entry); + + /* Determine if the write was successful. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + /* Set the old directory entry to free. */ + old_dir_entry.fx_dir_entry_name[0] = (CHAR)FX_DIR_ENTRY_FREE; + old_dir_entry.fx_dir_entry_short_name[0] = (CHAR)FX_DIR_ENTRY_FREE; + + /* Now wipe out the old directory entry. */ + status = _fx_directory_entry_write(media_ptr, &old_dir_entry); + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + /* End transaction. */ + status = _fx_fault_tolerant_transaction_end(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Directory rename is complete, return status. */ + return(status); +} + diff --git a/common/src/fx_directory_search.c b/common/src/fx_directory_search.c new file mode 100644 index 0000000..0efe4c4 --- /dev/null +++ b/common/src/fx_directory_search.c @@ -0,0 +1,936 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_utility.h" + +#ifndef FX_NO_LOCAL_PATH +FX_LOCAL_PATH_SETUP +#endif + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_search PORTABLE C */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function searches the media for the supplied name. The search */ +/* routine will find files as well as directory names. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* name_ptr Name pointer */ +/* entry_ptr Pointer to directory entry */ +/* record */ +/* last_dir_ptr Pointer to destination for */ +/* the last directory entry */ +/* last_name_ptr Pointer to the last name */ +/* token that was searched for */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_name_extract Extract directory name from */ +/* input string */ +/* _fx_directory_entry_read Read entries from root dir */ +/* _fx_utility_FAT_entry_read Read FAT entries to calculate */ +/* the sub-directory size */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), and */ +/* added conditional to */ +/* disable media search cache, */ +/* resulting in version 6.1 */ +/* 06-02-2021 Bhupendra Naphade Modified comment(s), and */ +/* added check for */ +/* volume label, */ +/* resulting in version 6.1.7 */ +/* 01-31-2022 William E. Lamie Modified comment(s), and */ +/* fixed path compare, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_search(FX_MEDIA *media_ptr, CHAR *name_ptr, FX_DIR_ENTRY *entry_ptr, + FX_DIR_ENTRY *last_dir_ptr, CHAR **last_name_ptr) +{ + +ULONG i, n; +UINT found; +UINT status; +#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE +UINT v, j; +#endif /* FX_MEDIA_DISABLE_SEARCH_CACHE */ +ULONG cluster, next_cluster = 0; +ULONG64 directory_size; +CHAR *dir_name_ptr; +CHAR *work_ptr; +CHAR *source_name_ptr; +CHAR *destination_name_ptr; +FX_DIR_ENTRY search_dir; +FX_DIR_ENTRY *search_dir_ptr; +CHAR *name, alpha, name_alpha; +#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE +UINT index; +CHAR *path_ptr = FX_NULL; +CHAR *original_name = name_ptr; +#endif + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of directory search requests. */ + media_ptr -> fx_media_directory_searches++; +#endif + + /* Setup pointer to media name buffer. */ + name = media_ptr -> fx_media_name_buffer; + + /* Setup the last directory, if required. */ + if (last_dir_ptr) + { + + /* Set the first character of the directory entry to NULL to + indicate root or no directory. */ + last_dir_ptr -> fx_dir_entry_name[0] = 0; + } + + /* Determine if the file name has a full directory path. */ + if ((*name_ptr == '\\') || (*name_ptr == '/')) + { + + /* Directory name has full path, set the search pointer to NULL. */ + search_dir_ptr = FX_NULL; + } + else + { + + /* Set the initial search directory to the current working + directory - if there is one. */ + + /* First check for a local path pointer stored in the thread control block. This + is only available in ThreadX Version 4 and above. */ +#ifndef FX_NO_LOCAL_PATH + if (_tx_thread_current_ptr -> tx_thread_filex_ptr) + { + + /* Determine if the local directory is not the root directory. */ + if (((FX_PATH *)_tx_thread_current_ptr -> tx_thread_filex_ptr) -> fx_path_directory.fx_dir_entry_name[0]) + { + + /* Start at the current working directory of the media. */ + search_dir = ((FX_PATH *)_tx_thread_current_ptr -> tx_thread_filex_ptr) -> fx_path_directory; + +#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE + + /* Setup pointer to the path. */ + path_ptr = ((FX_PATH *)_tx_thread_current_ptr -> tx_thread_filex_ptr) -> fx_path_string; +#endif + + /* Set the internal pointer to the search directory as well. */ + search_dir_ptr = &search_dir; + } + else + { + + /* We are searching in the root directory. */ + search_dir_ptr = FX_NULL; + } + } + else +#endif + if (media_ptr -> fx_media_default_path.fx_path_directory.fx_dir_entry_name[0]) + { + + /* Start at the current working directory of the media. */ + search_dir = media_ptr -> fx_media_default_path.fx_path_directory; + +#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE + + /* Setup pointer to the path. */ + path_ptr = media_ptr -> fx_media_default_path.fx_path_string; +#endif + + /* Set the internal pointer to the search directory as well. */ + search_dir_ptr = &search_dir; + } + else + { + + /* The current default directory is the root so just set the + search directory pointer to NULL. */ + search_dir_ptr = FX_NULL; + } + } + +#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE + + /* Determine if there is a previously found directory entry. */ + if (media_ptr -> fx_media_last_found_name[0]) + { + + UINT match; + CHAR *temp_ptr, beta; + + /* Yes, there is a previously found directory in our cache. */ + + /* Initialize the index. */ + v = 0; + + /* Determine if there is a full path. */ + if ((*name_ptr == '\\') || (*name_ptr == '/')) + { + + /* Yes, the full path is in the name buffer. Simply compare with what is in + the last search buffer. */ + while ((v < (FX_MAX_LAST_NAME_LEN - 1)) && (name_ptr[v])) + { + + /* Pickup the respective name characters. */ + alpha = name_ptr[v]; + beta = media_ptr -> fx_media_last_found_name[v]; + + /* Ensure directory markers are the same. */ + if (alpha == '\\') + { + alpha = '/'; + } + if (beta == '\\') + { + beta = '/'; + } + + /* Is the name the same? */ + if (alpha != beta) + { + + /* Break out of loop! */ + break; + } + + /* Move to next character. */ + v++; + } + + /* Determine if we have a match. */ + if (name_ptr[v] != media_ptr -> fx_media_last_found_name[v]) + { + match = FX_FALSE; + } + else + { + match = FX_TRUE; + } + } + else + { + + /* Default to found. */ + match = FX_TRUE; + + /* Determine if there is a default path to compare with. */ + if (path_ptr) + { + + /* Yes, compare the current path with what is contained in the last + found buffer. Note that the last found name must have at least one + path separator as well as room for at least one character for a name. */ + while ((v < (FX_MAX_LAST_NAME_LEN - 1)) && (path_ptr[v])) + { + + /* Pickup the respective name characters. */ + alpha = media_ptr -> fx_media_last_found_name[v]; + beta = path_ptr[v]; + + /* Ensure directory markers are the same. */ + if (alpha == '\\') + { + alpha = '/'; + } + if (beta == '\\') + { + beta = '/'; + } + + /* Is the name the same? */ + if (alpha != beta) + { + + /* Break out of loop! */ + break; + } + + /* Move to next character. */ + v++; + } + + /* Determine if we don't have a match... The relative path must be exhausted. */ + if (path_ptr[v]) + { + match = FX_FALSE; + } + } + + /* Determine if we still have a match. */ + if (match) + { + + /* Now examine the rest of the last name and the newly supplied + input name. */ + + /* Determine if a valid directory separator is present. */ + if ((media_ptr -> fx_media_last_found_name[v] != '\\') && + (media_ptr -> fx_media_last_found_name[v] != '/')) + { + + /* Set match to false - invalid directory path separator. */ + match = FX_FALSE; + } + else + { + /* Position past the next directory separator in the + last name string. */ + v++; + } + + /* Yes, the full path is in the name buffer. Simply compare with what is in + the last search buffer. */ + j = 0; + while ((v < (FX_MAX_LAST_NAME_LEN - 1)) && (name_ptr[j]) && (match)) + { + + /* Pickup the respective name characters. */ + alpha = name_ptr[j]; + beta = media_ptr -> fx_media_last_found_name[v]; + + /* Ensure directory markers are the same. */ + if (alpha == '\\') + { + alpha = '/'; + } + if (beta == '\\') + { + beta = '/'; + } + + /* Is the name the same? */ + if (alpha != beta) + { + + /* Break out of loop! */ + break; + } + + /* Move to next character. */ + v++; + j++; + } + + /* Avoid accessing fx_media_last_found_name out of bounds. */ + if (v >= 256) + { + match = FX_FALSE; + } + else if ((match) && (name_ptr[j] != media_ptr -> fx_media_last_found_name[v])) + { + + /* We don't have a match. */ + match = FX_FALSE; + } + } + } + + /* Now determine if we actually found a match. */ + if (match) + { + + /* Save the directory entry name pointer. */ + temp_ptr = entry_ptr -> fx_dir_entry_name; + + /* Copy the saved directory entry. */ + *entry_ptr = media_ptr -> fx_media_last_found_entry; + + /* Restore the directory entry name pointer. */ + entry_ptr -> fx_dir_entry_name = temp_ptr; + + /* Copy the directory name into the destination directory name. */ + for (index = 0; index < FX_MAX_LONG_NAME_LEN; index++) + { + + /* Copy character into the destination. */ + temp_ptr[index] = media_ptr -> fx_media_last_found_file_name[index]; + + /* See if we have copied the NULL termination character. */ + if (temp_ptr[index] == (CHAR)FX_NULL) + { + + /* Determine if we should break here or at the top of the loop. */ + if (index < (FX_MAX_LONG_NAME_LEN - 1)) + { + + /* Yes, break out of the loop early. */ + break; + } + } + } + + /* Determine if there is a search directory to copy. */ + if ((last_dir_ptr) && (media_ptr -> fx_media_last_found_directory_valid)) + { + + /* Yes, there was a search directory... and one is requested in this request as well. + Simply copy it into the destination. */ + + /* First, save the name pointer from the list directory pointer. */ + destination_name_ptr = last_dir_ptr -> fx_dir_entry_name; + + /* Copy the entire directory entry structure. */ + *last_dir_ptr = media_ptr -> fx_media_last_found_directory; + + /* Restore the original name buffer pointer. */ + last_dir_ptr -> fx_dir_entry_name = destination_name_ptr; + + /* Pickup pointer to name to copy. */ + source_name_ptr = media_ptr -> fx_media_last_found_directory.fx_dir_entry_name; + + /* Loop to copy the name into the last directory name buffer. */ + for (n = 0; n < FX_MAX_LONG_NAME_LEN; n++) + { + + /* Copy a character. */ + destination_name_ptr[n] = source_name_ptr[n]; + + /* See if we have copied the NULL termination character. */ + if (source_name_ptr[n] == (CHAR)FX_NULL) + { + + /* Determine if we should break here or at the top of the loop. */ + if (n < (FX_MAX_LONG_NAME_LEN - 1)) + { + + /* Yes, break out of the loop early. */ + break; + } + } + } + } + + /* Return the last name pointer, if required. */ + if (last_name_ptr) + { + + /* Just set the last name to initial name string. */ + *last_name_ptr = temp_ptr; + } + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of directory search cache hits. */ + media_ptr -> fx_media_directory_search_cache_hits++; +#endif + + /* Return success. */ + return(FX_SUCCESS); + } + } + + /* Not a sequential search, invalidate the saved information. */ + media_ptr -> fx_media_last_found_name[0] = FX_NULL; + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_DIR_CACHE_MISS, media_ptr, media_ptr -> fx_media_directory_searches - media_ptr -> fx_media_directory_search_cache_hits, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) +#else + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_DIR_CACHE_MISS, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) +#endif +#endif + + /* Loop to traverse the directory paths to find the specified file. */ + do + { + + /* Remember the last name pointer, if required. */ + if (last_name_ptr) + { + + /* Just set the last name to initial name string. */ + *last_name_ptr = name_ptr; + } + + /* Extract file name. */ + name_ptr = _fx_directory_name_extract(name_ptr, name); + + /* Calculate the directory size. */ + if (search_dir_ptr) + { + + /* Ensure that the search directory's last search cluster is cleared. */ + search_dir_ptr -> fx_dir_entry_last_search_cluster = 0; + + /* Calculate the directory size by counting the allocated + clusters for it. */ + i = 0; + cluster = search_dir_ptr -> fx_dir_entry_cluster; + while (cluster < media_ptr -> fx_media_fat_reserved) + { + + /* Increment the cluster count. */ + i++; + + /* Read the next FAT entry. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &next_cluster); + + /* Check the return status. */ + if (status != FX_SUCCESS) + { + + /* Return the bad status. */ + return(status); + } + + /* Check for error situation. */ + if ((cluster < FX_FAT_ENTRY_START) || (cluster == next_cluster) || (i > media_ptr -> fx_media_total_clusters)) + { + + /* Return the bad status. */ + return(FX_FAT_READ_ERROR); + } + + cluster = next_cluster; + } + + /* Now we can calculate the directory size. */ + directory_size = (((ULONG64) media_ptr -> fx_media_bytes_per_sector) * + ((ULONG64) media_ptr -> fx_media_sectors_per_cluster) * i) + / (ULONG64) FX_DIR_ENTRY_SIZE; + + /* Also save this in the directory entry so we don't have to + calculate it later. */ + search_dir_ptr -> fx_dir_entry_file_size = directory_size; + + /* If required, copy the last search directory entry into the + destination. */ + if (last_dir_ptr) + { + + /* Copy the last search directory into the destination. */ + + /* First, save the name pointer from the list directory pointer. */ + destination_name_ptr = last_dir_ptr -> fx_dir_entry_name; + + /* Copy the entire directory entry structure. */ + *last_dir_ptr = *search_dir_ptr; + + /* Restore the original name buffer pointer. */ + last_dir_ptr -> fx_dir_entry_name = destination_name_ptr; + + /* Pickup pointer to name to copy. */ + source_name_ptr = search_dir_ptr -> fx_dir_entry_name; + + /* Loop to copy the name into the last directory name buffer. */ + for (n = 0; n < FX_MAX_LONG_NAME_LEN; n++) + { + + /* Copy a character. */ + destination_name_ptr[n] = source_name_ptr[n]; + + /* See if we have copied the NULL termination character. */ + if (source_name_ptr[n] == (CHAR) FX_NULL) + { + + /* Determine if we should break here or at the top of the loop. */ + if (n < (FX_MAX_LONG_NAME_LEN - 1)) + { + + /* Yes, break out of the loop early. */ + break; + } + } + } + } + } + else + { + + /* Directory size is the number of entries in the root directory. */ + directory_size = (ULONG)media_ptr -> fx_media_root_directory_entries; + } + + /* Loop through entries in the directory. Yes, this is a + linear search! */ + i = 0; + found = FX_FALSE; + + + do + { + + /* Read an entry from the directory. */ + status = _fx_directory_entry_read(media_ptr, search_dir_ptr, &i, entry_ptr); + + i++; + + /* Check for error status. */ + if (status != FX_SUCCESS) + { + return(status); + } + + /* Determine if this is the last directory entry. */ + if ((UCHAR)entry_ptr -> fx_dir_entry_name[0] == (UCHAR)FX_DIR_ENTRY_DONE) + { + break; + } + + /* Determine if the entry is a volume label entry */ + if ((entry_ptr -> fx_dir_entry_attributes & FX_VOLUME)) + { + continue; + } + + /* Determine if this is an empty entry. */ + if (((UCHAR)entry_ptr -> fx_dir_entry_name[0] == (UCHAR)FX_DIR_ENTRY_FREE) && (entry_ptr -> fx_dir_entry_short_name[0] == 0)) + { + continue; + } + + /* Compare the input name and extension with the directory + entry. */ + work_ptr = &name[0]; + dir_name_ptr = &(entry_ptr -> fx_dir_entry_name[0]); + + /* Loop to compare names. */ + do + { + + /* Pickup character of directory name. */ + alpha = *dir_name_ptr; + + /* Pickup character of name. */ + name_alpha = *work_ptr; + + /* Determine if its case needs to be changed. */ + if ((alpha >= 'a') && (alpha <= 'z')) + { + + /* Yes, make upper case. */ + alpha = (CHAR)((INT)alpha - 0x20); + } + + /* Determine if its case needs to be changed. */ + if ((name_alpha >= 'a') && (name_alpha <= 'z')) + { + + /* Yes, make upper case. */ + name_alpha = (CHAR)((INT)name_alpha - 0x20); + } + + /* Compare name with directory name. */ + if (alpha != name_alpha) + { + + /* The names don't match, get out of the loop. */ + break; + } + + /* Otherwise, increment the name pointers. */ + work_ptr++; + dir_name_ptr++; + } while (*dir_name_ptr); + + /* Determine if the requested name has been found. If so, + return success to the caller. */ + if ((*dir_name_ptr == 0) && (*work_ptr == *dir_name_ptr)) + { + + /* Yes, the name was located. All pertinent directory + information is in the directory entry field. */ + found = FX_TRUE; + } + /* Determine if there is a short name to check. */ + else if (entry_ptr -> fx_dir_entry_short_name[0] != 0) + { + + /* Yes, check for the short part of the name. */ + + /* Compare the input name and extension with the directory entry. */ + work_ptr = &name[0]; + dir_name_ptr = &(entry_ptr -> fx_dir_entry_short_name[0]); + + /* Loop to compare names. */ + do + { + + /* Pickup character of directory name. */ + alpha = *dir_name_ptr; + + /* Pickup character of name. */ + name_alpha = *work_ptr; + + /* Determine if its case needs to be changed. */ + if ((name_alpha >= 'a') && (name_alpha <= 'z')) + { + + /* Yes, make upper case. */ + name_alpha = (CHAR)((INT)name_alpha - 0x20); + } + + /* Compare name with directory name. */ + if (alpha != name_alpha) + { + + /* The names don't match, get out of the loop. */ + break; + } + + /* Otherwise, move the name pointers and increment the + count. */ + work_ptr++; + dir_name_ptr++; + } while (*dir_name_ptr); + + /* Determine if the names match. */ + if ((*dir_name_ptr == 0) && (*work_ptr == *dir_name_ptr)) + { + + /* Yes, the name was located. All pertinent directory + information is in the directory entry field. */ + found = FX_TRUE; + } + } + } while ((i < directory_size) && (!found)); + + /* Now determine if we have a match. */ + if (!found) + { + + /* Return a "not found" status to the caller. */ + return(FX_NOT_FOUND); + } + + /* Determine if the found entry is indeed a sub-directory. */ + if (entry_ptr -> fx_dir_entry_attributes & FX_DIRECTORY) + { + + /* Move the directory search pointer to this entry. */ + search_dir = *entry_ptr; + search_dir_ptr = &search_dir; + + /* Ensure that the search directory's last search cluster is cleared. */ + search_dir_ptr -> fx_dir_entry_last_search_cluster = 0; + + /* Now determine if the new search directory is the root + directory. */ + if (!search_dir_ptr -> fx_dir_entry_cluster) + { + + /* This is a backward link to the root directory. Make + sure this is indicated in the search directory + information. */ + search_dir_ptr -> fx_dir_entry_name[0] = 0; + + /* Determine if we need to remember this in the last + directory searched return area. */ + if (last_dir_ptr) + { + + /* Yes, return this value to the caller. */ + + /* First, save the name pointer from the list directory pointer. */ + destination_name_ptr = last_dir_ptr -> fx_dir_entry_name; + + /* Copy the entire directory entry structure. */ + *last_dir_ptr = *search_dir_ptr; + + /* Restore the original name buffer pointer. */ + last_dir_ptr -> fx_dir_entry_name = destination_name_ptr; + + /* Pickup pointer to name to copy. */ + source_name_ptr = search_dir_ptr -> fx_dir_entry_name; + + /* Loop to copy the name into the last directory name buffer. */ + for (n = 0; n < FX_MAX_LONG_NAME_LEN; n++) + { + + /* Copy a character. */ + destination_name_ptr[n] = source_name_ptr[n]; + } + } + + /* Set the search directory pointer to NULL to indicate + we are at the root directory. */ + search_dir_ptr = FX_NULL; + } + } + else + { + + /* This is not a directory, we better return not found + since we can't continue the search. */ + if (name_ptr) + { + + /* Return not-found status to caller. */ + return(FX_NOT_FOUND); + } + } + } while (name_ptr); + + /* If you reach this point, the directory is found absolutely, since !found will return directly in the loop above. */ +#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE + + + /* At this point, cache the found information. If a subsequent search for the same name is done, + it will return immediately. */ + + /* Set the index of the saved name string. */ + v= 0; + + /* First, build the full path and name. */ + if ((*original_name != '\\') && (*original_name != '/') && (path_ptr)) + { + + /* Copy the path into the destination. */ + while ((v< (FX_MAX_LAST_NAME_LEN - 1)) && (path_ptr[v])) + { + + /* Copy one character. */ + media_ptr -> fx_media_last_found_name[v] = path_ptr[v]; + + /* Move to next character. */ + v++; + } + } + + /* Now see if there is no directory path symbol in the name itself. */ + if ((*original_name != '\\') && (*original_name != '/')) + { + + /* If there is room, place a directory separator character. */ + if (v < (FX_MAX_LAST_NAME_LEN - 1)) + { + media_ptr -> fx_media_last_found_name[v++] = '/'; + } + } + + /* Now append the name to the path. */ + j = 0; + while ((v < FX_MAX_LAST_NAME_LEN) && (original_name[j])) + { + + /* Copy one character. */ + media_ptr -> fx_media_last_found_name[v] = original_name[j]; + + /* Move to next character. */ + v++; + j++; + } + + /* Null terminate the last name string. */ + if (v< FX_MAX_LAST_NAME_LEN) + { + + /* Null terminate. */ + media_ptr -> fx_media_last_found_name[v] = FX_NULL; + } + else + { + + /* The string is too big, NULL the string so it won't be used in searching. */ + media_ptr -> fx_media_last_found_name[0] = FX_NULL; + } + + /* Determine if there is a search pointer. */ + if (search_dir_ptr) + { + + /* Yes, there is a search directory pointer so save it! */ + media_ptr -> fx_media_last_found_directory = *search_dir_ptr; + + /* Indicate the search directory is valid. */ + media_ptr -> fx_media_last_found_directory_valid = FX_TRUE; + } + else + { + + /* Indicate the search directory is not valid. */ + media_ptr -> fx_media_last_found_directory_valid = FX_FALSE; + } + + /* Copy the directory entry. */ + media_ptr -> fx_media_last_found_entry = *entry_ptr; + + /* Setup the directory entry for the last found internal file name. */ + media_ptr -> fx_media_last_found_entry.fx_dir_entry_name = media_ptr -> fx_media_last_found_file_name; + + /* Copy the actual directory name into the cached directory name. */ + for (index = 0; index < FX_MAX_LONG_NAME_LEN; index++) + { + + /* Copy character into the cached directory name. */ + media_ptr -> fx_media_last_found_file_name[index] = entry_ptr -> fx_dir_entry_name[index]; + + /* See if we have copied the NULL termination character. */ + if (entry_ptr -> fx_dir_entry_name[index] == (CHAR)FX_NULL) + { + + /* Check to see if we use the break to get out of the loop. */ + if (index < (FX_MAX_LONG_NAME_LEN - 1)) + { + + /* Yes, not at the end of the string, break. */ + break; + } + } + } +#endif + + return(FX_SUCCESS); +} + diff --git a/common/src/fx_directory_short_name_get.c b/common/src/fx_directory_short_name_get.c new file mode 100644 index 0000000..d63dd70 --- /dev/null +++ b/common/src/fx_directory_short_name_get.c @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_short_name_get PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the short file name via the supplied long file */ +/* name. If the long file name is really a short file name, the short */ +/* file name will be returned. */ +/* */ +/* Note, this API is deprecated as fx_directory_short_name_get_extended*/ +/* should be used. The maximum written size to short_file_name could */ +/* be FX_MAX_SHORT_NAME_LEN. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* long_file_name Pointer to long (max 255) name*/ +/* short_file_name Pointer to short (8.3) name */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_short_name_get_extended Actual short name get service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_short_name_get(FX_MEDIA *media_ptr, CHAR *long_file_name, CHAR *short_file_name) +{ + + /* Call the extended version with maximum short name length. */ + return(_fx_directory_short_name_get_extended(media_ptr, long_file_name, short_file_name, FX_MAX_SHORT_NAME_LEN)); +} + diff --git a/common/src/fx_directory_short_name_get_extended.c b/common/src/fx_directory_short_name_get_extended.c new file mode 100644 index 0000000..e8b6127 --- /dev/null +++ b/common/src/fx_directory_short_name_get_extended.c @@ -0,0 +1,170 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_directory_short_name_get_extended PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the short file name via the supplied long file */ +/* name. If the long file name is really a short file name, the short */ +/* file name will be returned. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* long_file_name Pointer to long (max 255) name*/ +/* short_file_name Pointer to short (8.3) name */ +/* short_file_name_length Buffer length for short name */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_search Search for the file name in */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_directory_short_name_get_extended(FX_MEDIA *media_ptr, CHAR *long_file_name, CHAR *short_file_name, UINT short_file_name_length) +{ + +UINT status; +UINT i; +FX_DIR_ENTRY dir_entry; + + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Clear the short name string. */ + dir_entry.fx_dir_entry_short_name[0] = 0; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_DIRECTORY_SHORT_NAME_GET, media_ptr, long_file_name, short_file_name, 0, FX_TRACE_DIRECTORY_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Search the system for the supplied short directory name. */ + status = _fx_directory_search(media_ptr, long_file_name, &dir_entry, FX_NULL, FX_NULL); + + /* Determine if the search was successful. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + /* Determine if there is a short name. */ + if (dir_entry.fx_dir_entry_short_name[0]) + { + + /* Yes, there is a short name. Copy the short file name portion into the destination string. */ + i = 0; + while ((i < (FX_MAX_SHORT_NAME_LEN - 1)) && (dir_entry.fx_dir_entry_short_name[i]) && (i < (short_file_name_length - 1))) + { + + /* Copy a character of the long file name into the destination name. */ + short_file_name[i] = dir_entry.fx_dir_entry_short_name[i]; + + /* Move to next character. */ + i++; + } + + /* Ensure the short file name is NULL terminated. */ + short_file_name[i] = FX_NULL; + + /* Check if the buffer is too short for the name. */ + if ((i == (short_file_name_length - 1)) && (dir_entry.fx_dir_entry_short_name[i])) + { + + /* Buffer too short, return error. */ + status = FX_BUFFER_ERROR; + } + } + else + { + + /* There is no long file name, simply copy the long file name into the short file name destination. */ + i = 0; + while ((i < (FX_MAX_SHORT_NAME_LEN - 1)) && (dir_entry.fx_dir_entry_name[i]) && (i < (short_file_name_length - 1))) + { + + /* Copy a character of the file name into the destination name. */ + short_file_name[i] = dir_entry.fx_dir_entry_name[i]; + + /* Move to next character. */ + i++; + } + + /* Ensure the short file name is NULL terminated. */ + short_file_name[i] = FX_NULL; + + /* Check if the buffer is too short for the name. */ + if ((i == (short_file_name_length - 1)) && (dir_entry.fx_dir_entry_name[i])) + { + + /* Buffer too short, return error. */ + status = FX_BUFFER_ERROR; + } + } + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the completion status. */ + return(status); +} + diff --git a/common/src/fx_fault_tolerant_add_FAT_log.c b/common/src/fx_fault_tolerant_add_FAT_log.c new file mode 100644 index 0000000..6dcd92f --- /dev/null +++ b/common/src/fx_fault_tolerant_add_FAT_log.c @@ -0,0 +1,113 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Fault Tolerant */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_fault_tolerant.h" + + +#ifdef FX_ENABLE_FAULT_TOLERANT +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_fault_tolerant_add_FAT_log PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function converts FAT write into log entry and add it into */ +/* log file. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* cluster Cluster entry number */ +/* value Next cluster value */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_16_unsigned_write Write a USHORT from memory */ +/* _fx_utility_32_unsigned_write Write a UINT from memory */ +/* */ +/* CALLED BY */ +/* */ +/* _fx_utility_FAT_entry_write */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_fault_tolerant_add_FAT_log(FX_MEDIA *media_ptr, ULONG cluster, ULONG value) +{ +ULONG file_size; +FX_FAULT_TOLERANT_FAT_LOG *fat_log; + + /* Increment the size of the log file. */ + file_size = media_ptr -> fx_media_fault_tolerant_file_size + FX_FAULT_TOLERANT_FAT_LOG_ENTRY_SIZE; + + /* Check whether log file exceeds the buffer. */ + if (file_size > media_ptr -> fx_media_fault_tolerant_memory_buffer_size) + { + + /* Log file exceeds the size of the log buffer. This is a failure. */ + return(FX_NO_MORE_SPACE); + } + + /* Set log pointer. */ + fat_log = (FX_FAULT_TOLERANT_FAT_LOG *)(media_ptr -> fx_media_fault_tolerant_memory_buffer + + media_ptr -> fx_media_fault_tolerant_file_size); + + /* Set log type. */ + _fx_utility_16_unsigned_write((UCHAR *)&fat_log -> fx_fault_tolerant_FAT_log_type, + FX_FAULT_TOLERANT_FAT_LOG_TYPE); + + /* Size of log. */ + _fx_utility_16_unsigned_write((UCHAR *)&fat_log -> fx_fault_tolerant_FAT_log_size, + FX_FAULT_TOLERANT_FAT_LOG_ENTRY_SIZE); + + /* Set cluster and value. */ + _fx_utility_32_unsigned_write((UCHAR *)&fat_log -> fx_fault_tolerant_FAT_log_cluster, cluster); + _fx_utility_32_unsigned_write((UCHAR *)&fat_log -> fx_fault_tolerant_FAT_log_value, value); + + /* Update log information. */ + media_ptr -> fx_media_fault_tolerant_file_size = (USHORT)file_size; + media_ptr -> fx_media_fault_tolerant_total_logs += 1; + + return(FX_SUCCESS); +} +#endif /* FX_ENABLE_FAULT_TOLERANT */ + diff --git a/common/src/fx_fault_tolerant_add_bitmap_log.c b/common/src/fx_fault_tolerant_add_bitmap_log.c new file mode 100644 index 0000000..35bbb05 --- /dev/null +++ b/common/src/fx_fault_tolerant_add_bitmap_log.c @@ -0,0 +1,30 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Fault Tolerant */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_fault_tolerant.h" + + + diff --git a/common/src/fx_fault_tolerant_add_checksum_log.c b/common/src/fx_fault_tolerant_add_checksum_log.c new file mode 100644 index 0000000..35bbb05 --- /dev/null +++ b/common/src/fx_fault_tolerant_add_checksum_log.c @@ -0,0 +1,30 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Fault Tolerant */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_fault_tolerant.h" + + + diff --git a/common/src/fx_fault_tolerant_add_dir_log.c b/common/src/fx_fault_tolerant_add_dir_log.c new file mode 100644 index 0000000..6c9ee6d --- /dev/null +++ b/common/src/fx_fault_tolerant_add_dir_log.c @@ -0,0 +1,132 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Fault Tolerant */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_fault_tolerant.h" + + +#ifdef FX_ENABLE_FAULT_TOLERANT +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_fault_tolerant_add_dir_log PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function converts directory entry write into log entry and add */ +/* it into log file. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* logical_sector Original sector to write to */ +/* offset Offset in original sector */ +/* data Data of log */ +/* data_size Size of data */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_16_unsigned_write Write a USHORT from memory */ +/* _fx_utility_32_unsigned_write Write a ULONG from memory */ +/* _fx_utility_64_unsigned_write Write a ULONG64 from memory */ +/* memcpy Memory Copy */ +/* */ +/* CALLED BY */ +/* */ +/* _fx_directory_entry_write */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), verified */ +/* memcpy usage, */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_fault_tolerant_add_dir_log(FX_MEDIA *media_ptr, ULONG64 logical_sector, ULONG offset, + UCHAR *data, ULONG data_size) +{ +ULONG file_size; +FX_FAULT_TOLERANT_DIR_LOG *dir_log; + + /* Increment the size of the log file. */ + file_size = media_ptr -> fx_media_fault_tolerant_file_size + data_size + FX_FAULT_TOLERANT_DIR_LOG_ENTRY_SIZE; + + /* Check whether log file exceeds the buffer. */ + if (file_size > media_ptr -> fx_media_fault_tolerant_memory_buffer_size) + { + + /* Log file exceeds the size of the log buffer. This is a failure. */ + return(FX_NO_MORE_SPACE); + } + + /* Any data to write? */ + if (data_size == 0) + { + + /* No. Just return. */ + return FX_SUCCESS; + } + + /* Set log pointer. */ + dir_log = (FX_FAULT_TOLERANT_DIR_LOG *)(media_ptr -> fx_media_fault_tolerant_memory_buffer + + media_ptr -> fx_media_fault_tolerant_file_size); + + /* Set log type. */ + _fx_utility_16_unsigned_write((UCHAR *)&dir_log -> fx_fault_tolerant_dir_log_type, + FX_FAULT_TOLERANT_DIR_LOG_TYPE); + + /* Size of log. */ + _fx_utility_16_unsigned_write((UCHAR *)&dir_log -> fx_fault_tolerant_dir_log_size, + data_size + FX_FAULT_TOLERANT_DIR_LOG_ENTRY_SIZE); + + /* Set sector and offset. */ + _fx_utility_64_unsigned_write((UCHAR *)&dir_log -> fx_fault_tolerant_dir_log_sector, logical_sector); + _fx_utility_32_unsigned_write((UCHAR *)&dir_log -> fx_fault_tolerant_dir_log_offset, offset); + + memcpy(media_ptr -> fx_media_fault_tolerant_memory_buffer + /* Use case of memcpy is verified. */ + media_ptr -> fx_media_fault_tolerant_file_size + FX_FAULT_TOLERANT_DIR_LOG_ENTRY_SIZE, + data, data_size); + + + /* Update log information. */ + media_ptr -> fx_media_fault_tolerant_file_size = (USHORT)file_size; + media_ptr -> fx_media_fault_tolerant_total_logs += 1; + + return(FX_SUCCESS); +} +#endif /* FX_ENABLE_FAULT_TOLERANT */ + diff --git a/common/src/fx_fault_tolerant_apply_logs.c b/common/src/fx_fault_tolerant_apply_logs.c new file mode 100644 index 0000000..afb7f23 --- /dev/null +++ b/common/src/fx_fault_tolerant_apply_logs.c @@ -0,0 +1,268 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Fault Tolerant */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_fault_tolerant.h" + + +#ifdef FX_ENABLE_FAULT_TOLERANT +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_fault_tolerant_apply_logs PORTABLE C */ +/* 6.2.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function applies changes to the file system. The changes are */ +/* already recorded in the fault tolerant log file. Therefore this */ +/* function reads the content of the log entries and apply these */ +/* changes to the file system. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_logical_sector_write Write a logical sector */ +/* _fx_utility_logical_sector_read Read a logical sector */ +/* _fx_utility_16_unsigned_read Read a USHORT from memory */ +/* _fx_utility_32_unsigned_read Read a ULONG from memory */ +/* _fx_utility_64_unsigned_read Read a ULONG64 from memory */ +/* _fx_utility_FAT_entry_write Write a FAT entry */ +/* _fx_fault_tolerant_cleanup_FAT_chain Cleanup FAT chain */ +/* memcpy Memory Copy */ +/* _fx_utility_FAT_flush Flush written FAT entries */ +/* _fx_utility_logical_sector_flush Flush written logical sectors */ +/* */ +/* CALLED BY */ +/* */ +/* _fx_fault_tolerant_enable */ +/* _fx_fault_tolerant_transaction_end */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), verified */ +/* memcpy usage, */ +/* resulting in version 6.1 */ +/* 10-31-2022 Tiejun Zhou Modified comment(s), fixed */ +/* overflow in log size check, */ +/* resulting in version 6.2.0 */ +/* */ +/**************************************************************************/ +UINT _fx_fault_tolerant_apply_logs(FX_MEDIA *media_ptr) +{ +UINT status; +ULONG64 log_sector; +ULONG remaining_logs; +ULONG copy_size; +ULONG copy_offset; +UCHAR *current_ptr; +ULONG size; +USHORT log_type; +ULONG log_len; +FX_FAULT_TOLERANT_LOG_HEADER *log_header; +FX_FAULT_TOLERANT_FAT_CHAIN *FAT_chain; +FX_FAULT_TOLERANT_LOG_CONTENT *log_content; +FX_FAULT_TOLERANT_FAT_LOG *fat_log; +FX_FAULT_TOLERANT_DIR_LOG *dir_log; + + /* Set log header, FAT chain and log content pointer. */ + log_header = (FX_FAULT_TOLERANT_LOG_HEADER *)media_ptr -> fx_media_fault_tolerant_memory_buffer; + FAT_chain = (FX_FAULT_TOLERANT_FAT_CHAIN *)(media_ptr -> fx_media_fault_tolerant_memory_buffer + FX_FAULT_TOLERANT_FAT_CHAIN_OFFSET); + log_content = (FX_FAULT_TOLERANT_LOG_CONTENT *)(media_ptr -> fx_media_fault_tolerant_memory_buffer + + FX_FAULT_TOLERANT_LOG_CONTENT_OFFSET); + + /* Find the size of the log file. */ + size = _fx_utility_16_unsigned_read((UCHAR *)&log_header -> fx_fault_tolerant_log_header_total_size); + + /* Extended port-specific processing macro, which is by default defined to white space. */ + FX_FAULT_TOLERANT_APPLY_LOGS_EXTENSION + + size -= FX_FAULT_TOLERANT_LOG_CONTENT_HEADER_SIZE; + + /* Find the number of log entries. */ + remaining_logs = _fx_utility_16_unsigned_read((UCHAR *)&log_content -> fx_fault_tolerant_log_content_count); + + /* Get start address of logs. */ + current_ptr = (UCHAR *)log_content + FX_FAULT_TOLERANT_LOG_CONTENT_HEADER_SIZE; + + /* Loop through all the logs to apply the changes. */ + while (remaining_logs) + { + + /* Obtain log type of this entry. */ + log_type = (USHORT)_fx_utility_16_unsigned_read(current_ptr); + + /* Read log length. */ + log_len = _fx_utility_16_unsigned_read(current_ptr + 2); + + + /* Validate log entry size. */ + if (log_len > size) + { + + /* Something wrong with log file. */ + return(FX_FILE_CORRUPT); + } + + /* Reduce the total log file size. */ + size -= log_len; + + /* Process log entry by type. */ + switch (log_type) + { + case FX_FAULT_TOLERANT_FAT_LOG_TYPE: + + /* This is a FAT log. */ + fat_log = (FX_FAULT_TOLERANT_FAT_LOG *)current_ptr; + + /* Write FAT entry. */ + status = _fx_utility_FAT_entry_write(media_ptr, + _fx_utility_32_unsigned_read((UCHAR *)&fat_log -> fx_fault_tolerant_FAT_log_cluster), + _fx_utility_32_unsigned_read((UCHAR *)&fat_log -> fx_fault_tolerant_FAT_log_value)); + + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + break; + + case FX_FAULT_TOLERANT_DIR_LOG_TYPE: + + /* This is a DIR log. */ + dir_log = (FX_FAULT_TOLERANT_DIR_LOG *)current_ptr; + + log_sector = _fx_utility_64_unsigned_read((UCHAR *)&dir_log -> fx_fault_tolerant_dir_log_sector); + + /* Get the destination sector. */ + status = _fx_utility_logical_sector_read(media_ptr, + log_sector, + media_ptr -> fx_media_memory_buffer, + ((ULONG) 1), FX_DATA_SECTOR); + + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + + /* Set copy information. */ + copy_offset = _fx_utility_32_unsigned_read((UCHAR *)&dir_log -> fx_fault_tolerant_dir_log_offset); + + + copy_size = log_len - FX_FAULT_TOLERANT_DIR_LOG_ENTRY_SIZE; + + if (((ULONG64)copy_offset + (ULONG64)copy_size) > (ULONG64)(media_ptr -> fx_media_memory_size)) + { + return(FX_FILE_CORRUPT); + } + + /* Copy data into destination sector. */ + memcpy(media_ptr -> fx_media_memory_buffer + copy_offset, /* Use case of memcpy is verified. */ + current_ptr + FX_FAULT_TOLERANT_DIR_LOG_ENTRY_SIZE, copy_size); + + + /* Write back the current logical sector. */ + status = _fx_utility_logical_sector_write(media_ptr, + log_sector, + media_ptr -> fx_media_memory_buffer, + ((ULONG) 1), FX_DIRECTORY_SECTOR); + + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + break; + + default: + + /* Wrong type. */ + return(FX_SECTOR_INVALID); + } + + /* Decrement the remaining log counter. */ + remaining_logs--; + + /* Move to start position of next log entry. */ + current_ptr += log_len; + } + + /* Check whether or not to process FAT chain. */ + if (FAT_chain -> fx_fault_tolerant_FAT_chain_flag & FX_FAULT_TOLERANT_FLAG_FAT_CHAIN_VALID) + { + + /* Free old link of FAT. */ + status = _fx_fault_tolerant_cleanup_FAT_chain(media_ptr, FX_FAULT_TOLERANT_FAT_CHAIN_CLEANUP); + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + } + + /* Flush the internal logical sector cache. */ + status = _fx_utility_logical_sector_flush(media_ptr, 1, (ULONG64) media_ptr -> fx_media_total_sectors, FX_FALSE); + + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + + /* Return the bad status. */ + return(status); + } + + /* Flush FAT table. */ +#ifdef FX_FAULT_TOLERANT + + /* Ensure the new FAT chain is properly written to the media. */ + + /* Flush the cached individual FAT entries */ + _fx_utility_FAT_flush(media_ptr); +#endif + + /* Return success. */ + return(FX_SUCCESS); +} +#endif /* FX_ENABLE_FAULT_TOLERANT */ + diff --git a/common/src/fx_fault_tolerant_calculate_checksum.c b/common/src/fx_fault_tolerant_calculate_checksum.c new file mode 100644 index 0000000..13fca29 --- /dev/null +++ b/common/src/fx_fault_tolerant_calculate_checksum.c @@ -0,0 +1,103 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Fault Tolerant */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + +#include "fx_api.h" +#include "fx_fault_tolerant.h" +#include "fx_utility.h" + + +#ifdef FX_ENABLE_FAULT_TOLERANT +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_fault_tolerant_calculate_checksum PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calculates checksum for consecutive data. The size */ +/* of the log file is required to be 4-byte aligned. Therefore this */ +/* checksum routine is able to perform 4-byte access. */ +/* */ +/* INPUT */ +/* */ +/* data Pointer to data */ +/* len Data length */ +/* */ +/* OUTPUT */ +/* */ +/* Computed checksum value */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_32_unsigned_read Read a UINT from memory */ +/* */ +/* CALLED BY */ +/* */ +/* _fx_fault_tolerant_enable */ +/* _fx_fault_tolerant_cleanup_FAT_chain */ +/* _fx_fault_tolerant_reset_log_file */ +/* _fx_fault_tolerant_set_FAT_chain */ +/* _fx_fault_tolerant_transaction_end */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +USHORT _fx_fault_tolerant_calculate_checksum(UCHAR *data, UINT len) +{ +ULONG checksum = 0; +ULONG long_value; + + while (len >= 4) + { + + /* Read first long value. */ + long_value = _fx_utility_32_unsigned_read(data); + + /* Calculate checksum. */ + checksum += (long_value >> 16) + (long_value & 0xFFFF); + + /* Decrease length. */ + len -= sizeof(ULONG); + data += sizeof(ULONG); + } + + /* Trim high 16 bits of checksum. */ + checksum = (checksum & 0xFFFF) + (checksum >> 16); + checksum = (checksum & 0xFFFF) + (checksum >> 16); + + return((USHORT)((~checksum) & 0xFFFF)); +} +#endif /* FX_ENABLE_FAULT_TOLERANT */ + diff --git a/common/src/fx_fault_tolerant_cleanup_FAT_chain.c b/common/src/fx_fault_tolerant_cleanup_FAT_chain.c new file mode 100644 index 0000000..5baf155 --- /dev/null +++ b/common/src/fx_fault_tolerant_cleanup_FAT_chain.c @@ -0,0 +1,356 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Fault Tolerant */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_fault_tolerant.h" + + +#ifdef FX_ENABLE_FAULT_TOLERANT + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_utility_FAT_entry_multiple_sectors_check PORTABLE C */ +/* 6.2.0 */ +/* AUTHOR */ +/* */ +/* Tiejun Zhou, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks whether the FAT entry spans multiple sectors. */ +/* It is only possible when the file system is FAT12. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* cluster Cluster entry number */ +/* */ +/* OUTPUT */ +/* */ +/* FX_TRUE FAT entry spans two sectors */ +/* FX_FALSE FAT entry in one sector */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _fx_fault_tolerant_cleanup_FAT_chain */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 10-31-2022 Tiejun Zhou Initial Version 6.2.0 */ +/* */ +/**************************************************************************/ +static UINT _fx_utility_FAT_entry_multiple_sectors_check(FX_MEDIA *media_ptr, ULONG cluster) +{ +ULONG byte_offset; +ULONG FAT_sector; + + /* Check FAT format. */ + if (!media_ptr -> fx_media_12_bit_FAT) + { + + /* Not FAT12. The FAT entry will be in one sector. */ + return(FX_FALSE); + } + + /* File system is FAT12. */ + /* Calculate the byte offset to the cluster entry. */ + byte_offset = (((ULONG)cluster << 1) + cluster) >> 1; + + /* Calculate the FAT sector the requested FAT entry resides in. */ + FAT_sector = (byte_offset / media_ptr -> fx_media_bytes_per_sector) + + (ULONG)media_ptr -> fx_media_reserved_sectors; + + /* Now calculate the byte offset into this FAT sector. */ + byte_offset = byte_offset - + ((FAT_sector - (ULONG)media_ptr -> fx_media_reserved_sectors) * + media_ptr -> fx_media_bytes_per_sector); + + /* Determine if we are now past the end of the FAT buffer in memory. */ + if (byte_offset == (ULONG)(media_ptr -> fx_media_bytes_per_sector - 1)) + { + + /* Yes, we are past the end of the FAT buffer. */ + return(FX_TRUE); + } + else + { + + /* No, we are not past the end of the FAT buffer. */ + return(FX_FALSE); + } +} + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_fault_tolerant_cleanup_FAT_chain PORTABLE C */ +/* 6.2.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function cleans up FAT chain. In order to prevent failures */ +/* during the link-list walk, and to improve efficiency, this function */ +/* frees the FAT entry chain in sections. For each section, it frees */ +/* FAT entry from the last entry in the chain and works backwards. */ +/* Using this method, if the system fails in the middle of the free */ +/* operation, the head is preserved, and fault tolerant module can */ +/* "redo" this operation next time it starts up. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* operation Recover a failure operation or*/ +/* cleanup old FAT chain */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_FAT_entry_read Read a FAT entry */ +/* _fx_utility_FAT_entry_write Write a FAT entry */ +/* _fx_utility_16_unsigned_write Write a USHORT from memory */ +/* _fx_utility_32_unsigned_write Write a ULONG from memory */ +/* _fx_utility_32_unsigned_read Read a ULONG from memory */ +/* _fx_utility_FAT_flush Flush written FAT entries */ +/* _fx_fault_tolerant_calculate_checksum Compute Checksum of data */ +/* _fx_fault_tolerant_write_log_file Write log file */ +/* _fx_utility_FAT_entry_multiple_sectors_check */ +/* Check sectors for FAT entry */ +/* */ +/* CALLED BY */ +/* */ +/* _fx_fault_tolerant_apply_logs */ +/* _fx_fault_tolerant_recover */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* 10-31-2022 Tiejun Zhou Modified comment(s), */ +/* fixed FAT entry span two */ +/* sectors for FAT12, */ +/* resulting in version 6.2.0 */ +/* */ +/**************************************************************************/ +UINT _fx_fault_tolerant_cleanup_FAT_chain(FX_MEDIA *media_ptr, UINT operation) +{ +UINT status; +ULONG current_cluster; +ULONG next_cluster = 0; +ULONG head_cluster; +ULONG tail_cluster; +ULONG cache_count; +ULONG cache_max = FX_FAULT_TOLERANT_CACHE_SIZE >> 2; +ULONG *cache_ptr = media_ptr -> fx_media_fault_tolerant_cache; +ULONG next_session; +USHORT checksum; +ULONG i; +FX_FAULT_TOLERANT_FAT_CHAIN *FAT_chain; +ULONG FAT_sector; +ULONG last_FAT_sector; + + /* Set FAT chain pointer. */ + FAT_chain = (FX_FAULT_TOLERANT_FAT_CHAIN *)(media_ptr -> fx_media_fault_tolerant_memory_buffer + FX_FAULT_TOLERANT_FAT_CHAIN_OFFSET); + + /* Get head and tail cluster. */ + if (operation == FX_FAULT_TOLERANT_FAT_CHAIN_RECOVER) + { + + /* Recover phase. Cleanup (remove) new FAT chain. */ + head_cluster = _fx_utility_32_unsigned_read((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_head_new); + } + else + { + + /* Cleanup phase. Cleanup (remove) old FAT chain. */ + head_cluster = _fx_utility_32_unsigned_read((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_head_original); + } + + /* At this point, the head_cluster points to the cluster chain that is to be removed. */ + + /* Tail cluster points to the back of the origianal FAT chain where the new chain is attached to. + The remove process terminates once this cluster is encountered. */ + tail_cluster = _fx_utility_32_unsigned_read((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_insertion_back); + + /* Are there any clusters to free? */ + if ((head_cluster < FX_FAT_ENTRY_START) || (head_cluster >= media_ptr -> fx_media_fat_reserved)) + { + return(FX_SUCCESS); + } + + /* To optimize the deletion process of a long FAT chain, the deletion is done in sessions. During one session, + a set of FAT entries are removed. If one session is interrupted, after restart the deletion process resumes and + picks up from where it left. + + During one deletion session, all FAT entries are between head_cluster(inclusive) and next_session (exclusive). + */ + next_session = _fx_utility_32_unsigned_read((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_next_deletion); + + /* Loop to cleanup all FAT entries. */ + while ((head_cluster >= FX_FAT_ENTRY_START) && (head_cluster < media_ptr -> fx_media_fat_reserved)) + { + + /* Initialize. */ + current_cluster = head_cluster; + cache_count = 0; + + /* Loop from head cluster to find entries to cleanup and store this information into cache. */ + do + { + + /* Get the next FAT entry. */ + status = _fx_utility_FAT_entry_read(media_ptr, current_cluster, &next_cluster); + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + + /* Whether or not the cluster is occupied. */ + if (next_cluster == FX_FREE_CLUSTER) + { + + /* Current cluster has already been released. */ + break; + } + + /* Cache the FAT list. */ + cache_ptr[cache_count++] = current_cluster; + + /* Check whether FAT entry spans multiple sectors. */ + if (_fx_utility_FAT_entry_multiple_sectors_check(media_ptr, current_cluster)) + { + if (head_cluster == next_session || next_session == FX_FREE_CLUSTER) + { + next_session = next_cluster; + } + break; + } + + /* Move to next cluster. */ + current_cluster = next_cluster; + } while ((next_cluster >= FX_FAT_ENTRY_START) && + (next_cluster < media_ptr -> fx_media_fat_reserved) && + (next_cluster != tail_cluster) && + (cache_count < cache_max)); + + /* Get next session. */ + if (cache_count == cache_max) + { + next_session = next_cluster; + } + + /* Update head cluster and next session into log file. */ + _fx_utility_32_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_next_deletion, next_session); + if (operation == FX_FAULT_TOLERANT_FAT_CHAIN_RECOVER) + { + _fx_utility_32_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_head_new, head_cluster); + } + else + { + _fx_utility_32_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_head_original, head_cluster); + } + + /* Update checksum. */ + _fx_utility_16_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_checksumm, 0); + checksum = _fx_fault_tolerant_calculate_checksum((UCHAR *)FAT_chain, FX_FAULT_TOLERANT_FAT_CHAIN_SIZE); + _fx_utility_16_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_checksumm, checksum); + + /* Flush the fault tolerant log into the file system. */ + _fx_fault_tolerant_write_log_file(media_ptr, 0); + + /* Loop to free FAT in cache from back to front. */ + if (cache_count > 0) + { + + /* Get sector of last FAT entry. */ + last_FAT_sector = _fx_utility_FAT_sector_get(media_ptr, cache_ptr[cache_count - 1]); + i = cache_count; + while (i > 0) + { + i--; + + /* Get sector of current FAT entry. */ + FAT_sector = _fx_utility_FAT_sector_get(media_ptr, cache_ptr[i]); + if (FAT_sector != last_FAT_sector) + { + + /* Current FAT entry is located in different sector. Force flush. */ + /* Flush the cached individual FAT entries */ + _fx_utility_FAT_flush(media_ptr); + + /* Update sector of current FAT entry. */ + last_FAT_sector = FAT_sector; + } + + /* Free current_cluster. */ + status = _fx_utility_FAT_entry_write(media_ptr, cache_ptr[i], FX_FREE_CLUSTER); + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + + } + + /* Increase the available clusters in the media control block. */ + media_ptr -> fx_media_available_clusters += cache_count; + } + + /* Flush the cached individual FAT entries */ + _fx_utility_FAT_flush(media_ptr); + + + /* Get head cluster. */ + if (head_cluster == next_session) + { + break; + } + head_cluster = next_session; + } + + return(FX_SUCCESS); +} +#endif /* FX_ENABLE_FAULT_TOLERANT */ diff --git a/common/src/fx_fault_tolerant_create_log_file.c b/common/src/fx_fault_tolerant_create_log_file.c new file mode 100644 index 0000000..f1852cb --- /dev/null +++ b/common/src/fx_fault_tolerant_create_log_file.c @@ -0,0 +1,233 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Fault Tolerant */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_directory.h" +#include "fx_fault_tolerant.h" + + +#ifdef FX_ENABLE_FAULT_TOLERANT +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_fault_tolerant_create_log_file PORTABLE C */ +/* 6.1.11 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function finds an available cluster used by log file. Then it */ +/* sets the cluster number to boot sector. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_FAT_entry_read Read a FAT entry */ +/* _fx_utility_FAT_entry_write Write a FAT entry */ +/* _fx_utility_FAT_flush Flush written FAT entries */ +/* _fx_utility_32_unsigned_write Write a ULONG from memory */ +/* _fx_utility_logical_sector_read Read a logical sector */ +/* */ +/* CALLED BY */ +/* */ +/* _fx_fault_tolerant_enable */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s), replaced */ +/* sector size constant, */ +/* resulting in version 6.1.10 */ +/* 04-25-2022 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1.11 */ +/* */ +/**************************************************************************/ +UINT _fx_fault_tolerant_create_log_file(FX_MEDIA *media_ptr) +{ +ULONG clusters; +ULONG FAT_index; +ULONG FAT_value = 0; +UINT found; +UINT status; +UINT i; + + /* Yes. Create a log file. */ + /* First find a free cluster. */ + if (media_ptr -> fx_media_available_clusters < media_ptr -> fx_media_fault_tolerant_clusters) + { + + /* Out of disk space. */ + return(FX_NO_MORE_SPACE); + } + + /* Now we need to find the consecutive clusters. */ + clusters = media_ptr -> fx_media_fault_tolerant_clusters; + FAT_index = FX_FAT_ENTRY_START; + found = FX_FALSE; + while (FAT_index <= (media_ptr -> fx_media_total_clusters - clusters + FX_FAT_ENTRY_START)) + { + + /* Determine if enough consecutive FAT entries are available. */ + i = 0; + + + do + { + + /* Read a FAT entry. */ + status = _fx_utility_FAT_entry_read(media_ptr, (FAT_index + i), &FAT_value); + + /* Check for a successful status. */ + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + + /* Determine if the entry is free. */ + if (FAT_value != FX_FREE_CLUSTER) + { + break; + } + + /* Otherwise, increment the consecutive FAT indices. */ + i++; + } while (i < clusters); + + /* Determine if we found enough FAT entries. */ + if (i >= clusters) + { + + /* Yes, we have found enough FAT entries - set the found + flag and get out of this loop. */ + found = FX_TRUE; + break; + } + else + { + /* Position to the next possibly free FAT entry. */ + FAT_index = FAT_index + i + 1; + } + } + + /* Determine if we found enough consecutive clusters to satisfy the + request. */ + if (!found) + { + + /* Not enough contiguous space on the media. Return error status. */ + return(FX_NO_MORE_SPACE); + } + + /* Update the link pointers in the new clusters. */ + for (i = 0; i < (clusters - 1); i++) + { + + /* Update the cluster links. Since the allocation is + sequential, we just have to link each FAT entry to the + next one. */ + status = _fx_utility_FAT_entry_write(media_ptr, FAT_index + i, FAT_index + i + 1); + + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + + } + + /* Now place an EOF in the last cluster entry. */ + status = _fx_utility_FAT_entry_write(media_ptr, FAT_index + clusters - 1, media_ptr -> fx_media_fat_last); + + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + + +#ifdef FX_FAULT_TOLERANT + + /* Flush the cached individual FAT entries */ + _fx_utility_FAT_flush(media_ptr); + +#endif /* FX_FAULT_TOLERANT */ + + /* Write start cluster for the file tolerant log file into the boot sector. */ + _fx_utility_32_unsigned_write(media_ptr -> fx_media_fault_tolerant_memory_buffer + FX_FAULT_TOLERANT_BOOT_INDEX, FAT_index); + + /* Write the boot sector. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_BOOT_WRITE; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = media_ptr -> fx_media_fault_tolerant_memory_buffer; + media_ptr -> fx_media_driver_sectors = 1; + media_ptr -> fx_media_driver_sector_type = FX_BOOT_SECTOR; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_BOOT_WRITE, media_ptr, media_ptr -> fx_media_fault_tolerant_memory_buffer, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Invoke the driver to read the boot sector. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Determine if the boot sector was read correctly. */ + if (media_ptr -> fx_media_driver_status != FX_SUCCESS) + { + + /* Return the boot sector error status. */ + return(FX_BOOT_ERROR); + } + + + /* Set the start cluster. */ + media_ptr -> fx_media_fault_tolerant_start_cluster = FAT_index; + + /* Decrease available cluster. */ + media_ptr -> fx_media_available_clusters = + media_ptr -> fx_media_available_clusters - clusters; + + /* Return success. */ + return(FX_SUCCESS); +} +#endif /* FX_ENABLE_FAULT_TOLERANT */ + diff --git a/common/src/fx_fault_tolerant_enable.c b/common/src/fx_fault_tolerant_enable.c new file mode 100644 index 0000000..bb3d011 --- /dev/null +++ b/common/src/fx_fault_tolerant_enable.c @@ -0,0 +1,500 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Fault Tolerant */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_directory.h" +#include "fx_fault_tolerant.h" + + +#ifdef FX_ENABLE_FAULT_TOLERANT +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_fault_tolerant_enable PORTABLE C */ +/* 6.2.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enables the FileX Fault Tolerant feature. It first */ +/* searches for a valid log file. A valid log file indicates the */ +/* previous write operation failed, and appropriate action now must */ +/* be taken to restore the integrity of the file system. Once the */ +/* recovery effort is completed, the file system is properly restored. */ +/* An empty log file indicates the previous write operation was */ +/* successfully completed and no action needs to be taken at this */ +/* point. If the file system does not have a log file, or the */ +/* checksum is not valid, it is an indication either the file system */ +/* is not under the protection of FileX Fault Tolerant. A new log */ +/* file is created. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* memory_buffer Pointer to memory buffer. */ +/* memory_size Size of memory buffer. */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_fault_tolerant_calculate_checksum Compute Checksum of data */ +/* _fx_fault_tolerant_apply_logs Apply logs into file system */ +/* _fx_fault_tolerant_recover Recover FAT chain */ +/* _fx_fault_tolerant_reset_log_file Reset the log file */ +/* _fx_fault_tolerant_read_log_file Read log file to cache */ +/* _fx_utility_FAT_entry_read Read a FAT entry */ +/* _fx_utility_16_unsigned_read Read a USHORT from memory */ +/* _fx_utility_32_unsigned_read Read a ULONG from memory */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* 10-31-2022 Tiejun Zhou Modified comment(s), */ +/* fixed memory buffer when */ +/* cache is disabled, */ +/* resulting in version 6.2.0 */ +/* */ +/**************************************************************************/ +UINT _fx_fault_tolerant_enable(FX_MEDIA *media_ptr, VOID *memory_buffer, UINT memory_size) +{ +ULONG start_cluster; +ULONG FAT_value; +UINT status; +ULONG checksum; +ULONG total_size; +FX_FAULT_TOLERANT_LOG_HEADER *log_header; +FX_FAULT_TOLERANT_FAT_CHAIN *FAT_chain; +ULONG cluster_number; +ULONG i, j; +ULONG FAT_entry, FAT_sector, FAT_read_sectors; +ULONG bytes_in_buffer; +ULONG clusters; +ULONG bytes_per_sector; +ULONG bytes_per_cluster; + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Calculate clusters needed for fault tolerant log. */ + bytes_per_sector = media_ptr -> fx_media_bytes_per_sector; + bytes_per_cluster = bytes_per_sector * media_ptr -> fx_media_sectors_per_cluster; + + if (bytes_per_cluster == 0) + { + + /* Release media protection. */ + FX_UNPROTECT + + return(FX_MEDIA_INVALID); + } + + clusters = (FX_FAULT_TOLERANT_MAXIMUM_LOG_FILE_SIZE + bytes_per_cluster - 1) / bytes_per_cluster; + media_ptr -> fx_media_fault_tolerant_clusters = clusters; + + /* Check buffer size requirement. */ + if (memory_size < bytes_per_sector) + { + + /* Release media protection. */ + FX_UNPROTECT + + return(FX_NOT_ENOUGH_MEMORY); + } + + + if (media_ptr -> fx_media_FAT32_additional_info_sector) + { + + /* A 32-bit FAT is present. Read directly into the logical sector + cache memory to optimize I/O on larger devices. Since we are looking for + values of zero, endian issues are not important. */ + /* Force update the available cluster. */ + +#ifndef FX_DISABLE_CACHE + /* Invalidate the current logical sector cache. */ + _fx_utility_logical_sector_flush(media_ptr, ((ULONG64) 1), (ULONG64) (media_ptr -> fx_media_total_sectors), FX_TRUE); + + /* Reset the memory pointer. */ + media_ptr -> fx_media_memory_buffer = media_ptr -> fx_media_sector_cache[0].fx_cached_sector_memory_buffer; +#endif /* FX_DISABLE_CACHE */ + + /* Reset the available cluster. */ + media_ptr -> fx_media_available_clusters = 0; + + /* Loop through all FAT sectors in the primary FAT. The first two entries are + examined in this loop, but they are always unavailable. */ + cluster_number = 0; +#ifndef FX_DISABLE_CACHE + for (i = 0; i < media_ptr -> fx_media_sectors_per_FAT; i = i + media_ptr -> fx_media_sector_cache_size) + { + + /* Calculate the starting next FAT sector. */ + FAT_sector = media_ptr -> fx_media_reserved_sectors + i; + + /* Calculate how many sectors to read. */ + FAT_read_sectors = media_ptr -> fx_media_sectors_per_FAT - i; + + /* Determine if there is not enough memory to read the remaining FAT sectors. */ + if (FAT_read_sectors > media_ptr -> fx_media_sector_cache_size) + { + FAT_read_sectors = media_ptr -> fx_media_sector_cache_size; + } +#else + /* Reset the buffer sector. */ + media_ptr -> fx_media_memory_buffer_sector = (ULONG64)-1; + for (i = 0; i < media_ptr -> fx_media_sectors_per_FAT; i++) + { + + /* Calculate the starting next FAT sector. */ + FAT_sector = media_ptr -> fx_media_reserved_sectors + i; + + /* Calculate how many sectors to read. */ + FAT_read_sectors = 1; +#endif /* FX_DISABLE_CACHE */ + + /* Read the FAT sectors directly from the driver. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_READ; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = media_ptr -> fx_media_memory_buffer; + media_ptr -> fx_media_driver_logical_sector = FAT_sector; + media_ptr -> fx_media_driver_sectors = FAT_read_sectors; + media_ptr -> fx_media_driver_sector_type = FX_FAT_SECTOR; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_READ, media_ptr, FAT_sector, FAT_read_sectors, media_ptr -> fx_media_memory_buffer, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Invoke the driver to read the FAT sectors. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Determine if the read was successful. */ + if (media_ptr -> fx_media_driver_status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + return(FX_FAT_READ_ERROR); + } + + /* Calculate the number of bytes in the buffer. */ + bytes_in_buffer = (media_ptr -> fx_media_bytes_per_sector * FAT_read_sectors); + + /* Walk through the sector cache memory to search for available clusters and the first + available if not already found. */ + for (j = 0; j < bytes_in_buffer;) + { + + /* Pickup 32-bit FAT entry. */ + FAT_entry = *((ULONG *)&(media_ptr -> fx_media_memory_buffer[j])); + + /* Advance to next FAT entry. */ + j = j + 4; + + /* Determine if the FAT entry is free. */ + if (FAT_entry == FX_FREE_CLUSTER) + { + + /* Entry is free, increment available clusters. */ + media_ptr -> fx_media_available_clusters++; + + /* Determine if the starting free cluster has been found yet. */ + if (media_ptr -> fx_media_cluster_search_start == 0) + { + + /* Remember the first free cluster to start further searches from. */ + media_ptr -> fx_media_cluster_search_start = cluster_number; + } + } + + /* Increment the cluster number. */ + cluster_number++; + + /* Determine if we have reviewed all FAT entries. */ + if (cluster_number >= (media_ptr -> fx_media_total_clusters + FX_FAT_ENTRY_START)) + { + + /* Yes, we have looked at all the FAT entries. */ + + /* Ensure that the outer loop terminates as well. */ + i = media_ptr -> fx_media_sectors_per_FAT; + break; + } + } + } + } + + /* Store memory buffer and size. */ + media_ptr -> fx_media_fault_tolerant_memory_buffer = (UCHAR *)memory_buffer; + if (memory_size > (clusters * bytes_per_cluster)) + { + media_ptr -> fx_media_fault_tolerant_memory_buffer_size = clusters * bytes_per_cluster; + } + else + { + media_ptr -> fx_media_fault_tolerant_memory_buffer_size = memory_size / bytes_per_sector * bytes_per_sector; + } + + /* Read the boot sector from the device. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_BOOT_READ; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = (UCHAR *)memory_buffer; + media_ptr -> fx_media_driver_sectors = 1; + media_ptr -> fx_media_driver_sector_type = FX_BOOT_SECTOR; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_BOOT_READ, media_ptr, memory_buffer, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Invoke the driver to read the boot sector. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Determine if the boot sector was read correctly. */ + if (media_ptr -> fx_media_driver_status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the boot sector error status. */ + return(FX_BOOT_ERROR); + } + + /* Check whether the boot index is used. */ + start_cluster = _fx_utility_32_unsigned_read((UCHAR *)memory_buffer + FX_FAULT_TOLERANT_BOOT_INDEX); + if (start_cluster != 0) + { + + /* The location of the fault tolerant log file is found. Need to verify the integrity of the log file. */ + if ((start_cluster >= FX_FAT_ENTRY_START) && (start_cluster < media_ptr -> fx_media_fat_reserved)) + { + + /* Check whether this cluster is used. */ + for (i = 0; i < clusters; i++) + { + + /* Read FAT entry. */ + status = _fx_utility_FAT_entry_read(media_ptr, start_cluster + i, &FAT_value); + + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + if (i < clusters - 1) + { + if (FAT_value != (start_cluster + i + 1)) + { + + /* Mark invalid. */ + start_cluster = 0; + break; + } + } + else if (FAT_value != media_ptr -> fx_media_fat_last) + { + + /* Mark invalid. */ + start_cluster = 0; + break; + } + } + + /* Is this FAT entry occupied by log file? */ + if (start_cluster) + { + + /* Set the start cluster. */ + media_ptr -> fx_media_fault_tolerant_start_cluster = start_cluster; + + /* Read log file from file system to memory. */ + status = _fx_fault_tolerant_read_log_file(media_ptr); + + /* Check for good completion status. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* Set log header and FAT chain pointer. */ + log_header = (FX_FAULT_TOLERANT_LOG_HEADER *)media_ptr -> fx_media_fault_tolerant_memory_buffer; + FAT_chain = (FX_FAULT_TOLERANT_FAT_CHAIN *)(media_ptr -> fx_media_fault_tolerant_memory_buffer + + FX_FAULT_TOLERANT_FAT_CHAIN_OFFSET); + + /* Verify ID field. */ + if (_fx_utility_32_unsigned_read((UCHAR *)&log_header -> fx_fault_tolerant_log_header_id) == FX_FAULT_TOLERANT_ID) + { + + /* Calculate checksum of log header. */ + checksum = _fx_fault_tolerant_calculate_checksum((UCHAR *)log_header, + FX_FAULT_TOLERANT_LOG_HEADER_SIZE); + + if (checksum == 0) + { + + /* Fault tolerant log file is valid. */ + /* Initialize file size. */ + total_size = _fx_utility_16_unsigned_read((UCHAR *)&log_header -> fx_fault_tolerant_log_header_total_size); + media_ptr -> fx_media_fault_tolerant_file_size = total_size; + + + /* Verify the checksum of the FAT chain. */ + checksum = _fx_fault_tolerant_calculate_checksum((UCHAR *)FAT_chain, + FX_FAULT_TOLERANT_FAT_CHAIN_SIZE); + + if (checksum == 0) + { + + /* Checksum of FAT chain is correct. */ + + if (total_size > (FX_FAULT_TOLERANT_LOG_CONTENT_OFFSET + FX_FAULT_TOLERANT_LOG_HEADER_SIZE)) + { + + /* Log content is present. */ + /* Now verify the checksum of log content. */ + checksum = _fx_fault_tolerant_calculate_checksum((media_ptr -> fx_media_fault_tolerant_memory_buffer + + FX_FAULT_TOLERANT_LOG_CONTENT_OFFSET), + (total_size - FX_FAULT_TOLERANT_LOG_CONTENT_OFFSET)); + if (checksum == 0) + { + + /* Checksum of log content is correct. */ + + /* Extended port-specific processing macro, which is by default defined to white space. */ + FX_FAULT_TOLERANT_ENABLE_EXTENSION + + /* This is the situation where the log file contains log entries. This is an indication + that previous write operation did not complete successfully. Need to apply the log entries + to recover the previous write operation, effectively to finish up the previous write operation. */ + status = _fx_fault_tolerant_apply_logs(media_ptr); + } + } + else + { + + /* Extended port-specific processing macro, which is by default defined to white space. */ + FX_FAULT_TOLERANT_ENABLE_EXTENSION + + /* The log file does not contain log content but the FAT chain operation information is present. + This is the situation where the FAT chain has been modified but the rest of the content of these + clusters are not updated yet. In this situation, the previous FAT chain operation needs to be + reverted to restore the file system back to its state prior to the write operation. */ + status = _fx_fault_tolerant_recover(media_ptr); + } + + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + } + } + } + } + } + else + { + + /* Not a valid cluster number. Set the flag to create a new log file. */ + start_cluster = 0; + } + } + + /* Check whether or not to create a log file. */ + if (start_cluster == 0) + { + + /* Create log file. */ + status = _fx_fault_tolerant_create_log_file(media_ptr); + + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + } + + /* Reset log file. */ + status = _fx_fault_tolerant_reset_log_file(media_ptr); + + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* Mark fault tolerant feature is enabled. */ + media_ptr -> fx_media_fault_tolerant_enabled = FX_TRUE; + + /* Reset the transaction count. */ + media_ptr -> fx_media_fault_tolerant_transaction_count = 0; + + /* Initialize the sector number of cached FAT entries. */ + media_ptr -> fx_media_fault_tolerant_cached_FAT_sector = 0; + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(FX_SUCCESS); +} + +#endif /* FX_ENABLE_FAULT_TOLERANT */ + diff --git a/common/src/fx_fault_tolerant_read_FAT.c b/common/src/fx_fault_tolerant_read_FAT.c new file mode 100644 index 0000000..43f982a --- /dev/null +++ b/common/src/fx_fault_tolerant_read_FAT.c @@ -0,0 +1,167 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Fault Tolerant */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_fault_tolerant.h" + + +#ifdef FX_ENABLE_FAULT_TOLERANT +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_fault_tolerant_read_FAT PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads value of a FAT entry from log file if it */ +/* exists. During file or directory operations with Fault Tolerant */ +/* protection, intermediate operations are written to the fault */ +/* tolerant log files. Therefore, FAT-entry read should search for */ +/* fault tolerant log before the request can be passed to normal FAT */ +/* entry read routine. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* cluster Cluster entry number */ +/* value Pointer to value for the entry*/ +/* log_type FAT or bitmap */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_16_unsigned_read Read a USHORT from memory */ +/* _fx_utility_32_unsigned_read Read a ULONG from memory */ +/* */ +/* CALLED BY */ +/* */ +/* _fx_utility_FAT_entry_read */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_fault_tolerant_read_FAT(FX_MEDIA *media_ptr, ULONG cluster, ULONG *value, ULONG log_type) +{ +ULONG logs_remaining; +UCHAR *current_ptr; +UCHAR found = FX_FALSE; +ULONG size; +USHORT type; +ULONG log_len; +FX_FAULT_TOLERANT_FAT_LOG *fat_log; + + + /* Get fault tolerant data. */ + logs_remaining = media_ptr -> fx_media_fault_tolerant_total_logs; + + /* Any redo logs? */ + if (logs_remaining == 0) + { + + /* No. Just return. */ + return(FX_READ_CONTINUE); + } + + /* Get size of all logs. */ + size = media_ptr -> fx_media_fault_tolerant_file_size - FX_FAULT_TOLERANT_LOG_CONTENT_OFFSET; + + /* Get the first log pointer. */ + current_ptr = media_ptr -> fx_media_fault_tolerant_memory_buffer + + FX_FAULT_TOLERANT_LOG_CONTENT_OFFSET + + FX_FAULT_TOLERANT_LOG_CONTENT_HEADER_SIZE; + + /* Loop throught all FAT logs. */ + while (logs_remaining) + { + + /* Check log type. */ + type = (USHORT)_fx_utility_16_unsigned_read(current_ptr); + + /* Get log length. */ + log_len = _fx_utility_16_unsigned_read(current_ptr + 2); + + /* Validate the size value. */ + if (log_len > size) + { + + /* Log file is corrupted. Nothing can be done. Return.*/ + return(FX_FILE_CORRUPT); + } + size -= log_len; + + /* Check log type. */ + if (type == log_type) + { + + /* This is the log with same type . */ + /* Get the log pointer. */ + fat_log = (FX_FAULT_TOLERANT_FAT_LOG *)current_ptr; + + /* Is this FAT log entry the one looking for? */ + if (_fx_utility_32_unsigned_read((UCHAR *)&fat_log -> fx_fault_tolerant_FAT_log_cluster) == cluster) + { + + /* Yes, it is. */ + *value = _fx_utility_32_unsigned_read((UCHAR *)&fat_log -> fx_fault_tolerant_FAT_log_value); + + /* Do not return since there may be more than one log for this cluster. */ + found = FX_TRUE; + } + } + + /* Decrease the logs_remaining counter. */ + logs_remaining--; + + /* Get next log pointer. */ + current_ptr += log_len; + } + + if (found != FX_TRUE) + { + + /* Pass the request to FAT entry read. */ + return(FX_READ_CONTINUE); + } + + /* Return success. */ + return(FX_SUCCESS); +} + +#endif /* FX_ENABLE_FAULT_TOLERANT */ + diff --git a/common/src/fx_fault_tolerant_read_directory_sector.c b/common/src/fx_fault_tolerant_read_directory_sector.c new file mode 100644 index 0000000..4493a2f --- /dev/null +++ b/common/src/fx_fault_tolerant_read_directory_sector.c @@ -0,0 +1,178 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Fault Tolerant */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_fault_tolerant.h" + + +#ifdef FX_ENABLE_FAULT_TOLERANT +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_fault_tolerant_read_directory_sector PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function handles directory sector read requests. During file or*/ +/* directory operations with Fault Tolerant protection, intermediate */ +/* operations are written to the fault tolerant log files. Therefore, */ +/* sector read should search for fault tolerant log before the request */ +/* can be passed to normal directory entry read routine. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* logical_sector Logical sector number */ +/* buffer_ptr Pointer of receiving buffer */ +/* sectors Number of sectors to read */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_16_unsigned_read Read a USHORT from memory */ +/* _fx_utility_32_unsigned_read Read a ULONG from memory */ +/* memcpy Memory Copy */ +/* */ +/* CALLED BY */ +/* */ +/* _fx_utility_logical_sector_read */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), verified */ +/* memcpy usage, */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_fault_tolerant_read_directory_sector(FX_MEDIA *media_ptr, ULONG64 logical_sector, + VOID *buffer_ptr, ULONG64 sectors) +{ +ULONG logs_remaining; +ULONG copy_offset; +ULONG copy_size; +UCHAR *current_ptr; +UCHAR *current_buffer_ptr; +ULONG size; +USHORT type; +ULONG log_len; +ULONG64 log_sector; +FX_FAULT_TOLERANT_DIR_LOG *dir_log; + + /* Get fault tolerant data. */ + logs_remaining = media_ptr -> fx_media_fault_tolerant_total_logs; + + /* Any redo logs? */ + if (logs_remaining == 0) + { + + /* No. Just return. */ + return(FX_SUCCESS); + } + + /* Get size of all logs. */ + size = media_ptr -> fx_media_fault_tolerant_file_size - FX_FAULT_TOLERANT_LOG_CONTENT_OFFSET; + + /* Get the first log pointer. */ + current_ptr = media_ptr -> fx_media_fault_tolerant_memory_buffer + + FX_FAULT_TOLERANT_LOG_CONTENT_OFFSET + + FX_FAULT_TOLERANT_LOG_CONTENT_HEADER_SIZE; + + /* Loop throught all FAT logs. */ + while (logs_remaining) + { + + /* Check log type. */ + type = (USHORT)_fx_utility_16_unsigned_read(current_ptr); + + /* Get log length. */ + log_len = _fx_utility_16_unsigned_read(current_ptr + 2); + + /* Validate the size value. */ + if (log_len > size) + { + + /* Log file is corrupted. Nothing can be done. Return.*/ + return(FX_FILE_CORRUPT); + } + size -= log_len; + + /* Check log type. */ + if (type == FX_FAULT_TOLERANT_DIR_LOG_TYPE) + { + + /* Get the log pointer. */ + dir_log = (FX_FAULT_TOLERANT_DIR_LOG *)current_ptr; + + /* Get the sector of log. */ + log_sector = _fx_utility_64_unsigned_read((UCHAR *)&dir_log -> fx_fault_tolerant_dir_log_sector); + + /* Is this log sector in the range of sectors to read? */ + if ((log_sector >= logical_sector) && (log_sector < logical_sector + sectors)) + { + + /* Yes. Update the content in this sector. */ + current_buffer_ptr = ((UCHAR *)buffer_ptr) + (log_sector - logical_sector) * + media_ptr -> fx_media_bytes_per_sector; + + /* Set copy information. */ + copy_offset = _fx_utility_32_unsigned_read((UCHAR *)&dir_log -> fx_fault_tolerant_dir_log_offset); + + copy_size = log_len - FX_FAULT_TOLERANT_DIR_LOG_ENTRY_SIZE; + + if ((copy_offset + copy_size) > media_ptr -> fx_media_bytes_per_sector) + { + return(FX_FILE_CORRUPT); + } + + /* Copy data into destination sector. */ + memcpy(current_buffer_ptr + copy_offset, /* Use case of memcpy is verified. */ + current_ptr + FX_FAULT_TOLERANT_DIR_LOG_ENTRY_SIZE, copy_size); + } + } + + /* Decrease the logs_remaining counter. */ + logs_remaining--; + + /* Move to start position of next log entry. */ + current_ptr += log_len; + } + + /* Return success. */ + return(FX_SUCCESS); +} + +#endif /* FX_ENABLE_FAULT_TOLERANT */ + diff --git a/common/src/fx_fault_tolerant_read_log_file.c b/common/src/fx_fault_tolerant_read_log_file.c new file mode 100644 index 0000000..9d0c16a --- /dev/null +++ b/common/src/fx_fault_tolerant_read_log_file.c @@ -0,0 +1,93 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Fault Tolerant */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_fault_tolerant.h" + + +#ifdef FX_ENABLE_FAULT_TOLERANT +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_fault_tolerant_read_log_file PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads the entire log file from file system into the */ +/* fault tolerant cache. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_logical_sector_read Read a logical sector */ +/* */ +/* CALLED BY */ +/* */ +/* _fx_fault_tolerant_enable */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_fault_tolerant_read_log_file(FX_MEDIA *media_ptr) +{ +UINT status; +ULONG sectors; +ULONG start_sector; + + /* Calculate count of sectors to read. */ + sectors = media_ptr -> fx_media_fault_tolerant_memory_buffer_size / media_ptr -> fx_media_bytes_per_sector; + + /* Calculate the start sector of log file. */ + start_sector = (media_ptr -> fx_media_fault_tolerant_start_cluster - FX_FAT_ENTRY_START) * + media_ptr -> fx_media_sectors_per_cluster + + media_ptr -> fx_media_data_sector_start; + + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) start_sector, + media_ptr -> fx_media_fault_tolerant_memory_buffer, + sectors, FX_DATA_SECTOR); + + /* Return the status. */ + return(status); +} +#endif /* FX_ENABLE_FAULT_TOLERANT */ + diff --git a/common/src/fx_fault_tolerant_recover.c b/common/src/fx_fault_tolerant_recover.c new file mode 100644 index 0000000..03efc01 --- /dev/null +++ b/common/src/fx_fault_tolerant_recover.c @@ -0,0 +1,159 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Fault Tolerant */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_fault_tolerant.h" + + +#ifdef FX_ENABLE_FAULT_TOLERANT +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_fault_tolerant_recover PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called after the FAT chain is been updated but */ +/* the rest of the write operation fails. At this point, there is */ +/* need to undo the FAT chain operation, which includes: */ +/* (1) Remove newly allocated FAT entries; */ +/* (2) Restore the origianl FAT chain removed during the FAT chain */ +/* update; */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_fault_tolerant_cleanup_FAT_chain Cleanup FAT chain */ +/* _fx_utility_32_unsigned_read Read a ULONG from memory */ +/* _fx_utility_FAT_entry_write Write a FAT entry */ +/* _fx_utility_FAT_flush Flush written FAT entries */ +/* */ +/* CALLED BY */ +/* */ +/* _fx_directory_attributes_set */ +/* _fx_directory_create */ +/* _fx_directory_delete */ +/* _fx_directory_rename */ +/* _fx_file_allocate */ +/* _fx_file_attributes_set */ +/* _fx_file_best_effort_allocate */ +/* _fx_file_create */ +/* _fx_file_delete */ +/* _fx_file_rename */ +/* _fx_file_truncate */ +/* _fx_file_truncate_release */ +/* _fx_file_write */ +/* _fx_unicode_directory_create */ +/* _fx_unicode_file_create */ +/* _fx_fault_tolerant_enable */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_fault_tolerant_recover(FX_MEDIA *media_ptr) +{ +UINT status; +ULONG insertion_front; +ULONG origianl_head_cluster; +FX_FAULT_TOLERANT_FAT_CHAIN *FAT_chain; + + + /* Set fault tolerant state to IDLE. */ + media_ptr -> fx_media_fault_tolerant_state = FX_FAULT_TOLERANT_STATE_IDLE; + + /* Set FAT chain pointer. */ + FAT_chain = (FX_FAULT_TOLERANT_FAT_CHAIN *)(media_ptr -> fx_media_fault_tolerant_memory_buffer + FX_FAULT_TOLERANT_FAT_CHAIN_OFFSET); + + /* Whether or not the supplied FAT chain is valid. */ + if (!(FAT_chain -> fx_fault_tolerant_FAT_chain_flag & FX_FAULT_TOLERANT_FLAG_FAT_CHAIN_VALID)) + { + + /* Invalid, which indiates the FAT chain has been cleaned up already. In this case, just return. */ + return(FX_SUCCESS); + } + + /* Set FAT chain pointer. */ + FAT_chain = (FX_FAULT_TOLERANT_FAT_CHAIN *)(media_ptr -> fx_media_fault_tolerant_memory_buffer + FX_FAULT_TOLERANT_FAT_CHAIN_OFFSET); + + /* Recover FAT chain. */ + status = _fx_fault_tolerant_cleanup_FAT_chain(media_ptr, FX_FAULT_TOLERANT_FAT_CHAIN_RECOVER); + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + + /* Now, link the front of the insertion point back to the origianl FAT chain. */ + insertion_front = _fx_utility_32_unsigned_read((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_insertion_front); + origianl_head_cluster = _fx_utility_32_unsigned_read((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_head_original); + + if (insertion_front != FX_FREE_CLUSTER) + { + + /* Front of the insertion point exists. Link the origianl chain back to the front of the insertion point. */ + status = _fx_utility_FAT_entry_write(media_ptr, insertion_front, origianl_head_cluster); + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + } + + /* New FAT chain is always linked by FAT entries. */ + + /* Flush FAT table. */ +#ifdef FX_FAULT_TOLERANT + + /* Ensure the new FAT chain is properly written to the media. */ + + /* Flush the cached individual FAT entries */ + _fx_utility_FAT_flush(media_ptr); +#endif + + /* Return success. */ + return(FX_SUCCESS); +} +#endif /* FX_ENABLE_FAULT_TOLERANT */ + diff --git a/common/src/fx_fault_tolerant_reset_log_file.c b/common/src/fx_fault_tolerant_reset_log_file.c new file mode 100644 index 0000000..5cfb040 --- /dev/null +++ b/common/src/fx_fault_tolerant_reset_log_file.c @@ -0,0 +1,142 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Fault Tolerant */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_directory.h" +#include "fx_fault_tolerant.h" + + +#ifdef FX_ENABLE_FAULT_TOLERANT +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_fault_tolerant_reset_log_file PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function removes all log entries from the log file, and */ +/* it to its initial state. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_16_unsigned_write Write a USHORT from memory */ +/* _fx_utility_32_unsigned_write Write a ULONG from memory */ +/* _fx_fault_tolerant_calculate_checksum Compute Checksum of data */ +/* _fx_fault_tolerant_write_log_file Write log file */ +/* */ +/* CALLED BY */ +/* */ +/* _fx_directory_attributes_set */ +/* _fx_directory_create */ +/* _fx_directory_delete */ +/* _fx_directory_rename */ +/* _fx_file_allocate */ +/* _fx_file_attributes_set */ +/* _fx_file_best_effort_allocate */ +/* _fx_file_create */ +/* _fx_file_delete */ +/* _fx_file_rename */ +/* _fx_file_truncate */ +/* _fx_file_truncate_release */ +/* _fx_file_write */ +/* _fx_unicode_directory_create */ +/* _fx_unicode_file_create */ +/* _fx_fault_tolerant_enable */ +/* _fx_fault_tolerant_transaction_end */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ + +UINT _fx_fault_tolerant_reset_log_file(FX_MEDIA *media_ptr) +{ +UINT status; +USHORT checksum; +FX_FAULT_TOLERANT_LOG_HEADER *log_header; +FX_FAULT_TOLERANT_FAT_CHAIN *FAT_chain; +FX_FAULT_TOLERANT_LOG_CONTENT *log_content; + + /* Set log header, FAT chain and log content pointer. */ + log_header = (FX_FAULT_TOLERANT_LOG_HEADER *)media_ptr -> fx_media_fault_tolerant_memory_buffer; + FAT_chain = (FX_FAULT_TOLERANT_FAT_CHAIN *)(media_ptr -> fx_media_fault_tolerant_memory_buffer + + FX_FAULT_TOLERANT_FAT_CHAIN_OFFSET); + log_content = (FX_FAULT_TOLERANT_LOG_CONTENT *)(media_ptr -> fx_media_fault_tolerant_memory_buffer + + FX_FAULT_TOLERANT_LOG_CONTENT_OFFSET); + + /* Reset the log file header. */ + _fx_utility_32_unsigned_write((UCHAR *)&log_header -> fx_fault_tolerant_log_header_id, FX_FAULT_TOLERANT_ID); + _fx_utility_16_unsigned_write((UCHAR *)&log_header -> fx_fault_tolerant_log_header_total_size, + (FX_FAULT_TOLERANT_LOG_HEADER_SIZE + FX_FAULT_TOLERANT_FAT_CHAIN_SIZE)); + _fx_utility_16_unsigned_write((UCHAR *)&log_header -> fx_fault_tolerant_log_header_checksum, 0); + log_header -> fx_fault_tolerant_log_header_version_major = FX_FAULT_TOLERANT_VERSION_MAJOR; + log_header -> fx_fault_tolerant_log_header_version_minor = FX_FAULT_TOLERANT_VERSION_MINOR; + _fx_utility_16_unsigned_write((UCHAR *)&log_header -> fx_fault_tolerant_log_header_reserved, 0); + checksum = _fx_fault_tolerant_calculate_checksum((UCHAR *)log_header, FX_FAULT_TOLERANT_LOG_HEADER_SIZE); + _fx_utility_16_unsigned_write((UCHAR *)&log_header -> fx_fault_tolerant_log_header_checksum, checksum); + + /* Reset the undo log section. */ + _fx_utility_16_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_checksumm, 0xFFFF); + FAT_chain -> fx_fault_tolerant_FAT_chain_flag = 0; + FAT_chain -> fx_fault_tolerant_FAT_chain_reserved = 0; + _fx_utility_32_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_insertion_front, 0); + _fx_utility_32_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_head_new, 0); + _fx_utility_32_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_head_original, 0); + _fx_utility_32_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_insertion_back, 0); + _fx_utility_32_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_next_deletion, 0); + + /* Reset log content header. */ + _fx_utility_16_unsigned_write((UCHAR *)&log_content -> fx_fault_tolerant_log_content_checksum, 0xFFFF); + _fx_utility_16_unsigned_write((UCHAR *)&log_content -> fx_fault_tolerant_log_content_count, 0xFFFF); + + /* No matter success or fail, close this transaction. */ + media_ptr -> fx_media_fault_tolerant_state = FX_FAULT_TOLERANT_STATE_IDLE; + + /* Write the log header and FAT chain. */ + status = _fx_fault_tolerant_write_log_file(media_ptr, 0); + + /* Return the status. */ + return(status); +} +#endif /* FX_ENABLE_FAULT_TOLERANT */ + diff --git a/common/src/fx_fault_tolerant_set_FAT_chain.c b/common/src/fx_fault_tolerant_set_FAT_chain.c new file mode 100644 index 0000000..fedd89f --- /dev/null +++ b/common/src/fx_fault_tolerant_set_FAT_chain.c @@ -0,0 +1,125 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Fault Tolerant */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_fault_tolerant.h" + + +#ifdef FX_ENABLE_FAULT_TOLERANT +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_fault_tolerant_set_FAT_chain PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets data of FAT chain. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* use_bitmap Whether or not the orignal */ +/* FAT chain uses bitmap */ +/* insertion_front Previous cluster of head */ +/* new_head_cluster Head cluster of new chain */ +/* origianl_head_cluster Head cluster of the original */ +/* chain */ +/* insertion_back next cluster of chain tail */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_16_unsigned_write Write a USHORT from memory */ +/* _fx_utility_32_unsigned_write Write a ULONG from memory */ +/* _fx_fault_tolerant_calculate_checksum Compute Checksum of data */ +/* _fx_fault_tolerant_write_log_file Write log file */ +/* */ +/* CALLED BY */ +/* */ +/* _fx_file_allocate */ +/* _fx_file_best_effort_allocate */ +/* _fx_file_delete */ +/* _fx_file_truncate_release */ +/* _fx_file_write */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_fault_tolerant_set_FAT_chain(FX_MEDIA *media_ptr, UINT use_bitmap, ULONG insertion_front, + ULONG new_head_cluster, ULONG original_head_cluster, + ULONG insertion_back) +{ +UINT status; +USHORT checksum; +UCHAR flag = FX_FAULT_TOLERANT_FLAG_FAT_CHAIN_VALID; +FX_FAULT_TOLERANT_FAT_CHAIN *FAT_chain; + + /* Set FAT chain pointer. */ + FAT_chain = (FX_FAULT_TOLERANT_FAT_CHAIN *)(media_ptr -> fx_media_fault_tolerant_memory_buffer + FX_FAULT_TOLERANT_FAT_CHAIN_OFFSET); + + /* Parameters not used. Avoid compiler warnings. */ + FX_PARAMETER_NOT_USED(use_bitmap); + + + /* Reset checksum first. */ + _fx_utility_16_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_checksumm, 0); + + /* Set clusters. */ + _fx_utility_32_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_insertion_front, insertion_front); + _fx_utility_32_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_head_new, new_head_cluster); + _fx_utility_32_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_head_original, original_head_cluster); + _fx_utility_32_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_insertion_back, insertion_back); + _fx_utility_32_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_next_deletion, FX_FREE_CLUSTER); + + /* Set flag and reserved. */ + FAT_chain -> fx_fault_tolerant_FAT_chain_flag = flag; + FAT_chain -> fx_fault_tolerant_FAT_chain_reserved = (UCHAR)0; + + /* Calculate checksum. */ + checksum = _fx_fault_tolerant_calculate_checksum((UCHAR *)FAT_chain, FX_FAULT_TOLERANT_FAT_CHAIN_SIZE); + _fx_utility_16_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_checksumm, checksum); + + /* Write the log header and FAT chain. */ + status = _fx_fault_tolerant_write_log_file(media_ptr, 0); + + /* Return the status. */ + return(status); +} +#endif /* FX_ENABLE_FAULT_TOLERANT */ + diff --git a/common/src/fx_fault_tolerant_transaction_end.c b/common/src/fx_fault_tolerant_transaction_end.c new file mode 100644 index 0000000..56c0dfa --- /dev/null +++ b/common/src/fx_fault_tolerant_transaction_end.c @@ -0,0 +1,207 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Fault Tolerant */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + +#include "fx_api.h" +#include "fx_fault_tolerant.h" +#include "fx_utility.h" + +#ifdef FX_ENABLE_FAULT_TOLERANT +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_fault_tolerant_transaction_end PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function completes a file system update operation protected */ +/* by fault tolerant. This function first computes the checksum */ +/* for the log entries, flush the log file to the file system (a */ +/* necessary step to record vital information for the file system to */ +/* recover, in case the update operation fails midway). After the */ +/* log entries are written to the physical medium, the actualy file */ +/* system changes (FAT table, directory entries) are applied. */ +/* */ +/* If the file system changes are successfully applied, the log */ +/* entries can be removed. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_16_unsigned_write Write a USHORT from memory */ +/* _fx_fault_tolerant_apply_logs Apply logs into file system */ +/* _fx_fault_tolerant_calculate_checksum Compute Checksum of data */ +/* _fx_fault_tolerant_write_log_file Write log file */ +/* _fx_fault_tolerant_reset_log_file Reset the log file */ +/* */ +/* CALLED BY */ +/* */ +/* _fx_directory_attributes_set */ +/* _fx_directory_create */ +/* _fx_directory_delete */ +/* _fx_directory_rename */ +/* _fx_file_allocate */ +/* _fx_file_attributes_set */ +/* _fx_file_best_effort_allocate */ +/* _fx_file_create */ +/* _fx_file_delete */ +/* _fx_file_rename */ +/* _fx_file_truncate */ +/* _fx_file_truncate_release */ +/* _fx_file_write */ +/* _fx_unicode_directory_create */ +/* _fx_unicode_file_create */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_fault_tolerant_transaction_end(FX_MEDIA *media_ptr) +{ +ULONG relative_sector; +UINT status; +USHORT checksum; +UINT offset; +FX_FAULT_TOLERANT_LOG_HEADER *log_header; +FX_FAULT_TOLERANT_LOG_CONTENT *log_content; + + /* Is fault tolerant enabled? */ + if (media_ptr -> fx_media_fault_tolerant_enabled == FX_FALSE) + { + return(FX_SUCCESS); + } + + /* Decrease the transaction. */ + media_ptr -> fx_media_fault_tolerant_transaction_count--; + + /* Is transaction finished? */ + if (media_ptr -> fx_media_fault_tolerant_transaction_count != 0) + { + return(FX_SUCCESS); + } + + /* Close this transaction. */ + media_ptr -> fx_media_fault_tolerant_state = FX_FAULT_TOLERANT_STATE_IDLE; + + /* Set log header and FAT chain pointer. */ + log_header = (FX_FAULT_TOLERANT_LOG_HEADER *)media_ptr -> fx_media_fault_tolerant_memory_buffer; + log_content = (FX_FAULT_TOLERANT_LOG_CONTENT *)(media_ptr -> fx_media_fault_tolerant_memory_buffer + + FX_FAULT_TOLERANT_LOG_CONTENT_OFFSET); + + /* Reset checksum field and update log entry counter field. */ + _fx_utility_16_unsigned_write((UCHAR *)&log_content -> fx_fault_tolerant_log_content_checksum, 0); + _fx_utility_16_unsigned_write((UCHAR *)&log_content -> fx_fault_tolerant_log_content_count, + media_ptr -> fx_media_fault_tolerant_total_logs); + + /* Now calculate the checksum of log content. */ + checksum = _fx_fault_tolerant_calculate_checksum((media_ptr -> fx_media_fault_tolerant_memory_buffer + + FX_FAULT_TOLERANT_LOG_CONTENT_OFFSET), + (media_ptr -> fx_media_fault_tolerant_file_size - + FX_FAULT_TOLERANT_LOG_CONTENT_OFFSET)); + + /* Record checksum field of log content. */ + _fx_utility_16_unsigned_write((UCHAR *)&log_content -> fx_fault_tolerant_log_content_checksum, checksum); + + /* Record log header. */ + _fx_utility_16_unsigned_write((UCHAR *)&log_header -> fx_fault_tolerant_log_header_total_size, + media_ptr -> fx_media_fault_tolerant_file_size); + _fx_utility_16_unsigned_write((UCHAR *)&log_header -> fx_fault_tolerant_log_header_checksum, 0); + + /* Calculate checksum of the header. */ + checksum = _fx_fault_tolerant_calculate_checksum((UCHAR *)log_header, FX_FAULT_TOLERANT_LOG_HEADER_SIZE); + + /* Record checksum field of the header. */ + _fx_utility_16_unsigned_write((UCHAR *)&log_header -> fx_fault_tolerant_log_header_checksum, checksum); + + /* Flush log content and flush first sector at last. The first sector contains size of total log file. + * The log file will contain wrong value of size when it is interrupted after first sector flushed to file system. */ + relative_sector = 1; + offset = media_ptr -> fx_media_bytes_per_sector; + while (offset < media_ptr -> fx_media_fault_tolerant_file_size) + { + + /* Write back the log. */ + status = _fx_fault_tolerant_write_log_file(media_ptr, relative_sector); + + /* Check for good completion status. */ + if (status != FX_SUCCESS) + { + + /* Error. */ + return(status); + } + + /* Increase relative sector and offset. */ + relative_sector++; + offset += media_ptr -> fx_media_bytes_per_sector; + } + + /* Flush first sector. */ + status = _fx_fault_tolerant_write_log_file(media_ptr, 0); + + /* Check for good completion status. */ + if (status != FX_SUCCESS) + { + + /* Error. */ + return(status); + } + + /* At this pointer, the vital information has been flushed to the physical medium. + Update the file system (FAT table, directory entry) using information recorded in the + log file. */ + status = _fx_fault_tolerant_apply_logs(media_ptr); + + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + + /* Return the bad status. */ + return(status); + } + + /* The file system has been updated successfully. Remove and reset the fault tolerant + log file. */ + status = _fx_fault_tolerant_reset_log_file(media_ptr); + + return(status); +} +#endif /* FX_ENABLE_FAULT_TOLERANT */ + diff --git a/common/src/fx_fault_tolerant_transaction_fail.c b/common/src/fx_fault_tolerant_transaction_fail.c new file mode 100644 index 0000000..b0af1c9 --- /dev/null +++ b/common/src/fx_fault_tolerant_transaction_fail.c @@ -0,0 +1,105 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Fault Tolerant */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOUCE_CODE + +#include "fx_api.h" +#include "fx_fault_tolerant.h" + +#if defined(FX_ENABLE_FAULT_TOLERANT) && defined(FX_FAULT_TOLERANT_TRANSACTION_FAIL_FUNCTION) +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_fault_tolerant_transaction_fail PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function cleans up resources created by fault tolerant when */ +/* transaction fails. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _fx_directory_attributes_set */ +/* _fx_directory_create */ +/* _fx_directory_delete */ +/* _fx_directory_rename */ +/* _fx_file_allocate */ +/* _fx_file_attributes_set */ +/* _fx_file_best_effort_allocate */ +/* _fx_file_create */ +/* _fx_file_delete */ +/* _fx_file_rename */ +/* _fx_file_truncate */ +/* _fx_file_truncate_release */ +/* _fx_file_write */ +/* _fx_unicode_directory_create */ +/* _fx_unicode_file_create */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_fault_tolerant_transaction_fail(FX_MEDIA *media_ptr) +{ + + /* Check whether fault tolerant is enabled or not. */ + if (media_ptr -> fx_media_fault_tolerant_enabled == FX_TRUE) + { + + /* Yes. Decrease the counter for transaction. */ + media_ptr -> fx_media_fault_tolerant_transaction_count--; + + /* Is this the last transaction? */ + if (media_ptr -> fx_media_fault_tolerant_transaction_count == 0) + { + + /* Yes. Perform recover and reset the log file. */ + _fx_fault_tolerant_recover(media_ptr); + _fx_fault_tolerant_reset_log_file(media_ptr); + } + } + + return(FX_SUCCESS); +} +#endif diff --git a/common/src/fx_fault_tolerant_transaction_start.c b/common/src/fx_fault_tolerant_transaction_start.c new file mode 100644 index 0000000..f50506a --- /dev/null +++ b/common/src/fx_fault_tolerant_transaction_start.c @@ -0,0 +1,115 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Fault Tolerant */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + +#include "fx_api.h" +#include "fx_fault_tolerant.h" + + +#ifdef FX_ENABLE_FAULT_TOLERANT +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_fault_tolerant_transaction_start PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called at the beginning of an update to FileX */ +/* file, directory entry, or FAT table. It resets fault tolerant */ +/* internal state information. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _fx_directory_attributes_set */ +/* _fx_directory_create */ +/* _fx_directory_delete */ +/* _fx_directory_rename */ +/* _fx_file_allocate */ +/* _fx_file_attributes_set */ +/* _fx_file_best_effort_allocate */ +/* _fx_file_create */ +/* _fx_file_delete */ +/* _fx_file_rename */ +/* _fx_file_truncate */ +/* _fx_file_truncate_release */ +/* _fx_file_write */ +/* _fx_unicode_directory_create */ +/* _fx_unicode_file_create */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_fault_tolerant_transaction_start(FX_MEDIA *media_ptr) +{ + + /* Is fault tolerant enabled? */ + if (media_ptr -> fx_media_fault_tolerant_enabled == FX_TRUE) + { + + /* Is this a new transaction? */ + if (media_ptr -> fx_media_fault_tolerant_transaction_count == 0) + { + + /* Yes. Initialize data. */ + media_ptr -> fx_media_fault_tolerant_file_size = FX_FAULT_TOLERANT_LOG_HEADER_SIZE + + FX_FAULT_TOLERANT_FAT_CHAIN_SIZE + + FX_FAULT_TOLERANT_LOG_CONTENT_HEADER_SIZE; + + /* Reset total logs. */ + media_ptr -> fx_media_fault_tolerant_total_logs = 0; + + /* Set state of fault tolerant. */ + media_ptr -> fx_media_fault_tolerant_state = FX_FAULT_TOLERANT_STATE_STARTED; + } + + /* Increase the transaction. */ + media_ptr -> fx_media_fault_tolerant_transaction_count++; + } + + return(FX_SUCCESS); +} +#endif /* FX_ENABLE_FAULT_TOLERANT */ + diff --git a/common/src/fx_fault_tolerant_write_log_file.c b/common/src/fx_fault_tolerant_write_log_file.c new file mode 100644 index 0000000..71f96f5 --- /dev/null +++ b/common/src/fx_fault_tolerant_write_log_file.c @@ -0,0 +1,112 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Fault Tolerant */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_directory.h" +#include "fx_fault_tolerant.h" + + +#ifdef FX_ENABLE_FAULT_TOLERANT +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_fault_tolerant_write_log_file PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes data of one sector of fault tolerant data */ +/* from memory to log file in file system. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* relative_sector Relative sector number of the */ +/* log file to write to */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_logical_sector_write Write a logical sector */ +/* _fx_utility_logical_sector_flush Flush written logical sectors */ +/* */ +/* CALLED BY */ +/* */ +/* _fx_fault_tolerant_cleanup_FAT_chain */ +/* _fx_fault_tolerant_reset_log_file */ +/* _fx_fault_tolerant_set_FAT_chain */ +/* _fx_fault_tolerant_transaction_end */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_fault_tolerant_write_log_file(FX_MEDIA *media_ptr, ULONG relative_sector) +{ +UINT status; +ULONG start_sector; + + /* Calculate the start sector of log file. */ + start_sector = (media_ptr -> fx_media_fault_tolerant_start_cluster - FX_FAT_ENTRY_START) * + media_ptr -> fx_media_sectors_per_cluster + + media_ptr -> fx_media_data_sector_start; + + + /* Write sector directly. */ + status = _fx_utility_logical_sector_write(media_ptr, (ULONG64) (start_sector + relative_sector), + media_ptr -> fx_media_fault_tolerant_memory_buffer + + media_ptr -> fx_media_bytes_per_sector * relative_sector, + ((ULONG) 1), FX_DATA_SECTOR); + + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + + /* Return the bad status. */ + return(status); + } + + /* Flush the internal logical sector cache. */ + status = _fx_utility_logical_sector_flush(media_ptr, (ULONG64) start_sector, ((ULONG64) 1), FX_FALSE); + + FX_FAULT_TOLERANT_WRITE_LOG_FILE_EXTENSION + + /* Return the status. */ + return(status); +} +#endif /* FX_ENABLE_FAULT_TOLERANT */ + diff --git a/common/src/fx_file_allocate.c b/common/src/fx_file_allocate.c new file mode 100644 index 0000000..4b90dca --- /dev/null +++ b/common/src/fx_file_allocate.c @@ -0,0 +1,84 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_file_allocate PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function attempts to allocate the number of consecutive */ +/* clusters required to satisfy the user's request. If there are */ +/* enough clusters, the clusters are allocated and linked to the file. */ +/* Otherwise, if there are not enough consecutive clusters, an error */ +/* code is returned to the caller. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* size Number of bytes to allocate */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_extended_allocate Allocate the clusters */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), and */ +/* Added conditional to */ +/* disable one line function, */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +#ifndef FX_DISABLE_ONE_LINE_FUNCTION +UINT _fx_file_allocate(FX_FILE *file_ptr, ULONG size) +{ + + return(_fx_file_extended_allocate(file_ptr, (ULONG64)size)); +} +#endif /* FX_DISABLE_ONE_LINE_FUNCTION */ diff --git a/common/src/fx_file_attributes_read.c b/common/src/fx_file_attributes_read.c new file mode 100644 index 0000000..5c9b34d --- /dev/null +++ b/common/src/fx_file_attributes_read.c @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_file.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_file_attributes_read PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function first attempts to find the specified file. If found, */ +/* the attribute read request is valid and the directory entry will be */ +/* in order to pickup the file's attributes. Otherwise, if the file */ +/* is not found, the appropriate error code is returned to the caller. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* file_name File name pointer */ +/* attributes_ptr Return attributes pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_search Search for the file name in */ +/* the directory structure */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_file_attributes_read(FX_MEDIA *media_ptr, CHAR *file_name, UINT *attributes_ptr) +{ + +UINT status; +FX_DIR_ENTRY dir_entry; + +#ifdef TX_ENABLE_EVENT_TRACE +TX_TRACE_BUFFER_ENTRY *trace_event; +ULONG trace_timestamp; +#endif +UCHAR not_a_file_attr; + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_file_attributes_reads++; +#endif + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Clear the short name string. */ + dir_entry.fx_dir_entry_short_name[0] = 0; + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + not_a_file_attr = FX_DIRECTORY | FX_VOLUME; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_ATTRIBUTES_READ, media_ptr, file_name, 0, 0, FX_TRACE_FILE_EVENTS, &trace_event, &trace_timestamp) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Search the system for the supplied file name. */ + status = _fx_directory_search(media_ptr, file_name, &dir_entry, FX_NULL, FX_NULL); + + /* Determine if the search was successful. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + /* Check to make sure the found entry is a file. */ + if (dir_entry.fx_dir_entry_attributes & not_a_file_attr) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the not a file error code. */ + return(FX_NOT_A_FILE); + } + + /* Place the current attributes into the destination. */ + *attributes_ptr = (UINT)dir_entry.fx_dir_entry_attributes; + + /* Update the trace event with the attributes read. */ + FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_FILE_ATTRIBUTES_READ, 0, 0, dir_entry.fx_dir_entry_attributes, 0) + + /* Release media protection. */ + FX_UNPROTECT + + /* File attribute read is complete, return successful status. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_file_attributes_set.c b/common/src/fx_file_attributes_set.c new file mode 100644 index 0000000..dd11a8c --- /dev/null +++ b/common/src/fx_file_attributes_set.c @@ -0,0 +1,221 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_file.h" +#include "fx_utility.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_file_attributes_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function first attempts to find the specified file. If found, */ +/* the attribute set request is valid and the directory entry will be */ +/* modified with the new attributes. Otherwise, if the file is not */ +/* found, the appropriate error code is returned to the caller. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* file_name File name pointer */ +/* attributes New file attributes */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_entry_write Write the new directory entry */ +/* _fx_directory_search Search for the file name in */ +/* the directory structure */ +/* _fx_fault_tolerant_transaction_start Start fault tolerant */ +/* transaction */ +/* _fx_fault_tolerant_transaction_end End fault tolerant transaction*/ +/* _fx_fault_tolerant_recover Recover FAT chain */ +/* _fx_fault_tolerant_reset_log_file Reset the log file */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_file_attributes_set(FX_MEDIA *media_ptr, CHAR *file_name, UINT attributes) +{ + +UINT status; +ULONG open_count; +FX_FILE *search_ptr; +FX_DIR_ENTRY dir_entry; +UCHAR not_a_file_attr; + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_file_attributes_sets++; +#endif + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Clear the short name string. */ + dir_entry.fx_dir_entry_short_name[0] = 0; + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + not_a_file_attr = FX_DIRECTORY | FX_VOLUME; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_ATTRIBUTES_SET, media_ptr, file_name, attributes, 0, FX_TRACE_FILE_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Check for write protect at the media level (set by driver). */ + if (media_ptr -> fx_media_driver_write_protect) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return write protect error. */ + return(FX_WRITE_PROTECT); + } + + /* Search the system for the supplied file name. */ + status = _fx_directory_search(media_ptr, file_name, &dir_entry, FX_NULL, FX_NULL); + + /* Determine if the search was successful. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + /* Check to make sure the found entry is a file. */ + if (dir_entry.fx_dir_entry_attributes & not_a_file_attr) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the not a file error code. */ + return(FX_NOT_A_FILE); + } + + /* Search the opened files to see if this file is currently + opened. */ + open_count = media_ptr -> fx_media_opened_file_count; + search_ptr = media_ptr -> fx_media_opened_file_list; + while (open_count) + { + + /* Look at each opened file to see if the same file is opened. */ + if ((search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector == dir_entry.fx_dir_entry_log_sector) && + (search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset == dir_entry.fx_dir_entry_byte_offset)) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* The file is currently open. */ + return(FX_ACCESS_ERROR); + } + + /* Adjust the pointer and decrement the search count. */ + search_ptr = search_ptr -> fx_file_opened_next; + open_count--; + } + + /* Place the new attributes in the directory entry. */ + dir_entry.fx_dir_entry_attributes = (UCHAR)attributes; + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Start transaction. */ + _fx_fault_tolerant_transaction_start(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Now write out the directory entry. */ + status = _fx_directory_entry_write(media_ptr, &dir_entry); + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + /* End transaction. */ + status = _fx_fault_tolerant_transaction_end(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* File attribute set is complete, return status. */ + return(status); +} + diff --git a/common/src/fx_file_best_effort_allocate.c b/common/src/fx_file_best_effort_allocate.c new file mode 100644 index 0000000..fcb935a --- /dev/null +++ b/common/src/fx_file_best_effort_allocate.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_file_best_effort_allocate PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function attempts to allocate the number of consecutive */ +/* clusters required to satisfy the user's request. If there are not */ +/* enough clusters, the largest set of clusters are allocated and */ +/* linked to the file. If there are no free clusters, an error */ +/* code is returned to the caller. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* size Number of bytes to allocate */ +/* actual_size_allocated Number of bytes allocated */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_extended_best_effort_allocate */ +/* Allocate the largest set of */ +/* clusters */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_file_best_effort_allocate(FX_FILE *file_ptr, ULONG size, ULONG *actual_size_allocated) +{ + +UINT status; +ULONG64 temp_actual_size_allocated; + + /* Call actual best effort file allocate service. */ + status = _fx_file_extended_best_effort_allocate(file_ptr, (ULONG64)size, &temp_actual_size_allocated); + + /* Check status. */ + if (status == FX_SUCCESS) + { + *actual_size_allocated = (ULONG)temp_actual_size_allocated; + } + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fx_file_close.c b/common/src/fx_file_close.c new file mode 100644 index 0000000..9d96b17 --- /dev/null +++ b/common/src/fx_file_close.c @@ -0,0 +1,186 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_file.h" +#include "fx_utility.h" +#include "fx_directory.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_file_close PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function closes the specified file. If the file was written */ +/* to this function will also write the directory entry (with the new */ +/* size and time/date stamp) out to disk. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_entry_write Write the directory entry */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_file_close(FX_FILE *file_ptr) +{ + +UINT status; +FX_MEDIA *media_ptr; +FX_INT_SAVE_AREA + + + /* First, determine if the file is still open. */ + if (file_ptr -> fx_file_id != FX_FILE_ID) + { + + /* Return the file not open error status. */ + return(FX_NOT_OPEN); + } + + /* Setup a pointer to the associated media. */ + media_ptr = file_ptr -> fx_file_media_ptr; + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_file_closes++; +#endif + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_CLOSE, file_ptr, file_ptr -> fx_file_current_file_size, 0, 0, FX_TRACE_FILE_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + /* If trace is enabled, unregister this object. */ + FX_TRACE_OBJECT_UNREGISTER(file_ptr) + + /* Remove this file from the opened list for the media. */ + + /* See if the file is the only one on the open list for this media. */ + if (file_ptr == file_ptr -> fx_file_opened_next) + { + + /* Only opened file, just set the opened list to NULL. */ + media_ptr -> fx_media_opened_file_list = FX_NULL; + } + else + { + + /* Otherwise, not the only opened file, link-up the neighbors. */ + (file_ptr -> fx_file_opened_next) -> fx_file_opened_previous = + file_ptr -> fx_file_opened_previous; + (file_ptr -> fx_file_opened_previous) -> fx_file_opened_next = + file_ptr -> fx_file_opened_next; + + /* See if we have to update the opened list head pointer. */ + if (media_ptr -> fx_media_opened_file_list == file_ptr) + { + + /* Yes, move the head pointer to the next opened file. */ + media_ptr -> fx_media_opened_file_list = file_ptr -> fx_file_opened_next; + } + } + + /* Decrement the opened file counter. */ + media_ptr -> fx_media_opened_file_count--; + + /* Finally, Indicate that this file is closed. */ + file_ptr -> fx_file_id = FX_FILE_CLOSED_ID; + + /* Check to see if this file needs to have its directory entry written + back to the media. */ + if ((file_ptr -> fx_file_open_mode == FX_OPEN_FOR_WRITE) && + (file_ptr -> fx_file_modified)) + { + + /* Lockout interrupts for time/date access. */ + FX_DISABLE_INTS + + /* Set the new time and date. */ + file_ptr -> fx_file_dir_entry.fx_dir_entry_time = _fx_system_time; + file_ptr -> fx_file_dir_entry.fx_dir_entry_date = _fx_system_date; + + /* Set the last access date. */ + file_ptr -> fx_file_dir_entry.fx_dir_entry_last_accessed_date = _fx_system_date; + + /* Restore interrupts. */ + FX_RESTORE_INTS + + /* Copy the new file size into the directory entry. */ + file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size = + file_ptr -> fx_file_current_file_size; + + /* Write the directory entry to the media. */ + status = _fx_directory_entry_write(media_ptr, &(file_ptr -> fx_file_dir_entry)); + + /* Check for a good status. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Error writing the directory. */ + return(status); + } + } + + /* Release media protection. */ + FX_UNPROTECT + + /* Return status to the caller. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_file_create.c b/common/src/fx_file_create.c new file mode 100644 index 0000000..e1dcc75 --- /dev/null +++ b/common/src/fx_file_create.c @@ -0,0 +1,303 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_file.h" +#include "fx_utility.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_file_create PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function first attempts to find the specified file. If found, */ +/* the create request is invalid and an error is returned to the */ +/* caller. After the file name verification is made, a search for a */ +/* free directory entry will be made. If nothing is available, an */ +/* error will be returned to the caller. Otherwise, if all is okay, a */ +/* file of 0 bytes will be created. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* file_name File name */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_entry_write Write the new directory entry */ +/* _fx_directory_name_extract Extract directory name */ +/* _fx_directory_search Search for the file name in */ +/* the directory structure */ +/* _fx_directory_free_search Search for a free directory */ +/* entry */ +/* _fx_fault_tolerant_transaction_start Start fault tolerant */ +/* transaction */ +/* _fx_fault_tolerant_transaction_end End fault tolerant transaction*/ +/* _fx_fault_tolerant_recover Recover FAT chain */ +/* _fx_fault_tolerant_reset_log_file Reset the log file */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_file_create(FX_MEDIA *media_ptr, CHAR *file_name) +{ + +FX_INT_SAVE_AREA + +UINT status; +CHAR *name_ptr; +UINT i; +CHAR *work_ptr; +FX_DIR_ENTRY dir_entry; +FX_DIR_ENTRY search_directory; + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_file_creates++; +#endif + + /* Determine if the supplied name is less than the maximum supported name size. The + maximum name (FX_MAX_LONG_NAME_LEN) is defined in fx_api.h. */ + i = 0; + work_ptr = (CHAR *)file_name; + while (*work_ptr) + { + + /* Determine if the character designates a new path. */ + if ((*work_ptr == '\\') || (*work_ptr == '/')) + { + /* Yes, reset the name size. */ + i = 0; + } + /* Check for leading spaces. */ + else if ((*work_ptr != ' ') || (i != 0)) + { + + /* No leading spaces, increment the name size. */ + i++; + } + + /* Move to the next character. */ + work_ptr++; + } + + /* Determine if the supplied name is valid. */ + if ((i == 0) || (i >= FX_MAX_LONG_NAME_LEN)) + { + + /* Return an invalid name value. */ + return(FX_INVALID_NAME); + } + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Setup another pointer to another media name buffer. */ + search_directory.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN * 2; + + /* Clear the short name strings. */ + dir_entry.fx_dir_entry_short_name[0] = 0; + search_directory.fx_dir_entry_short_name[0] = 0; + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_CREATE, media_ptr, file_name, 0, 0, FX_TRACE_FILE_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Start transaction. */ + _fx_fault_tolerant_transaction_start(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Check for write protect at the media level (set by driver). */ + if (media_ptr -> fx_media_driver_write_protect) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return write protect error. */ + return(FX_WRITE_PROTECT); + } + + /* Search the system for the supplied file name. */ + status = _fx_directory_search(media_ptr, file_name, &dir_entry, &search_directory, &name_ptr); + + /* Determine if the search was successful. */ + if (status == FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* File found - Return the error code. */ + return(FX_ALREADY_CREATED); + } + + /* Determine if there is anything left after the name. */ + if (_fx_directory_name_extract(name_ptr, &dir_entry.fx_dir_entry_name[0])) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Extra information after the file name, return an invalid path + error. */ + return(FX_INVALID_PATH); + } + + + /* Find a free slot for the new file. */ + status = _fx_directory_free_search(media_ptr, &search_directory, &dir_entry); + + /* Determine if the search was successful. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + /* Populate the directory entry. */ + + /* Isolate the file name. */ + _fx_directory_name_extract(name_ptr, &dir_entry.fx_dir_entry_name[0]); + + /* Disable interrupts for time/date access. */ + FX_DISABLE_INTS + + /* Set time and date stamps. */ + dir_entry.fx_dir_entry_time = _fx_system_time; + dir_entry.fx_dir_entry_date = _fx_system_date; + + /* Restore interrupts. */ + FX_RESTORE_INTS + + /* Set the attributes for the file. */ + dir_entry.fx_dir_entry_attributes = FX_ARCHIVE; + + /* Set file size to 0. */ + dir_entry.fx_dir_entry_file_size = 0; + + + /* Set the cluster to NULL. */ + dir_entry.fx_dir_entry_cluster = FX_NULL; + + /* Is there a leading dot? */ + if (dir_entry.fx_dir_entry_name[0] == '.') + { + + /* Yes, toggle the hidden attribute bit. */ + dir_entry.fx_dir_entry_attributes |= FX_HIDDEN; + } + + + /* Now write out the directory entry. */ + status = _fx_directory_entry_write(media_ptr, &dir_entry); + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + /* End transaction. */ + status = _fx_fault_tolerant_transaction_end(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* File create is complete, return status. */ + return(status); +} + diff --git a/common/src/fx_file_date_time_set.c b/common/src/fx_file_date_time_set.c new file mode 100644 index 0000000..791f4f9 --- /dev/null +++ b/common/src/fx_file_date_time_set.c @@ -0,0 +1,137 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" +#include "fx_file.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_file_date_time_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the specified file's date and time with the */ +/* values provided. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* file_name File name pointer */ +/* year Year */ +/* month Month */ +/* day Day */ +/* hour Hour */ +/* minute Minute */ +/* second Second */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_search Search for the file name in */ +/* the directory structure */ +/* _fx_directory_entry_write Write the directory entry */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_file_date_time_set(FX_MEDIA *media_ptr, CHAR *file_name, + UINT year, UINT month, UINT day, UINT hour, UINT minute, UINT second) +{ + +UINT status; +FX_DIR_ENTRY dir_entry; + + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Clear the short name string. */ + dir_entry.fx_dir_entry_short_name[0] = 0; + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_DATE_TIME_SET, media_ptr, file_name, year, month, FX_TRACE_FILE_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Search the system for the supplied directory name. */ + status = _fx_directory_search(media_ptr, file_name, &dir_entry, FX_NULL, FX_NULL); + + /* Determine if the search was successful. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + /* Set the new time and date. */ + dir_entry.fx_dir_entry_time = (hour << FX_HOUR_SHIFT) | (minute << FX_MINUTE_SHIFT) | (second / 2); + dir_entry.fx_dir_entry_date = ((year - FX_BASE_YEAR) << FX_YEAR_SHIFT) | (month << FX_MONTH_SHIFT) | day; + + /* Write the directory entry to the media. */ + status = _fx_directory_entry_write(media_ptr, &dir_entry); + + /* Release media protection. */ + FX_UNPROTECT + + /* Directory information write is complete, return status. */ + return(status); +} + diff --git a/common/src/fx_file_delete.c b/common/src/fx_file_delete.c new file mode 100644 index 0000000..89e77a7 --- /dev/null +++ b/common/src/fx_file_delete.c @@ -0,0 +1,382 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_file.h" +#include "fx_utility.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_file_delete PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function first attempts to find the specified file. If found, */ +/* the delete request is valid and all of its clusters will be */ +/* released and its directory entry will be marked as available. */ +/* Otherwise, if the file is not found, the appropriate error code is */ +/* returned to the caller. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* file_name File name pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_entry_write Write the new directory entry */ +/* _fx_directory_search Search for the file name in */ +/* the directory structure */ +/* _fx_utility_FAT_entry_read Read a FAT entry */ +/* _fx_utility_FAT_entry_write Write a FAT entry */ +/* _fx_utility_FAT_flush Flush written FAT entries */ +/* _fx_fault_tolerant_transaction_start Start fault tolerant */ +/* transaction */ +/* _fx_fault_tolerant_transaction_end End fault tolerant transaction*/ +/* _fx_fault_tolerant_recover Recover FAT chain */ +/* _fx_fault_tolerant_reset_log_file Reset the log file */ +/* _fx_fault_tolerant_set_FAT_chain Set data of FAT chain */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_file_delete(FX_MEDIA *media_ptr, CHAR *file_name) +{ + +UINT status; +ULONG cluster; +ULONG contents; +ULONG open_count; +FX_FILE *search_ptr; +ULONG cluster_count; +FX_DIR_ENTRY dir_entry; +UCHAR not_a_file_attr; + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_file_deletes++; +#endif + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Clear the short name string. */ + dir_entry.fx_dir_entry_short_name[0] = 0; + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_DELETE, media_ptr, file_name, 0, 0, FX_TRACE_FILE_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Start transaction. */ + _fx_fault_tolerant_transaction_start(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Check for write protect at the media level (set by driver). */ + if (media_ptr -> fx_media_driver_write_protect) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return write protect error. */ + return(FX_WRITE_PROTECT); + } + + /* Search the system for the supplied file name. */ + status = _fx_directory_search(media_ptr, file_name, &dir_entry, FX_NULL, FX_NULL); + + /* Determine if the search was successful. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + not_a_file_attr = FX_DIRECTORY | FX_VOLUME; + + /* Check to make sure the found entry is a file. */ + if (dir_entry.fx_dir_entry_attributes & not_a_file_attr) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the not a file error code. */ + return(FX_NOT_A_FILE); + } + + /* Check if the entry is read only */ + if (dir_entry.fx_dir_entry_attributes & (UCHAR)(FX_READ_ONLY)) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the not a directory error code. */ + return(FX_WRITE_PROTECT); + } + + /* Search the opened files to see if this file is currently + opened. */ + open_count = media_ptr -> fx_media_opened_file_count; + search_ptr = media_ptr -> fx_media_opened_file_list; + while (open_count) + { + + /* Look at each opened file to see if the same file is opened. */ + if ((search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector == dir_entry.fx_dir_entry_log_sector) && + (search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset == dir_entry.fx_dir_entry_byte_offset)) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* The file is currently open. */ + return(FX_ACCESS_ERROR); + } + + /* Adjust the pointer and decrement the search count. */ + search_ptr = search_ptr -> fx_file_opened_next; + open_count--; + } + + /* Pickup the starting cluster of the file. */ + cluster = dir_entry.fx_dir_entry_cluster; + + /* At this point, make the directory entry invalid in order to delete the file. */ + +#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE + + /* Invalidate the directory search saved information. */ + media_ptr -> fx_media_last_found_name[0] = FX_NULL; +#endif + + /* Mark the directory entry as available, while leaving the other + information for the sake of posterity. */ + dir_entry.fx_dir_entry_name[0] = (CHAR)FX_DIR_ENTRY_FREE; + dir_entry.fx_dir_entry_short_name[0] = (CHAR)FX_DIR_ENTRY_FREE; + + /* Now write out the directory entry. */ + status = _fx_directory_entry_write(media_ptr, &dir_entry); + + /* Determine if the write was successful. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + /* Now that the directory entry is no longer valid and pointing at the chain of clusters, + walk the chain of allocated FAT entries and mark each of them as free. */ + cluster_count = 0; + + +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled) + { + + /* Note: Directory entries are already written to log files. FAT chain is updated as the last step. + Since there are no others FAT entries written to the log. Therefore there is no need to set + the flag FX_FRAULT_TOLERANT_STATE_SET_FAT_CHAIN here. */ + status = _fx_fault_tolerant_set_FAT_chain(media_ptr, FX_FALSE, 0, + media_ptr -> fx_media_fat_last, cluster, media_ptr -> fx_media_fat_last); + + /* Determine if the write was successful. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + } + else + { +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Follow the link of FAT entries. */ + while ((cluster >= FX_FAT_ENTRY_START) && (cluster < media_ptr -> fx_media_fat_reserved)) + { + + /* Increment the number of clusters. */ + cluster_count++; + + + /* Read the current cluster entry from the FAT. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &contents); + + /* Check the return value. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + if ((cluster == contents) || (cluster_count > media_ptr -> fx_media_total_clusters)) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(FX_FAT_READ_ERROR); + } + + + /* Make the current cluster available. */ + status = _fx_utility_FAT_entry_write(media_ptr, cluster, FX_FREE_CLUSTER); + + /* Check the return value. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* Setup for the next cluster. */ + cluster = contents; + } +#ifdef FX_ENABLE_FAULT_TOLERANT + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Update the free clusters in the media control block. */ + media_ptr -> fx_media_available_clusters = + media_ptr -> fx_media_available_clusters + cluster_count; + +#ifdef FX_FAULT_TOLERANT + + /* Flush the cached individual FAT entries */ + _fx_utility_FAT_flush(media_ptr); +#endif + +#ifdef FX_ENABLE_FAULT_TOLERANT + + /* End transaction. */ + status = _fx_fault_tolerant_transaction_end(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* File delete is complete, return status. */ + return(status); +} + diff --git a/common/src/fx_file_extended_allocate.c b/common/src/fx_file_extended_allocate.c new file mode 100644 index 0000000..f16f06f --- /dev/null +++ b/common/src/fx_file_extended_allocate.c @@ -0,0 +1,543 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_file.h" +#include "fx_utility.h" +#include "fx_directory.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_file_extended_allocate PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function attempts to allocate the number of consecutive */ +/* clusters required to satisfy the user's request. If there are */ +/* enough clusters, the clusters are allocated and linked to the file. */ +/* Otherwise, if there are not enough consecutive clusters, an error */ +/* code is returned to the caller. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* size Number of bytes to allocate */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_entry_write Update directory entry */ +/* _fx_utility_FAT_entry_read Read a FAT entry */ +/* _fx_utility_FAT_entry_write Write a FAT entry */ +/* _fx_utility_FAT_flush Flush written FAT entries */ +/* _fx_utility_logical_sector_flush Flush the written log sector */ +/* _fx_fault_tolerant_transaction_start Start fault tolerant */ +/* transaction */ +/* _fx_fault_tolerant_transaction_end End fault tolerant transaction*/ +/* _fx_fault_tolerant_recover Recover FAT chain */ +/* _fx_fault_tolerant_reset_log_file Reset the log file */ +/* _fx_fault_tolerant_set_FAT_chain Set data of FAT chain */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_file_extended_allocate(FX_FILE *file_ptr, ULONG64 size) +{ + +UINT status; +ULONG i; +UINT found; +ULONG bytes_per_cluster; +ULONG FAT_index; +ULONG FAT_value; +ULONG clusters; +FX_MEDIA *media_ptr; + +#ifdef TX_ENABLE_EVENT_TRACE +TX_TRACE_BUFFER_ENTRY *trace_event; +ULONG trace_timestamp; +#endif + + + /* First, determine if the file is still open. */ + if (file_ptr -> fx_file_id != FX_FILE_ID) + { + + /* Return the file not open error status. */ + return(FX_NOT_OPEN); + } + + /* Setup pointer to media structure. */ + media_ptr = file_ptr -> fx_file_media_ptr; + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_file_allocates++; +#endif + + /* Make sure this file is open for writing. */ + if (file_ptr -> fx_file_open_mode != FX_OPEN_FOR_WRITE) + { + + /* Return the access error exception - a write was attempted from + a file opened for reading! */ + return(FX_ACCESS_ERROR); + } + + /* Determine if the requested allocation is for zero bytes. */ + if (size == 0) + { + + /* Return a successful completion - nothing needs to be done. */ + return(FX_SUCCESS); + } + + /* Setup pointer to associated media control block. */ + media_ptr = file_ptr -> fx_file_media_ptr; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_ALLOCATE, file_ptr, size, file_ptr -> fx_file_current_available_size, 0, FX_TRACE_FILE_EVENTS, &trace_event, &trace_timestamp) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Start transaction. */ + _fx_fault_tolerant_transaction_start(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Check for write protect at the media level (set by driver). */ + if (media_ptr -> fx_media_driver_write_protect) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return write protect error. */ + return(FX_WRITE_PROTECT); + } + + /* Calculate the number of bytes per cluster. */ + bytes_per_cluster = ((ULONG)media_ptr -> fx_media_bytes_per_sector) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster); + + /* Check for invalid value. */ + if (bytes_per_cluster == 0) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Invalid media, return error. */ + return(FX_MEDIA_INVALID); + } + + /* Calculate the number of consecutive clusters needed to satisfy this + request. */ + clusters = (ULONG)(((size + bytes_per_cluster - 1) / bytes_per_cluster)); + + /* Determine if cluster count is 0. */ + if (clusters == 0) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Size overflow when rounding to the next cluster, return an error status. */ + return(FX_NO_MORE_SPACE); + } + + /* Determine if there are enough available clusters on the media. */ + if (clusters > media_ptr -> fx_media_available_clusters) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Not enough clusters, return an error status. */ + return(FX_NO_MORE_SPACE); + } + + /* Determine if the requested file allocation would exceed the physical limit of the file. */ + if (((file_ptr -> fx_file_current_available_size + (((ULONG64) clusters) * ((ULONG64) bytes_per_cluster))) < file_ptr -> fx_file_current_available_size) || + ((file_ptr -> fx_file_current_available_size + (((ULONG64) clusters) * ((ULONG64) bytes_per_cluster))) > 0xFFFFFFFFULL)) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the no more space error, since the new file size would be larger than + the 32-bit field to represent it in the file's directory entry. */ + return(FX_NO_MORE_SPACE); + } + + /* Now we need to find the consecutive clusters. */ + FAT_index = FX_FAT_ENTRY_START; + found = FX_FALSE; + + + while (FAT_index <= (media_ptr -> fx_media_total_clusters - clusters + FX_FAT_ENTRY_START)) + { + + /* Determine if enough consecutive FAT entries are available. */ + i = 0; + + + do + { + + /* Read a FAT entry. */ + status = _fx_utility_FAT_entry_read(media_ptr, (FAT_index + i), &FAT_value); + + /* Check for a successful status. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* Determine if the entry is free. */ + if (FAT_value != FX_FREE_CLUSTER) + { + break; + } + + /* Otherwise, increment the consecutive FAT indices. */ + i++; + } while (i < clusters); + + /* Determine if we found enough FAT entries. */ + if (i >= clusters) + { + + /* Yes, we have found enough FAT entries - set the found + flag and get out of this loop. */ + found = FX_TRUE; + break; + } + else + { + /* Position to the next possibly free FAT entry. */ + FAT_index = FAT_index + i + 1; + } +} + + /* Determine if we found enough consecutive clusters to satisfy the + request. */ + if (found) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled) + { + + /* Record the action that is about to take place. This information + would aid the undo process should fault condition happens. */ + media_ptr -> fx_media_fault_tolerant_state |= FX_FAULT_TOLERANT_STATE_SET_FAT_CHAIN; + _fx_fault_tolerant_set_FAT_chain(media_ptr, FX_FALSE, file_ptr -> fx_file_last_physical_cluster, + FAT_index, media_ptr -> fx_media_fat_last, media_ptr -> fx_media_fat_last); + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + + /* Update the link pointers in the new clusters. */ + for (i = 0; i < (clusters - 1); i++) + { + + /* Update the cluster links. Since the allocation is + sequential, we just have to link each FAT entry to the + next one. */ + status = _fx_utility_FAT_entry_write(media_ptr, FAT_index + i, FAT_index + i + 1); + + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + } + + /* Now place an EOF in the last cluster entry. */ + status = _fx_utility_FAT_entry_write(media_ptr, FAT_index + clusters - 1, media_ptr -> fx_media_fat_last); + + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + +#ifdef FX_FAULT_TOLERANT + + /* Flush the cached individual FAT entries */ + _fx_utility_FAT_flush(media_ptr); + +#endif + + /* Actually link up the new clusters to the file. */ + + /* Determine if there are already clusters allocated for this file. */ + if (file_ptr -> fx_file_total_clusters) + { + + /* Linkup the last cluster. */ + status = _fx_utility_FAT_entry_write(media_ptr, + file_ptr -> fx_file_last_physical_cluster, FAT_index); + + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* Determine if we are adding a sector after a write filled the previously + allocated cluster exactly. */ + if ((file_ptr -> fx_file_current_relative_sector >= + (media_ptr -> fx_media_sectors_per_cluster - 1)) && + (file_ptr -> fx_file_current_logical_offset >= + media_ptr -> fx_media_bytes_per_sector)) + { + + /* Yes, we need to adjust all of the pertinent file parameters for + access into this newly allocated cluster. */ + file_ptr -> fx_file_current_physical_cluster = FAT_index; + file_ptr -> fx_file_current_relative_cluster++; + file_ptr -> fx_file_current_relative_sector = 0; + file_ptr -> fx_file_current_logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG64)(FAT_index - FX_FAT_ENTRY_START)) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)); + file_ptr -> fx_file_current_logical_offset = 0; + } + } + else + { + + /* These new clusters are also the first! Setup the initial + file parameters. */ + file_ptr -> fx_file_first_physical_cluster = FAT_index; + file_ptr -> fx_file_current_physical_cluster = file_ptr -> fx_file_first_physical_cluster; + file_ptr -> fx_file_current_relative_cluster = 0; + file_ptr -> fx_file_current_logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG64)(file_ptr -> fx_file_first_physical_cluster - FX_FAT_ENTRY_START)) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)); + file_ptr -> fx_file_current_logical_offset = 0; + file_ptr -> fx_file_current_file_offset = 0; + + /* Update the first cluster in the directory entry. */ + file_ptr -> fx_file_dir_entry.fx_dir_entry_cluster = FAT_index; + } + + /* Remember the last physical cluster. */ + file_ptr -> fx_file_last_physical_cluster = FAT_index + clusters - 1; + + + /* Check for wrap-around when updating the available size. */ + /* Update the available size. */ + file_ptr -> fx_file_current_available_size = + file_ptr -> fx_file_current_available_size + (bytes_per_cluster * clusters); + + + /* Increment the total clusters for this file. */ + file_ptr -> fx_file_total_clusters = + file_ptr -> fx_file_total_clusters + clusters; + + /* Decrease the available clusters on the media. */ + media_ptr -> fx_media_available_clusters = + media_ptr -> fx_media_available_clusters - clusters; + +#if defined(FX_UPDATE_FILE_SIZE_ON_ALLOCATE) || defined(FX_ENABLE_FAULT_TOLERANT) + + /* Set the file size the current size plus what what was added. */ + file_ptr -> fx_file_current_file_size += size; + + /* Copy the new file size into the directory entry. */ + file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size = file_ptr -> fx_file_current_file_size; +#endif + +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled) + { + + /* Clear undo phase. */ + media_ptr -> fx_media_fault_tolerant_state &= (UCHAR)(~FX_FAULT_TOLERANT_STATE_SET_FAT_CHAIN & 0xff); + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Update the trace event with the new size. */ + FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_FILE_ALLOCATE, 0, 0, 0, file_ptr -> fx_file_current_file_size); + + /* Write the directory entry to the media. */ + status = _fx_directory_entry_write(media_ptr, &(file_ptr -> fx_file_dir_entry)); + + /* Check for a good status. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + } + else + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Not enough contiguous space on the media. Return error status. */ + return(FX_NO_MORE_SPACE); + } + +#ifdef FX_FAULT_TOLERANT + + /* Flush the cached individual FAT entries */ + _fx_utility_FAT_flush(media_ptr); +#endif + + /* Flush the internal logical sector cache. */ + status = _fx_utility_logical_sector_flush(media_ptr, ((ULONG64) 1), (ULONG64)(media_ptr -> fx_media_sectors_per_FAT), FX_FALSE); + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + if (media_ptr -> fx_media_fault_tolerant_enabled) + { + + /* Copy the new file size into the directory entry. */ + file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size = file_ptr -> fx_file_current_file_size; + } + + /* End transaction. */ + status = _fx_fault_tolerant_transaction_end(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fx_file_extended_best_effort_allocate.c b/common/src/fx_file_extended_best_effort_allocate.c new file mode 100644 index 0000000..a7e14ea --- /dev/null +++ b/common/src/fx_file_extended_best_effort_allocate.c @@ -0,0 +1,583 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_file.h" +#include "fx_utility.h" +#include "fx_directory.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_file_extended_best_effort_allocate PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function attempts to allocate the number of consecutive */ +/* clusters required to satisfy the user's request. If there are not */ +/* enough clusters, the largest set of clusters are allocated and */ +/* linked to the file. If there are no free clusters, an error */ +/* code is returned to the caller. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* size Number of bytes to allocate */ +/* actual_size_allocated Number of bytes allocated */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_entry_write Update directory entry */ +/* _fx_utility_FAT_entry_read Read a FAT entry */ +/* _fx_utility_FAT_entry_write Write a FAT entry */ +/* _fx_utility_FAT_flush Flush written FAT entries */ +/* _fx_utility_logical_sector_flush Flush the written log sector */ +/* _fx_fault_tolerant_transaction_start Start fault tolerant */ +/* transaction */ +/* _fx_fault_tolerant_transaction_end End fault tolerant transaction*/ +/* _fx_fault_tolerant_recover Recover FAT chain */ +/* _fx_fault_tolerant_reset_log_file Reset the log file */ +/* _fx_fault_tolerant_set_FAT_chain Set data of FAT chain */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_file_extended_best_effort_allocate(FX_FILE *file_ptr, ULONG64 size, ULONG64 *actual_size_allocated) +{ + +UINT status; +ULONG i; +UINT found; +ULONG bytes_per_cluster; +ULONG FAT_index, start_FAT_index; +ULONG FAT_value; +ULONG clusters, maximum_clusters; +FX_MEDIA *media_ptr; + + +#ifdef TX_ENABLE_EVENT_TRACE +TX_TRACE_BUFFER_ENTRY *trace_event; +ULONG trace_timestamp; +#endif + + + /* First, determine if the file is still open. */ + if (file_ptr -> fx_file_id != FX_FILE_ID) + { + + /* Return the file not open error status. */ + return(FX_NOT_OPEN); + } + +#ifndef FX_MEDIA_STATISTICS_DISABLE + /* Setup pointer to media structure. */ + media_ptr = file_ptr -> fx_file_media_ptr; + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_file_best_effort_allocates++; +#endif + + /* Make sure this file is open for writing. */ + if (file_ptr -> fx_file_open_mode != FX_OPEN_FOR_WRITE) + { + + /* Return the access error exception - a write was attempted from + a file opened for reading! */ + return(FX_ACCESS_ERROR); + } + + /* Determine if the requested allocation is for zero bytes. */ + if (size == 0) + { + + /* Return a size allocated of zero. */ + *actual_size_allocated = 0; + + /* Return a successful completion - nothing needs to be done. */ + return(FX_SUCCESS); + } + + /* Setup pointer to associated media control block. */ + media_ptr = file_ptr -> fx_file_media_ptr; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_BEST_EFFORT_ALLOCATE, file_ptr, size, 0, 0, FX_TRACE_FILE_EVENTS, &trace_event, &trace_timestamp) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Start transaction. */ + _fx_fault_tolerant_transaction_start(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Check for write protect at the media level (set by driver). */ + if (media_ptr -> fx_media_driver_write_protect) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return write protect error. */ + return(FX_WRITE_PROTECT); + } + + /* Calculate the number of bytes per cluster. */ + bytes_per_cluster = ((ULONG)media_ptr -> fx_media_bytes_per_sector) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster); + + /* Check for invalid value. */ + if (bytes_per_cluster == 0) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Invalid media, return error. */ + return(FX_MEDIA_INVALID); + } + + /* Calculate the number of consecutive clusters needed to satisfy this + request. */ + clusters = (ULONG)(((size + bytes_per_cluster - 1) / bytes_per_cluster)); + + /* Determine if cluster count is 0. */ + if (clusters == 0) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Size overflow when rounding to the next cluster, return an error status. */ + return(FX_NO_MORE_SPACE); + } + + /* Determine if there are no available clusters on the media. */ + if (!media_ptr -> fx_media_available_clusters) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return a size allocated of zero, since no clusters were available. */ + *actual_size_allocated = 0; + + /* Not enough clusters, return an error status. */ + return(FX_NO_MORE_SPACE); + } + + /* Determine if the requested file allocation would exceed the physical limit of the file. */ + if (((file_ptr -> fx_file_current_available_size + (((ULONG64) clusters) * ((ULONG64) bytes_per_cluster))) < file_ptr -> fx_file_current_available_size) || + ((file_ptr -> fx_file_current_available_size + (((ULONG64) clusters) * ((ULONG64) bytes_per_cluster))) > 0xFFFFFFFFULL)) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the no more space error, since the new file size would be larger than + the 32-bit field to represent it in the file's directory entry. */ + return(FX_NO_MORE_SPACE); + } + + /* Now we need to find the consecutive clusters. */ + found = FX_FALSE; + FAT_index = FX_FAT_ENTRY_START; + maximum_clusters = 0; + start_FAT_index = FAT_index; + + while (FAT_index < (media_ptr -> fx_media_total_clusters + FX_FAT_ENTRY_START)) + { + + /* Determine if enough consecutive FAT entries are available. */ + i = 0; + + + do + { + + /* Read a FAT entry. */ + status = _fx_utility_FAT_entry_read(media_ptr, (FAT_index + i), &FAT_value); + + /* Check for a successful status. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* Determine if the entry is free. */ + if (FAT_value != FX_FREE_CLUSTER) + { + break; + } + + /* Otherwise, increment the consecutive FAT indices. */ + i++; + } while ((i < clusters) && ((FAT_index + i) < media_ptr -> fx_media_total_clusters + FX_FAT_ENTRY_START)); + + /* Determine if a new maximum number of clusters has been found. */ + if (i > maximum_clusters) + { + + /* Yes, remember the maximum number of clusters and the starting + cluster. */ + maximum_clusters = i; + start_FAT_index = FAT_index; + } + + /* Determine if we found enough FAT entries. */ + if (i >= clusters) + { + + /* Yes, we have found enough FAT entries - set the found + flag and get out of this loop. */ + found = FX_TRUE; + break; + } + else + { + + /* Position to the next possibly free FAT entry. */ + FAT_index = FAT_index + i + 1; + } +} + + /* Determine if the total request could not be satisfied, but a partial allocation + could be satisfied. */ + if (maximum_clusters) + { + + /* Yes, there was at least one cluster. Prepare to return this + to the caller. */ + FAT_index = start_FAT_index; + clusters = maximum_clusters; + found = FX_TRUE; + } + + /* Determine if we found enough consecutive clusters to satisfy the + request. */ + if (found) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled) + { + + /* Record the FAT chain being applied to the file system. This information aids + recovery effort if fault happens. */ + media_ptr -> fx_media_fault_tolerant_state |= FX_FAULT_TOLERANT_STATE_SET_FAT_CHAIN; + _fx_fault_tolerant_set_FAT_chain(media_ptr, FX_FALSE, file_ptr -> fx_file_last_physical_cluster, + FAT_index, media_ptr -> fx_media_fat_last, media_ptr -> fx_media_fat_last); + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + + /* Update the link pointers in the new clusters. */ + for (i = 0; i < (clusters - 1); i++) + { + + /* Update the cluster links. Since the allocation is + sequential, we just have to link each FAT entry to the + next one. */ + status = _fx_utility_FAT_entry_write(media_ptr, FAT_index + i, FAT_index + i + 1); + + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + } + + + /* Now place an EOF in the last cluster entry. */ + status = _fx_utility_FAT_entry_write(media_ptr, FAT_index + clusters - 1, media_ptr -> fx_media_fat_last); + + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + +#ifdef FX_FAULT_TOLERANT + + /* Flush the cached individual FAT entries */ + _fx_utility_FAT_flush(media_ptr); +#endif + + /* Actually link up the new clusters to the file. */ + + /* Determine if there are already clusters allocated for this file. */ + if (file_ptr -> fx_file_total_clusters) + { + + + /* Linkup the last cluster. */ + status = _fx_utility_FAT_entry_write(media_ptr, + file_ptr -> fx_file_last_physical_cluster, FAT_index); + + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* Determine if we are adding a sector after a write filled the previously + allocated cluster exactly. */ + if ((file_ptr -> fx_file_current_relative_sector >= + (media_ptr -> fx_media_sectors_per_cluster - 1)) && + (file_ptr -> fx_file_current_logical_offset >= + media_ptr -> fx_media_bytes_per_sector)) + { + + /* Yes, we need to adjust all of the pertinent file parameters for + access into this newly allocated cluster. */ + file_ptr -> fx_file_current_physical_cluster = FAT_index; + file_ptr -> fx_file_current_relative_cluster++; + file_ptr -> fx_file_current_relative_sector = 0; + file_ptr -> fx_file_current_logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG64)(FAT_index - FX_FAT_ENTRY_START)) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)); + file_ptr -> fx_file_current_logical_offset = 0; + } + } + else + { + + /* These new clusters are also the first! Setup the initial + file parameters. */ + file_ptr -> fx_file_first_physical_cluster = FAT_index; + file_ptr -> fx_file_current_physical_cluster = file_ptr -> fx_file_first_physical_cluster; + file_ptr -> fx_file_current_relative_cluster = 0; + file_ptr -> fx_file_current_logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG64)(file_ptr -> fx_file_first_physical_cluster - FX_FAT_ENTRY_START)) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)); + file_ptr -> fx_file_current_logical_offset = 0; + file_ptr -> fx_file_current_file_offset = 0; + + /* Setup the consecutive clusters at the beginning of the file. */ + file_ptr -> fx_file_consecutive_cluster = clusters; + + /* Update the first cluster in the directory entry. */ + file_ptr -> fx_file_dir_entry.fx_dir_entry_cluster = FAT_index; + +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled) + { + + /* Clear undo phase. */ + media_ptr -> fx_media_fault_tolerant_state &= (UCHAR)(~FX_FAULT_TOLERANT_STATE_SET_FAT_CHAIN & 0xff); + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + } + + /* Remember the last physical cluster. */ + file_ptr -> fx_file_last_physical_cluster = FAT_index + clusters - 1; + + /* Update the available size. */ + + /* Normal condition, update the available size. */ + file_ptr -> fx_file_current_available_size = + file_ptr -> fx_file_current_available_size + bytes_per_cluster * clusters; + + /* Increment the total clusters for this file. */ + file_ptr -> fx_file_total_clusters = + file_ptr -> fx_file_total_clusters + clusters; + + /* Decrease the available clusters on the media. */ + media_ptr -> fx_media_available_clusters = + media_ptr -> fx_media_available_clusters - clusters; + + /* Return the actual size allocated. */ + *actual_size_allocated = ((ULONG64)clusters) * bytes_per_cluster; + +#if defined(FX_UPDATE_FILE_SIZE_ON_ALLOCATE) || defined(FX_ENABLE_FAULT_TOLERANT) + + /* Set the file size the current size plus what what was added. */ + if (size < *actual_size_allocated) + { + file_ptr -> fx_file_current_file_size += size; + } + else + { + file_ptr -> fx_file_current_file_size += *actual_size_allocated; + } + + /* Copy the new file size into the directory entry. */ + file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size = file_ptr -> fx_file_current_file_size; +#endif + + /* Update the trace event with the bytes allocated. */ + FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_FILE_BEST_EFFORT_ALLOCATE, 0, 0, *actual_size_allocated, 0); + + /* Write the directory entry to the media. */ + status = _fx_directory_entry_write(media_ptr, &(file_ptr -> fx_file_dir_entry)); + + /* Check for a good status. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + } + else + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return a size allocated of zero, since no clusters were available. */ + *actual_size_allocated = 0; + + /* Not enough contiguous space on the media. Return error status. */ + return(FX_NO_MORE_SPACE); + } + +#ifdef FX_FAULT_TOLERANT + + /* Flush the cached individual FAT entries */ + _fx_utility_FAT_flush(media_ptr); +#endif + + /* Flush the internal logical sector cache. */ + status = _fx_utility_logical_sector_flush(media_ptr, ((ULONG64) 1), (ULONG64) (media_ptr -> fx_media_sectors_per_FAT), FX_FALSE); + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + /* End transaction. */ + status = _fx_fault_tolerant_transaction_end(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fx_file_extended_relative_seek.c b/common/src/fx_file_extended_relative_seek.c new file mode 100644 index 0000000..b11a424 --- /dev/null +++ b/common/src/fx_file_extended_relative_seek.c @@ -0,0 +1,163 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_file.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_file_extended_relative_seek PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function positions the internal file pointers to the specified */ +/* byte relative offset such that the next read or write operation is */ +/* performed there. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* byte_offset Byte offset of the seek */ +/* seek_from Direction for relative seek, */ +/* legal values are: */ +/* */ +/* FX_SEEK_BEGIN */ +/* FX_SEEK_END */ +/* FX_SEEK_FORWARD */ +/* FX_SEEK_BACK */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_extended_seek Seek to specified position */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_file_extended_relative_seek(FX_FILE *file_ptr, ULONG64 byte_offset, UINT seek_from) +{ + +#ifndef FX_MEDIA_STATISTICS_DISABLE +FX_MEDIA *media_ptr; + + /* First, determine if the file is still open. */ + if (file_ptr -> fx_file_id != FX_FILE_ID) + { + + /* Return the file not open error status. */ + return(FX_NOT_OPEN); + } + + /* Setup pointer to media structure. */ + media_ptr = file_ptr -> fx_file_media_ptr; + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_file_relative_seeks++; +#endif + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_RELATIVE_SEEK, file_ptr, byte_offset, seek_from, file_ptr -> fx_file_current_file_offset, FX_TRACE_FILE_EVENTS, 0, 0) + + /* Determine if seeking from the beginning is requested. */ + if (seek_from == FX_SEEK_BEGIN) + { + + /* Yes, use the base file seek routine to seek from the beginning of + the file. */ + return(_fx_file_extended_seek(file_ptr, byte_offset)); + } + /* Otherwise, determine if seeking from the end is requested. */ + else if (seek_from == FX_SEEK_END) + { + + /* Yes, seek from the end of the file. */ + + /* Determine if the requested seek offset is greater than + the file size. */ + if (byte_offset >= file_ptr -> fx_file_current_file_size) + { + + /* Yes, just seek to the beginning of the file. */ + return(_fx_file_extended_seek(file_ptr, ((ULONG64) 0))); + } + else + { + + /* Logically seek from the end of the file. */ + return(_fx_file_extended_seek(file_ptr, file_ptr -> fx_file_current_file_size - byte_offset)); + } + } + /* Otherwise, determine if seeking from the current position is requested. */ + else if (seek_from == FX_SEEK_FORWARD) + { + + /* Yes, just seek ahead from the current file position. */ + return(_fx_file_extended_seek(file_ptr, file_ptr -> fx_file_current_file_offset + byte_offset)); + } + /* Otherwise, seeking backward from the current position is assumed. */ + else + { + + /* Determine if the backward offset is greater than the current file offset. */ + if (byte_offset >= file_ptr -> fx_file_current_file_offset) + { + + /* Yes, just position the file to the beginning. */ + return(_fx_file_extended_seek(file_ptr, ((ULONG64) 0))); + } + else + { + + /* Seek backward relative to the current position. */ + return(_fx_file_extended_seek(file_ptr, file_ptr -> fx_file_current_file_offset - byte_offset)); + } + } +} + diff --git a/common/src/fx_file_extended_seek.c b/common/src/fx_file_extended_seek.c new file mode 100644 index 0000000..036aa45 --- /dev/null +++ b/common/src/fx_file_extended_seek.c @@ -0,0 +1,300 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_file.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_file_extended_seek PORTABLE C */ +/* 6.1.7 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function positions the internal file pointers to the specified */ +/* byte offset such that the next read or write operation will be */ +/* performed there. If the byte offset is greater than the size, the */ +/* file pointers will be positioned to the end of the file. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* byte_offset Byte offset into the file */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_FAT_entry_read Read a FAT entry */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* 06-02-2021 Bhupendra Naphade Modified comment(s), fixed */ +/* relative cluster logic, */ +/* resulting in version 6.1.7 */ +/* */ +/**************************************************************************/ +UINT _fx_file_extended_seek(FX_FILE *file_ptr, ULONG64 byte_offset) +{ + +UINT status; +ULONG cluster; +ULONG contents = 0; +ULONG bytes_per_cluster; +ULONG last_cluster; +ULONG cluster_count; +ULONG64 bytes_remaining; +FX_MEDIA *media_ptr; + + + /* First, determine if the file is still open. */ + if (file_ptr -> fx_file_id != FX_FILE_ID) + { + + /* Return the file not open error status. */ + return(FX_NOT_OPEN); + } + +#ifndef FX_MEDIA_STATISTICS_DISABLE + /* Setup pointer to media structure. */ + media_ptr = file_ptr -> fx_file_media_ptr; + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_file_seeks++; +#endif + + /* Setup pointer to associated media control block. */ + media_ptr = file_ptr -> fx_file_media_ptr; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_SEEK, file_ptr, byte_offset, file_ptr -> fx_file_current_file_offset, 0, FX_TRACE_FILE_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Check if we actually have to do anything. */ + if (byte_offset == file_ptr -> fx_file_current_file_offset) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Seek is complete, return successful status. */ + return(FX_SUCCESS); + } + + /* Calculate the number of bytes per cluster. */ + bytes_per_cluster = ((ULONG)media_ptr -> fx_media_bytes_per_sector) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster); + + /* Check for invalid value. */ + if (bytes_per_cluster == 0) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Invalid media, return error. */ + return(FX_MEDIA_INVALID); + } + + /* See if we need to adjust the byte offset. */ + if (byte_offset > file_ptr -> fx_file_current_file_size) + { + + /* Adjust the byte offset down to the file size. */ + byte_offset = file_ptr -> fx_file_current_file_size; + } + + /* Check if the desired position within the leading consecutive clusters. */ + if (byte_offset >= (ULONG64)file_ptr -> fx_file_consecutive_cluster * (ULONG64)bytes_per_cluster) + { + + /* At this point, we are ready to walk list of clusters to setup the + seek position of this file. */ + + /* check if byte_offset is greater than where we were left off earlier */ + if ((ULONG64)file_ptr -> fx_file_current_relative_cluster * (ULONG64)bytes_per_cluster < byte_offset) + { + + cluster = file_ptr -> fx_file_current_physical_cluster; + + bytes_remaining = byte_offset - + file_ptr -> fx_file_current_relative_cluster * bytes_per_cluster; + + cluster_count = file_ptr -> fx_file_current_relative_cluster; + } + else + { + + cluster = file_ptr -> fx_file_first_physical_cluster + + (file_ptr -> fx_file_consecutive_cluster - 1); + bytes_remaining = byte_offset - + (file_ptr -> fx_file_consecutive_cluster - 1) * bytes_per_cluster; + cluster_count = (file_ptr -> fx_file_consecutive_cluster - 1); + } + + + /* Follow the link of FAT entries. */ + while ((cluster >= FX_FAT_ENTRY_START) && (cluster < media_ptr -> fx_media_fat_reserved)) + { + + /* Increment the number of clusters. */ + cluster_count++; + + /* Read the current cluster entry from the FAT. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &contents); + + /* Check the return value. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* Save the last valid cluster. */ + last_cluster = cluster; + + /* Setup for the next cluster. */ + cluster = contents; + + /* Determine if this is the last written cluster. */ + if (bytes_remaining > bytes_per_cluster) + { + + /* Still more seeking, just decrement the working byte offset. */ + bytes_remaining = bytes_remaining - bytes_per_cluster; + } + else + { + + /* Remember this cluster number. */ + file_ptr -> fx_file_current_physical_cluster = last_cluster; + + /* Remember the relative cluster. */ + file_ptr -> fx_file_current_relative_cluster = cluster_count - 1; + + /* If the remaining bytes exactly fits the cluster size, check for + a possible adjustment to the next cluster. */ + if ((bytes_remaining == bytes_per_cluster) && + (cluster >= FX_FAT_ENTRY_START) && (cluster < media_ptr -> fx_media_fat_reserved)) + { + + /* We need to position to next allocated cluster. */ + file_ptr -> fx_file_current_physical_cluster = cluster; + file_ptr -> fx_file_current_relative_cluster++; + + /* Clear the remaining bytes. */ + bytes_remaining = 0; + } + + /* This is the cluster that contains the seek position. */ + break; + } + } + + /* Check for errors in traversal of the FAT chain. */ + if (byte_offset > (((ULONG64) bytes_per_cluster) * ((ULONG64) cluster_count))) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* This is an error that suggests a corrupt file. */ + return(FX_FILE_CORRUPT); + } + } + else + { + + /* we should directly access the desired cluster */ + file_ptr -> fx_file_current_relative_cluster = (ULONG)(byte_offset / bytes_per_cluster); + + file_ptr -> fx_file_current_physical_cluster = + file_ptr -> fx_file_first_physical_cluster + file_ptr -> fx_file_current_relative_cluster; + + bytes_remaining = byte_offset % bytes_per_cluster; + } + + + /* Determine if the remaining bytes fit exactly into the cluster size. */ + if (bytes_remaining == bytes_per_cluster) + { + + /* Position to the end of the cluster. */ + file_ptr -> fx_file_current_logical_sector = (ULONG)(((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG64)file_ptr -> fx_file_current_physical_cluster - FX_FAT_ENTRY_START) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) + + ((bytes_remaining - 1) / (ULONG)media_ptr -> fx_media_bytes_per_sector)); + file_ptr -> fx_file_current_relative_sector = (UINT)(((bytes_remaining - 1) / (ULONG)media_ptr -> fx_media_bytes_per_sector)); + file_ptr -> fx_file_current_file_offset = byte_offset; + file_ptr -> fx_file_current_logical_offset = media_ptr -> fx_media_bytes_per_sector; + } + else + { + + /* Position the pointers to the new offset. */ + file_ptr -> fx_file_current_logical_sector = (ULONG)(((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG64)file_ptr -> fx_file_current_physical_cluster - FX_FAT_ENTRY_START) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) + + (bytes_remaining / (ULONG)media_ptr -> fx_media_bytes_per_sector)); + file_ptr -> fx_file_current_relative_sector = (UINT)((bytes_remaining / (ULONG)media_ptr -> fx_media_bytes_per_sector)); + file_ptr -> fx_file_current_file_offset = byte_offset; + file_ptr -> fx_file_current_logical_offset = (ULONG)(bytes_remaining % ((ULONG)media_ptr -> fx_media_bytes_per_sector)); + } + + /* Release media protection. */ + FX_UNPROTECT + + /* Seek is complete, return successful status. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_file_extended_truncate.c b/common/src/fx_file_extended_truncate.c new file mode 100644 index 0000000..c5de8c6 --- /dev/null +++ b/common/src/fx_file_extended_truncate.c @@ -0,0 +1,347 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_file.h" +#include "fx_utility.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_file_extended_truncate PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the file to the specified size, if smaller than */ +/* the current file size. If the new file size is less than the */ +/* current file read/write position, the internal file pointers will */ +/* also be modified. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* size New size of the file in bytes */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_FAT_entry_read Read a FAT entry */ +/* _fx_fault_tolerant_transaction_start Start fault tolerant */ +/* transaction */ +/* _fx_fault_tolerant_transaction_end End fault tolerant transaction*/ +/* _fx_fault_tolerant_recover Recover FAT chain */ +/* _fx_fault_tolerant_reset_log_file Reset the log file */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_file_extended_truncate(FX_FILE *file_ptr, ULONG64 size) +{ + +UINT status; +ULONG cluster; +ULONG contents = 0; +ULONG bytes_per_cluster; +ULONG last_cluster; +ULONG cluster_count; +ULONG64 bytes_remaining; +FX_MEDIA *media_ptr; + +#ifndef FX_DONT_UPDATE_OPEN_FILES +ULONG open_count; +FX_FILE *search_ptr; +#endif + +#ifdef TX_ENABLE_EVENT_TRACE +TX_TRACE_BUFFER_ENTRY *trace_event; +ULONG trace_timestamp; +#endif + + + /* First, determine if the file is still open. */ + if (file_ptr -> fx_file_id != FX_FILE_ID) + { + + /* Return the file not open error status. */ + return(FX_NOT_OPEN); + } + +#ifndef FX_MEDIA_STATISTICS_DISABLE + /* Setup pointer to media structure. */ + media_ptr = file_ptr -> fx_file_media_ptr; + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_file_truncates++; +#endif + + /* Setup pointer to associated media control block. */ + media_ptr = file_ptr -> fx_file_media_ptr; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_TRUNCATE, file_ptr, size, file_ptr -> fx_file_current_file_size, 0, FX_TRACE_FILE_EVENTS, &trace_event, &trace_timestamp) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Make sure this file is open for writing. */ + if (file_ptr -> fx_file_open_mode != FX_OPEN_FOR_WRITE) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the access error exception - a write was attempted from + a file opened for reading! */ + return(FX_ACCESS_ERROR); + } + + /* Check for write protect at the media level (set by driver). */ + if (media_ptr -> fx_media_driver_write_protect) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return write protect error. */ + return(FX_WRITE_PROTECT); + } + + /* Setup the new file size - if less than the current size. */ + if (size < file_ptr -> fx_file_current_file_size) + { + + /* Setup the new size. */ + file_ptr -> fx_file_current_file_size = size; + + /* Set the modified flag as well. */ + file_ptr -> fx_file_modified = FX_TRUE; + + /* Update the trace event with the truncated size. */ + FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_FILE_TRUNCATE, 0, 0, 0, size) + } + else + { + + /* Update the trace event with the truncated size. */ + FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_FILE_TRUNCATE, 0, 0, 0, file_ptr -> fx_file_current_file_size) + + /* Release media protection. */ + FX_UNPROTECT + + /* Just return, the new size is larger than the current size. */ + return(FX_SUCCESS); + } + +#ifndef FX_DONT_UPDATE_OPEN_FILES + + /* Search the opened files list to see if the same file is opened for reading. */ + open_count = media_ptr -> fx_media_opened_file_count; + search_ptr = media_ptr -> fx_media_opened_file_list; + while (open_count) + { + + /* Is this file the same file opened for reading? */ + if ((search_ptr != file_ptr) && + (search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector == + file_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector) && + (search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset == + file_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset)) + { + + /* Yes, the same file is opened for reading. */ + + /* Setup the new file size. */ + search_ptr -> fx_file_current_file_size = size; + search_ptr -> fx_file_dir_entry.fx_dir_entry_file_size = size; + } + + /* Adjust the pointer and decrement the search count. */ + search_ptr = search_ptr -> fx_file_opened_next; + open_count--; + } +#endif + + /* Now check to see if the read/write internal file pointers need + to be adjusted. */ + if (file_ptr -> fx_file_current_file_offset > file_ptr -> fx_file_current_file_size) + { + + /* Calculate the number of bytes per cluster. */ + bytes_per_cluster = ((ULONG)media_ptr -> fx_media_bytes_per_sector) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster); + + /* At this point, we are ready to walk list of clusters to setup the + seek position of this file. */ + cluster = file_ptr -> fx_file_first_physical_cluster; + bytes_remaining = size; + last_cluster = 0; + cluster_count = 0; + + /* Follow the link of FAT entries. */ + while ((cluster >= FX_FAT_ENTRY_START) && (cluster < media_ptr -> fx_media_fat_reserved)) + { + + /* Increment the number of clusters. */ + cluster_count++; + + /* Read the current cluster entry from the FAT. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &contents); + + /* Check the return value. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* Save the last valid cluster. */ + last_cluster = cluster; + + /* Setup for the next cluster. */ + cluster = contents; + + /* Determine if this is the last written cluster. */ + if (bytes_remaining >= bytes_per_cluster) + { + + /* Still more seeking, just decrement the working byte offset. */ + bytes_remaining = bytes_remaining - bytes_per_cluster; + } + else + { + + /* This is the cluster that contains the seek position. */ + break; + } + } + + /* Check for errors in traversal of the FAT chain. */ + if (size > (((ULONG64) bytes_per_cluster) * ((ULONG64) cluster_count))) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* This is an error that suggests a corrupt file. */ + return(FX_FILE_CORRUPT); + } + + /* Position the pointers to the new offset. */ + file_ptr -> fx_file_current_physical_cluster = last_cluster; + file_ptr -> fx_file_current_relative_cluster = cluster_count - 1; + file_ptr -> fx_file_current_logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG64)file_ptr -> fx_file_current_physical_cluster - FX_FAT_ENTRY_START) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) + + (bytes_remaining / (ULONG)media_ptr -> fx_media_bytes_per_sector); + file_ptr -> fx_file_current_relative_sector = (UINT)((bytes_remaining / (ULONG)media_ptr -> fx_media_bytes_per_sector)); + file_ptr -> fx_file_current_file_offset = size; + file_ptr -> fx_file_current_logical_offset = (ULONG)bytes_remaining % ((ULONG)media_ptr -> fx_media_bytes_per_sector); + } + +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled) + { + + /* Start transaction. */ + _fx_fault_tolerant_transaction_start(media_ptr); + + /* Copy the new file size into the directory entry. */ + file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size = file_ptr -> fx_file_current_file_size; + + /* Write the directory entry to the media. */ + status = _fx_directory_entry_write(media_ptr, &(file_ptr -> fx_file_dir_entry)); + + /* Check for a good status. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Error writing the directory. */ + return(status); + } + + /* End transaction. */ + status = _fx_fault_tolerant_transaction_end(media_ptr); + + /* Check for a good status. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Error writing the directory. */ + return(status); + } + + /* Update maximum size used if necessary. */ + if (size < file_ptr -> fx_file_maximum_size_used) + { + file_ptr -> fx_file_maximum_size_used = size; + } + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Truncate is complete, return successful status. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_file_extended_truncate_release.c b/common/src/fx_file_extended_truncate_release.c new file mode 100644 index 0000000..399ad6d --- /dev/null +++ b/common/src/fx_file_extended_truncate_release.c @@ -0,0 +1,814 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_file.h" +#include "fx_utility.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_file_extended_truncate_release PORTABLE C */ +/* 6.1.3 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the file to the specified size, if smaller than */ +/* the current file size. If the new file size is less than the */ +/* current file read/write position, the internal file pointers will */ +/* also be modified. Any unused clusters are released back to the */ +/* media. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* size New size of the file in bytes */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_entry_write Write directory entry */ +/* _fx_utility_FAT_entry_read Read a FAT entry */ +/* _fx_utility_FAT_entry_write Write a FAT entry */ +/* _fx_utility_FAT_flush Flush written FAT entries */ +/* _fx_fault_tolerant_transaction_start Start fault tolerant */ +/* transaction */ +/* _fx_fault_tolerant_transaction_end End fault tolerant transaction*/ +/* _fx_fault_tolerant_recover Recover FAT chain */ +/* _fx_fault_tolerant_reset_log_file Reset the log file */ +/* _fx_fault_tolerant_set_FAT_chain Set data of FAT chain */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* 12-31-2020 William E. Lamie Modified comment(s), fixed */ +/* available cluster issue, */ +/* resulting in version 6.1.3 */ +/* */ +/**************************************************************************/ +UINT _fx_file_extended_truncate_release(FX_FILE *file_ptr, ULONG64 size) +{ + +UINT status; +ULONG cluster; +ULONG contents; +ULONG bytes_per_cluster; +ULONG last_cluster; +ULONG cluster_count; +ULONG64 bytes_remaining; +FX_MEDIA *media_ptr; + + +#ifndef FX_DONT_UPDATE_OPEN_FILES +ULONG open_count; +FX_FILE *search_ptr; +#endif + +#ifdef TX_ENABLE_EVENT_TRACE +TX_TRACE_BUFFER_ENTRY *trace_event; +ULONG trace_timestamp; +#endif + + + /* First, determine if the file is still open. */ + if (file_ptr -> fx_file_id != FX_FILE_ID) + { + + /* Return the file not open error status. */ + return(FX_NOT_OPEN); + } + +#ifndef FX_MEDIA_STATISTICS_DISABLE + /* Setup pointer to media structure. */ + media_ptr = file_ptr -> fx_file_media_ptr; + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_file_truncate_releases++; +#endif + + /* Setup pointer to associated media control block. */ + media_ptr = file_ptr -> fx_file_media_ptr; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_TRUNCATE_RELEASE, file_ptr, size, file_ptr -> fx_file_current_file_size, 0, FX_TRACE_FILE_EVENTS, &trace_event, &trace_timestamp) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Start transaction. */ + _fx_fault_tolerant_transaction_start(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Make sure this file is open for writing. */ + if (file_ptr -> fx_file_open_mode != FX_OPEN_FOR_WRITE) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the access error exception - a write was attempted from + a file opened for reading! */ + return(FX_ACCESS_ERROR); + } + + /* Check for write protect at the media level (set by driver). */ + if (media_ptr -> fx_media_driver_write_protect) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return write protect error. */ + return(FX_WRITE_PROTECT); + } + + /* Calculate the number of bytes per cluster. */ + bytes_per_cluster = ((ULONG)media_ptr -> fx_media_bytes_per_sector) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster); + + /* Check for invalid value. */ + if (bytes_per_cluster == 0) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Invalid media, return error. */ + return(FX_MEDIA_INVALID); + } + + /* Setup the new file available size - if less than the current available size. */ + if (size < file_ptr -> fx_file_current_available_size) + { + + /* Yes, the file needs to be truncated. */ + + /* Update the available file size. */ + file_ptr -> fx_file_current_available_size = ((size + bytes_per_cluster - 1) / bytes_per_cluster) * bytes_per_cluster; + + /* Is the new available size less than the actual file size? */ + if (size < file_ptr -> fx_file_current_file_size) + { + + /* Setup the new file size. */ + file_ptr -> fx_file_current_file_size = size; + + /* Set the modified flag. */ + file_ptr -> fx_file_modified = FX_TRUE; + + /* Copy the new file size into the directory entry. */ + file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size = size; + + /* Set the first cluster to NULL. */ + if (size == 0) + { + + /* Yes, the first cluster needs to be cleared since the entire + file is going to be truncated. */ + file_ptr -> fx_file_dir_entry.fx_dir_entry_cluster = FX_NULL; + } + + /* Write the directory entry to the media. */ + status = _fx_directory_entry_write(media_ptr, &(file_ptr -> fx_file_dir_entry)); + + /* Check for a good status. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Error writing the directory. */ + return(status); + } + } + + /* Update the trace event with the truncated size. */ + FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_FILE_TRUNCATE_RELEASE, 0, 0, 0, file_ptr -> fx_file_current_file_size) + } + else + { + + /* Update the trace event with the truncated size. */ + FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_FILE_TRUNCATE_RELEASE, 0, 0, 0, file_ptr -> fx_file_current_file_size) + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Just return, the truncate size is larger than the available size. */ + return(FX_SUCCESS); + } + + /* Calculate the number of bytes per cluster. */ + bytes_per_cluster = ((ULONG)media_ptr -> fx_media_bytes_per_sector) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster); + + /* Now check to see if the read/write internal file pointers need + to be adjusted. */ + if (file_ptr -> fx_file_current_file_offset > file_ptr -> fx_file_current_file_size) + { + + + /* At this point, we are ready to walk list of clusters to setup the + seek position of this file. */ + cluster = file_ptr -> fx_file_first_physical_cluster; + bytes_remaining = size; + last_cluster = 0; + cluster_count = 0; + + /* Follow the link of FAT entries. */ + while ((cluster >= FX_FAT_ENTRY_START) && (cluster < media_ptr -> fx_media_fat_reserved)) + { + + /* Increment the number of clusters. */ + cluster_count++; + + + /* Read the current cluster entry from the FAT. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &contents); + + /* Check the return value. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* Save the last valid cluster. */ + last_cluster = cluster; + + /* Setup for the next cluster. */ + cluster = contents; + + /* Determine if this is the last written cluster. */ + if (bytes_remaining >= bytes_per_cluster) + { + + /* Still more seeking, just decrement the working byte offset. */ + bytes_remaining = bytes_remaining - bytes_per_cluster; + } + else + { + + /* This is the cluster that contains the seek position. */ + break; + } + } + + /* Check for errors in traversal of the FAT chain. */ + if (size > (((ULONG64) bytes_per_cluster) * ((ULONG64) cluster_count))) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* This is an error that suggests a corrupt file. */ + return(FX_FILE_CORRUPT); + } + + /* Position the pointers to the new offset. */ + file_ptr -> fx_file_current_physical_cluster = last_cluster; + file_ptr -> fx_file_current_relative_cluster = cluster_count - 1; + file_ptr -> fx_file_current_logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG64)file_ptr -> fx_file_current_physical_cluster - FX_FAT_ENTRY_START) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) + + (bytes_remaining / (ULONG)media_ptr -> fx_media_bytes_per_sector); + file_ptr -> fx_file_current_relative_sector = (UINT)((bytes_remaining / (ULONG)media_ptr -> fx_media_bytes_per_sector)); + file_ptr -> fx_file_current_file_offset = size; + file_ptr -> fx_file_current_logical_offset = (ULONG)bytes_remaining % ((ULONG)media_ptr -> fx_media_bytes_per_sector); + } + + /* Determine how many clusters are actually in-use now. */ + cluster_count = (ULONG) (file_ptr -> fx_file_current_available_size + (((ULONG64) bytes_per_cluster) - ((ULONG64) 1)))/bytes_per_cluster; + + /* Save the number of clusters in-use. */ + file_ptr -> fx_file_total_clusters = cluster_count; + + /* At this point, we are ready to walk list of clusters to find the clusters + that can be released. */ + cluster = file_ptr -> fx_file_first_physical_cluster; + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Is this the last used cluster? */ + if ((cluster_count == 0) && (media_ptr -> fx_media_fault_tolerant_enabled)) + { + + /* Set undo log. */ + status = _fx_fault_tolerant_set_FAT_chain(media_ptr, FX_FALSE, FX_FREE_CLUSTER, + media_ptr -> fx_media_fat_last, cluster, media_ptr -> fx_media_fat_last); + + /* Determine if the write was successful. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + file_ptr -> fx_file_last_physical_cluster = cluster; + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Follow the link of FAT entries. */ + while ((cluster >= FX_FAT_ENTRY_START) && (cluster < media_ptr -> fx_media_fat_reserved)) + { + + /* Read the current cluster entry from the FAT. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &contents); + + /* Check the return value. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* Determine if are more clusters to release. */ + if (cluster_count > 0) + { + + /* Decrement the number of clusters. */ + cluster_count--; + + /* Is this the last used cluster? */ + if (cluster_count == 0) + { + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled) + { + + /* Set undo phase. */ + media_ptr -> fx_media_fault_tolerant_state |= FX_FAULT_TOLERANT_STATE_SET_FAT_CHAIN; + + /* Read the current cluster entry from the FAT. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &contents); + + /* Check the return value. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* Set undo log. */ + status = _fx_fault_tolerant_set_FAT_chain(media_ptr, FX_FALSE, cluster, + media_ptr -> fx_media_fat_last, contents, media_ptr -> fx_media_fat_last); + + /* Determine if the write was successful. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Yes, it should be designated as last cluster. */ + status = _fx_utility_FAT_entry_write(media_ptr, cluster, media_ptr -> fx_media_fat_last); + + /* Check the return value. */ + if (status) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled) + { + + /* Clear undo phase. */ + media_ptr -> fx_media_fault_tolerant_state &= (UCHAR)(~FX_FAULT_TOLERANT_STATE_SET_FAT_CHAIN & 0xff); + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + } + + file_ptr -> fx_file_last_physical_cluster = cluster; + +#ifdef FX_FAULT_TOLERANT + + /* Flush the cached individual FAT entries. */ + status = _fx_utility_FAT_flush(media_ptr); + + /* Check the return value. */ + if (status) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } +#endif + } + } + else + { + + /* This is a cluster after the clusters used by the file, release + it back to the media. */ + +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled == FX_FALSE) + { +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + status = _fx_utility_FAT_entry_write(media_ptr, cluster, FX_FREE_CLUSTER); + + /* Check the return value. */ + if (status) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* Increment the number of available clusters. */ + media_ptr -> fx_media_available_clusters++; + +#ifdef FX_ENABLE_FAULT_TOLERANT + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + } + + /* Setup for the next cluster. */ + cluster = contents; + } + + /* Determine if we need to adjust the number of leading consecutive clusters. */ + if (file_ptr -> fx_file_consecutive_cluster > file_ptr -> fx_file_total_clusters) + { + + /* Adjust the leading consecutive cluster count. */ + file_ptr -> fx_file_consecutive_cluster = file_ptr -> fx_file_total_clusters; + } + + /* Determine if the file available size has been truncated to zero. */ + if (file_ptr -> fx_file_current_available_size == 0) + { + + /* Yes, the first cluster has already been released. Update the file info + to indicate the file has no clusters. */ + file_ptr -> fx_file_last_physical_cluster = 0; + file_ptr -> fx_file_first_physical_cluster = 0; + file_ptr -> fx_file_current_physical_cluster = 0; + file_ptr -> fx_file_current_logical_sector = 0; + file_ptr -> fx_file_current_relative_cluster = 0; + file_ptr -> fx_file_current_relative_sector = 0; + file_ptr -> fx_file_current_available_size = 0; + file_ptr -> fx_file_consecutive_cluster = 1; + } + +#ifndef FX_DONT_UPDATE_OPEN_FILES + + /* Search the opened files list to see if the same file is opened for reading. */ + open_count = media_ptr -> fx_media_opened_file_count; + search_ptr = media_ptr -> fx_media_opened_file_list; + while (open_count) + { + + /* Is this file the same file opened for reading? */ + if ((search_ptr != file_ptr) && + (search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector == + file_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector) && + (search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset == + file_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset)) + { + + /* Yes, the same file is opened for reading. */ + + /* Setup the new file size. */ + search_ptr -> fx_file_current_file_size = size; + + /* Setup the new total clusters. */ + search_ptr -> fx_file_total_clusters = file_ptr -> fx_file_total_clusters; + + /* Copy the directory entry. */ + search_ptr -> fx_file_dir_entry.fx_dir_entry_cluster = file_ptr -> fx_file_dir_entry.fx_dir_entry_cluster; + search_ptr -> fx_file_dir_entry.fx_dir_entry_file_size = file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size; + search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector = file_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector; + search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset = file_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset; + + /* Setup the other file parameters. */ + search_ptr -> fx_file_last_physical_cluster = file_ptr -> fx_file_last_physical_cluster; + search_ptr -> fx_file_first_physical_cluster = file_ptr -> fx_file_first_physical_cluster; + search_ptr -> fx_file_current_available_size = file_ptr -> fx_file_current_available_size; + search_ptr -> fx_file_consecutive_cluster = file_ptr -> fx_file_consecutive_cluster; + + /* Determine if the truncated file is smaller than the current file offset. */ + if (search_ptr -> fx_file_current_file_offset > size) + { + + /* Yes, the current file parameters need to be changed since the file was + truncated to a position prior to the current file position. */ + + /* Calculate the number of bytes per cluster. */ + bytes_per_cluster = ((ULONG)media_ptr -> fx_media_bytes_per_sector) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster); + + /* At this point, we are ready to walk list of clusters to setup the + seek position of this file. */ + cluster = search_ptr -> fx_file_first_physical_cluster; + bytes_remaining = size; + last_cluster = 0; + cluster_count = 0; + + /* Follow the link of FAT entries. */ + while ((cluster >= FX_FAT_ENTRY_START) && (cluster < media_ptr -> fx_media_fat_reserved)) + { + + /* Increment the number of clusters. */ + cluster_count++; + + + /* Read the current cluster entry from the FAT. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &contents); + + /* Check the return value. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* Save the last valid cluster. */ + last_cluster = cluster; + + /* Setup for the next cluster. */ + cluster = contents; + + /* Determine if this is the last written cluster. */ + if (bytes_remaining >= bytes_per_cluster) + { + + /* Still more seeking, just decrement the working byte offset. */ + bytes_remaining = bytes_remaining - bytes_per_cluster; + } + else + { + + /* This is the cluster that contains the seek position. */ + break; + } + } + + /* Check for errors in traversal of the FAT chain. */ + if (size > (((ULONG64) bytes_per_cluster) * ((ULONG64) cluster_count))) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* This is an error that suggests a corrupt file. */ + return(FX_FILE_CORRUPT); + } + + /* Position the pointers to the new offset. */ + + /* Determine if there is at least one cluster. */ + if (cluster_count) + { + + /* Calculate real file parameters. */ + search_ptr -> fx_file_current_physical_cluster = last_cluster; + search_ptr -> fx_file_current_relative_cluster = cluster_count - 1; + search_ptr -> fx_file_current_logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG64)search_ptr -> fx_file_current_physical_cluster - FX_FAT_ENTRY_START) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) + + (bytes_remaining / (ULONG)media_ptr -> fx_media_bytes_per_sector); + search_ptr -> fx_file_current_relative_sector = (UINT)((bytes_remaining / (ULONG)media_ptr -> fx_media_bytes_per_sector)); + search_ptr -> fx_file_current_file_offset = size; + search_ptr -> fx_file_current_logical_offset = (ULONG)bytes_remaining % ((ULONG)media_ptr -> fx_media_bytes_per_sector); + } + else + { + + /* Calculate zero-length file parameters. */ + search_ptr -> fx_file_current_physical_cluster = 0; + search_ptr -> fx_file_current_relative_cluster = 0; + search_ptr -> fx_file_current_logical_sector = 0; + search_ptr -> fx_file_current_relative_sector = 0; + search_ptr -> fx_file_current_file_offset = 0; + search_ptr -> fx_file_current_logical_offset = 0; + } + } + } + + /* Adjust the pointer and decrement the search count. */ + search_ptr = search_ptr -> fx_file_opened_next; + open_count--; + } +#endif + +#ifdef FX_FAULT_TOLERANT + + /* Flush the cached individual FAT entries */ + status = _fx_utility_FAT_flush(media_ptr); + + /* Check the return value. */ + if (status) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + +#endif + +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled && + (media_ptr -> fx_media_fault_tolerant_state & FX_FAULT_TOLERANT_STATE_STARTED)) + { + + /* Copy the new file size into the directory entry. */ + file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size = file_ptr -> fx_file_current_file_size; + } + + /* Write the directory entry to the media. */ + status = _fx_directory_entry_write(media_ptr, &(file_ptr -> fx_file_dir_entry)); + + /* Check for a good status. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Error writing the directory. */ + return(status); + } + + /* End transaction. */ + status = _fx_fault_tolerant_transaction_end(media_ptr); + + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + /* Update maximum size used if necessary. */ + if (size < file_ptr -> fx_file_maximum_size_used) + { + file_ptr -> fx_file_maximum_size_used = size; + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Truncate is complete, return successful status. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_file_open.c b/common/src/fx_file_open.c new file mode 100644 index 0000000..ca25fff --- /dev/null +++ b/common/src/fx_file_open.c @@ -0,0 +1,543 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_file.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_file_open PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function first attempts to find the specified file. If found, */ +/* the open request is validated and the file is opened. During the */ +/* opening process, all of the FAT entries for this file are examined */ +/* for their integrity. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* file_ptr File control block pointer */ +/* file_name Name pointer */ +/* open_type Type of open requested */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_search Search for the file name in */ +/* the directory structure */ +/* _fx_utility_FAT_entry_read Read a FAT entry */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), and */ +/* added conditional to */ +/* disable fast open and */ +/* consecutive detect, */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_file_open(FX_MEDIA *media_ptr, FX_FILE *file_ptr, CHAR *file_name, UINT open_type) +{ + +UINT status; +#ifndef FX_DISABLE_CONSECUTIVE_DETECT +UINT leading_consecutive; +#endif /* FX_DISABLE_CONSECUTIVE_DETECT */ +ULONG cluster; +ULONG contents = 0; +ULONG open_count; +FX_FILE *tail_ptr; +FX_FILE *search_ptr; +ULONG bytes_per_cluster; +UINT last_cluster; +ULONG cluster_count; +ULONG64 bytes_available; +ULONG64 bytes_remaining; +ULONG fat_last; +#ifndef FX_DISABLE_FAST_OPEN +UINT fast_open; +#endif /* FX_DISABLE_FAST_OPEN */ +UCHAR not_a_file_attr; + + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_file_opens++; +#endif + + /* Clear the notify function. */ + file_ptr -> fx_file_write_notify = FX_NULL; + + /* Determine the type of FAT and setup variables accordingly. */ + if (media_ptr -> fx_media_32_bit_FAT) + { + fat_last = FX_LAST_CLUSTER_1_32; + not_a_file_attr = FX_DIRECTORY | FX_VOLUME; + } + else + { + fat_last = FX_LAST_CLUSTER_1; + not_a_file_attr = FX_DIRECTORY | FX_VOLUME; + } + +#ifndef FX_DISABLE_FAST_OPEN + /* Determine if a fast open is selected. */ + if (open_type == FX_OPEN_FOR_READ_FAST) + { + + /* Yes, convert the open type to a standard read. */ + open_type = FX_OPEN_FOR_READ; + + /* Set the open fast flag. */ + fast_open = FX_TRUE; + } + else + { + + /* A fast open is not selected, set the flag to false. */ + fast_open = FX_FALSE; + } +#endif /* FX_DISABLE_FAST_OPEN */ + + /* If trace is enabled, register this object. */ + FX_TRACE_OBJECT_REGISTER(FX_TRACE_OBJECT_TYPE_FILE, file_ptr, file_name, 0, 0) + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_OPEN, media_ptr, file_ptr, file_name, open_type, FX_TRACE_FILE_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Setup file name pointer. */ + file_ptr -> fx_file_dir_entry.fx_dir_entry_name = file_ptr -> fx_file_name_buffer; + file_ptr -> fx_file_dir_entry.fx_dir_entry_short_name[0] = 0; + + /* Search the system for the supplied file name. */ + status = _fx_directory_search(media_ptr, file_name, &(file_ptr -> fx_file_dir_entry), FX_NULL, FX_NULL); + + /* Determine if the search was successful. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + /* Check to make sure the found entry is a file. */ + if (file_ptr -> fx_file_dir_entry.fx_dir_entry_attributes & not_a_file_attr) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the not a file error code. */ + return(FX_NOT_A_FILE); + } + +#ifdef FX_SINGLE_OPEN_LEGACY + /* Check to make sure the access is okay. */ + if (open_type == FX_OPEN_FOR_READ) + { + + /* Check the list of open files for others open for writing. */ + open_count = media_ptr -> fx_media_opened_file_count; + search_ptr = media_ptr -> fx_media_opened_file_list; + while (open_count) + { + + /* Look at each opened file to see if the same file is opened + for writing. */ + if ((search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector == + file_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector) && + (search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset == + file_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset) && + (search_ptr -> fx_file_open_mode)) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* The file has been opened for writing by a previous call. */ + return(FX_ACCESS_ERROR); + } + + /* Adjust the pointer and decrement the search count. */ + search_ptr = search_ptr -> fx_file_opened_next; + open_count--; + } + } + else +#else + if (open_type == FX_OPEN_FOR_WRITE) +#endif + { + + /* A open for write request is present, check the file attributes + and the list of open files for any other open instance of + this file. */ + if (media_ptr -> fx_media_driver_write_protect) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return write protect error. */ + return(FX_WRITE_PROTECT); + } + + if (file_ptr -> fx_file_dir_entry.fx_dir_entry_attributes & (UCHAR)(FX_READ_ONLY)) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the not a file error code. */ + return(FX_ACCESS_ERROR); + } + + /* Also search the opened files to see if this file is currently + opened. */ + open_count = media_ptr -> fx_media_opened_file_count; + search_ptr = media_ptr -> fx_media_opened_file_list; + while (open_count) + { + + /* Look at each opened file to see if the same file is already opened. */ +#ifdef FX_SINGLE_OPEN_LEGACY + if ((search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector == + file_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector) && + (search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset == + file_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset)) +#else + /* Look at each opened file to see if the same file is already opened + for writing. */ + if ((search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector == + file_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector) && + (search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset == + file_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset) && + (search_ptr -> fx_file_open_mode == FX_OPEN_FOR_WRITE)) +#endif + { + + /* Release media protection. */ + FX_UNPROTECT + + /* The file is currently open. */ + return(FX_ACCESS_ERROR); + } + + /* Adjust the pointer and decrement the search count. */ + search_ptr = search_ptr -> fx_file_opened_next; + open_count--; + } + } + + /* At this point, we are ready to walk list of clusters to setup the + initial condition of this file as well as to verify its integrity. */ + cluster = file_ptr -> fx_file_dir_entry.fx_dir_entry_cluster; + bytes_remaining = file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size; + bytes_per_cluster = ((ULONG)media_ptr -> fx_media_bytes_per_sector) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster); + file_ptr -> fx_file_current_physical_cluster = 0; + + /* Check for invalid value. */ + if (bytes_per_cluster == 0) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Invalid media, return error. */ + return(FX_MEDIA_INVALID); + } + + last_cluster = 0; + cluster_count = 0; + +#ifndef FX_DISABLE_CONSECUTIVE_DETECT + leading_consecutive = 1; +#endif /* FX_DISABLE_CONSECUTIVE_DETECT */ + file_ptr -> fx_file_consecutive_cluster = 1; +#ifndef FX_DISABLE_FAST_OPEN + + /* Determine if the file is being open for reading with the fast option. */ + if (fast_open) + { + + /* Calculate the bytes available. */ + bytes_available = ((bytes_remaining + bytes_per_cluster - 1) / bytes_per_cluster) * bytes_per_cluster; + + } + else +#endif /* FX_DISABLE_FAST_OPEN */ + { + + /* Follow the link of FAT entries. */ + while ((cluster >= FX_FAT_ENTRY_START) && (cluster < media_ptr -> fx_media_fat_reserved)) + { + + /* Increment the number of clusters. */ + cluster_count++; + + /* Read the current cluster entry from the FAT. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &contents); + + /* Check the return value. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* Determine if the cluster is invalid (points to itself) or the count exceeds the total number of clusters. */ + if ((cluster == contents) || (cluster_count > media_ptr -> fx_media_total_clusters)) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(FX_FAT_READ_ERROR); + } + +#ifndef FX_DISABLE_CONSECUTIVE_DETECT + + /* Check if present and next clusters are consecutive */ + if (cluster + 1 == contents) + { + + /* Determine if clusters are consecutive so far. */ + if (leading_consecutive) + { + + /* Yes, increment the number of leading consecutive clusters. */ + file_ptr -> fx_file_consecutive_cluster++; + } + } + else + { + + /* The clusters are no longer consecutive, clear the consecutive flag. */ + leading_consecutive = 0; + } +#endif /* FX_DISABLE_CONSECUTIVE_DETECT */ + + /* Save the last valid cluster. */ + last_cluster = cluster; + + /* Setup for the next cluster. */ + cluster = contents; + + /* Determine if this is the last written cluster. We need to remember this + for open for writing. */ + if (bytes_remaining > bytes_per_cluster) + { + + /* Still more written clusters, just decrement the counter. */ + bytes_remaining = bytes_remaining - bytes_per_cluster; + } + else if (!file_ptr -> fx_file_current_physical_cluster) + { + + /* Remember this cluster number. */ + file_ptr -> fx_file_current_physical_cluster = last_cluster; + + /* Remember the relative cluster. */ + file_ptr -> fx_file_current_relative_cluster = cluster_count - 1; + + /* If the remaining bytes exactly fits the cluster size, check for + a possible adjustment to the next cluster. */ + if ((bytes_remaining == bytes_per_cluster) && + (cluster >= FX_FAT_ENTRY_START) && (cluster < media_ptr -> fx_media_fat_reserved)) + { + + /* We need to position to next allocated cluster. */ + file_ptr -> fx_file_current_physical_cluster = cluster; + file_ptr -> fx_file_current_relative_cluster++; + + /* Clear the remaining bytes. */ + bytes_remaining = 0; + } + } + } + + /* Determine if the number of clusters is large enough to support the + specified file size. */ + bytes_available = ((ULONG64)media_ptr -> fx_media_bytes_per_sector) * + ((ULONG64)media_ptr -> fx_media_sectors_per_cluster) * + ((ULONG64)cluster_count); + + /* Check the bytes available in the cluster chain against the directory entry file size. */ + if ((bytes_available < file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size) || + ((cluster_count) && (contents < fat_last))) + { + /* File is corrupt, release media protection. */ + FX_UNPROTECT + + /* Return a corrupt file error status. */ + return(FX_FILE_CORRUPT); + } + } + + /* The file is okay, populate the file control block and complete the + file open process. */ + file_ptr -> fx_file_id = FX_FILE_ID; + file_ptr -> fx_file_name = file_ptr -> fx_file_name_buffer; + file_ptr -> fx_file_media_ptr = media_ptr; + file_ptr -> fx_file_open_mode = open_type; + file_ptr -> fx_file_modified = FX_FALSE; + file_ptr -> fx_file_total_clusters = cluster_count; + file_ptr -> fx_file_first_physical_cluster = file_ptr -> fx_file_dir_entry.fx_dir_entry_cluster; + file_ptr -> fx_file_last_physical_cluster = last_cluster; + file_ptr -> fx_file_current_file_size = file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size; + file_ptr -> fx_file_current_available_size = bytes_available; + file_ptr -> fx_file_disable_burst_cache = FX_FALSE; + + /* Set the current settings based on how the file was opened. */ + if (open_type == FX_OPEN_FOR_READ) + { + + /* Position the pointers to the beginning of the file. */ + file_ptr -> fx_file_current_physical_cluster = file_ptr -> fx_file_first_physical_cluster; + file_ptr -> fx_file_current_relative_cluster = 0; + file_ptr -> fx_file_current_logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG64)(file_ptr -> fx_file_first_physical_cluster - FX_FAT_ENTRY_START)) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)); + file_ptr -> fx_file_current_relative_sector = 0; + file_ptr -> fx_file_current_logical_offset = 0; + file_ptr -> fx_file_current_file_offset = 0; + } + else + { + + /* Open for writing - position the pointers to the end of the file. */ + + /* Determine if the remaining bytes fit exactly into the cluster size. */ + if (bytes_remaining == bytes_per_cluster) + { + + /* Position to the end of the cluster. */ + file_ptr -> fx_file_current_logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG64)file_ptr -> fx_file_current_physical_cluster - FX_FAT_ENTRY_START) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) + + ((ULONG)(((bytes_remaining - 1) / (ULONG)media_ptr -> fx_media_bytes_per_sector))); + file_ptr -> fx_file_current_relative_sector = (ULONG)(((bytes_remaining - 1) / (ULONG)media_ptr -> fx_media_bytes_per_sector)); + file_ptr -> fx_file_current_file_offset = file_ptr -> fx_file_current_file_size; + file_ptr -> fx_file_current_logical_offset = media_ptr -> fx_media_bytes_per_sector; + } + else + { + + /* Position file parameters at end of last cluster allocation. */ + file_ptr -> fx_file_current_logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG64)file_ptr -> fx_file_current_physical_cluster - FX_FAT_ENTRY_START) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) + + ((ULONG)((bytes_remaining / (ULONG)media_ptr -> fx_media_bytes_per_sector))); + file_ptr -> fx_file_current_relative_sector = (ULONG)((bytes_remaining / (ULONG)media_ptr -> fx_media_bytes_per_sector)); + file_ptr -> fx_file_current_file_offset = file_ptr -> fx_file_current_file_size; + file_ptr -> fx_file_current_logical_offset = (ULONG)bytes_remaining % ((ULONG)media_ptr -> fx_media_bytes_per_sector); + } + } + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* By default, the whole file is used. */ + file_ptr -> fx_file_maximum_size_used = file_ptr -> fx_file_current_file_size; +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Place newly opened file on the list of open files for + this media. First, check for an empty list. */ + if (media_ptr -> fx_media_opened_file_list) + { + + /* Pickup tail pointer. */ + tail_ptr = (media_ptr -> fx_media_opened_file_list) -> fx_file_opened_previous; + + /* Place the new file in the list. */ + (media_ptr -> fx_media_opened_file_list) -> fx_file_opened_previous = file_ptr; + tail_ptr -> fx_file_opened_next = file_ptr; + + /* Setup this file's opened links. */ + file_ptr -> fx_file_opened_previous = tail_ptr; + file_ptr -> fx_file_opened_next = media_ptr -> fx_media_opened_file_list; + } + else + { + + /* The opened media list is empty. Add the media to empty list. */ + media_ptr -> fx_media_opened_file_list = file_ptr; + file_ptr -> fx_file_opened_next = file_ptr; + file_ptr -> fx_file_opened_previous = file_ptr; + } + + /* Increment the opened file counter. */ + media_ptr -> fx_media_opened_file_count++; + + /* Release media protection. */ + FX_UNPROTECT + + /* Open is complete, return successful status. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_file_read.c b/common/src/fx_file_read.c new file mode 100644 index 0000000..22622f7 --- /dev/null +++ b/common/src/fx_file_read.c @@ -0,0 +1,441 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_file.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_file_read PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads the specified number of bytes (or as many as */ +/* possible into the buffer supplied by the caller. The actual number */ +/* of bytes and the status of the read operation is returned to the */ +/* caller. In addition, various internal file pointers in the file */ +/* control block are also updated. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* buffer_ptr Buffer pointer */ +/* request_size Number of bytes requested */ +/* actual_size Pointer to variable for the */ +/* number of bytes read */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_FAT_entry_read Read a FAT entry */ +/* _fx_utility_logical_sector_read Read a logical sector */ +/* _fx_utility_memory_copy Fast memory copy routine */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), verified */ +/* memcpy usage, */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_file_read(FX_FILE *file_ptr, VOID *buffer_ptr, ULONG request_size, ULONG *actual_size) +{ + +UINT status; +ULONG bytes_remaining, i; +ULONG copy_bytes; +UCHAR *destination_ptr; +ULONG cluster, next_cluster; +UINT sectors; +FX_MEDIA *media_ptr; + +#ifdef TX_ENABLE_EVENT_TRACE +TX_TRACE_BUFFER_ENTRY *trace_event; +ULONG trace_timestamp; +#endif + + + /* First, determine if the file is still open. */ + if (file_ptr -> fx_file_id != FX_FILE_ID) + { + + /* Return the file not open error status. */ + return(FX_NOT_OPEN); + } + +#ifndef FX_MEDIA_STATISTICS_DISABLE + /* Setup pointer to media structure. */ + media_ptr = file_ptr -> fx_file_media_ptr; + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_file_reads++; +#endif + + /* Setup pointer to associated media control block. */ + media_ptr = file_ptr -> fx_file_media_ptr; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_READ, file_ptr, buffer_ptr, request_size, 0, FX_TRACE_FILE_EVENTS, &trace_event, &trace_timestamp) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Next, determine if there is any more bytes to read in the file. */ + if (file_ptr -> fx_file_current_file_offset >= + file_ptr -> fx_file_current_file_size) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* The file is at the end, return the proper status and set the + actual size to 0. */ + *actual_size = 0; + return(FX_END_OF_FILE); + } + + /* At this point there is something to read. */ + + /* Setup local buffer pointer. */ + destination_ptr = (UCHAR *)buffer_ptr; + + /* Determine if there are less bytes left in the file than that specified + by the request. If so, adjust the requested size. */ + if ((ULONG64)request_size > + (file_ptr -> fx_file_current_file_size - file_ptr -> fx_file_current_file_offset)) + { + + /* Adjust the bytes remaining to what's available. */ + request_size = (ULONG)(file_ptr -> fx_file_current_file_size - file_ptr -> fx_file_current_file_offset); + } + + /* Setup the remaining number of bytes to read. */ + bytes_remaining = request_size; + + /* Loop to read all of the bytes. */ + while (bytes_remaining) + { + + /* Determine if a beginning or ending partial read is required. */ + if ((file_ptr -> fx_file_current_logical_offset) || + (bytes_remaining < media_ptr -> fx_media_bytes_per_sector)) + { + + /* A partial sector read is required. */ + + /* Read the current logical sector. */ + status = _fx_utility_logical_sector_read(media_ptr, + file_ptr -> fx_file_current_logical_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DATA_SECTOR); + + /* Check for good completion status. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* Copy the appropriate number of bytes into the destination buffer. */ + copy_bytes = media_ptr -> fx_media_bytes_per_sector - + file_ptr -> fx_file_current_logical_offset; + + /* Check to see if only a portion of the read sector needs to be + copied. */ + if (copy_bytes > bytes_remaining) + { + + /* Adjust the number of bytes to copy. */ + copy_bytes = bytes_remaining; + } + + /* Actually perform the memory copy. */ + _fx_utility_memory_copy(((UCHAR *)media_ptr -> fx_media_memory_buffer) + /* Use case of memcpy is verified. */ + file_ptr -> fx_file_current_logical_offset, + destination_ptr, copy_bytes); + + /* Increment the logical sector byte offset. */ + file_ptr -> fx_file_current_logical_offset = + file_ptr -> fx_file_current_logical_offset + copy_bytes; + + /* Adjust the remaining bytes to read. */ + bytes_remaining = bytes_remaining - copy_bytes; + + /* Adjust the pointer to the destination buffer. */ + destination_ptr = destination_ptr + copy_bytes; + } + else + { + + /* Attempt to read multiple sectors directly into the destination + buffer. */ + + /* Calculate the number of whole sectors to read directly into + the destination buffer. */ + sectors = (UINT)(bytes_remaining / media_ptr -> fx_media_bytes_per_sector); + + + next_cluster = cluster = file_ptr -> fx_file_current_physical_cluster; + for (i = (media_ptr -> fx_media_sectors_per_cluster - + file_ptr -> fx_file_current_relative_sector); i < sectors; i += media_ptr -> fx_media_sectors_per_cluster) + { + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &next_cluster); + + /* Determine if an error is present. */ + if ((status != FX_SUCCESS) || (next_cluster < FX_FAT_ENTRY_START) || + (next_cluster > media_ptr -> fx_media_fat_reserved)) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Send error message back to caller. */ + if (status != FX_SUCCESS) + { + return(status); + } + else + { + return(FX_FILE_CORRUPT); + } + } + + if (next_cluster != cluster + 1) + { + break; + } + else + { + cluster = next_cluster; + } + } + + if (i < sectors) + { + sectors = i; + } + + /* Determine if this is a single sector read request. If so, read the sector so it will + come from the internal cache. */ + if (sectors == 1) + { + + /* Read the current logical sector. */ + status = _fx_utility_logical_sector_read(media_ptr, + file_ptr -> fx_file_current_logical_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DATA_SECTOR); + + /* Check for good completion status. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* Actually perform the memory copy. */ + _fx_utility_memory_copy((UCHAR *)media_ptr -> fx_media_memory_buffer, destination_ptr, media_ptr -> fx_media_bytes_per_sector); /* Use case of memcpy is verified. */ + } + else + { + + /* Multiple sector read request. Read all the sectors at once. */ + + /* Perform the data read directly into the user's buffer of + the appropriate number of sectors. */ + media_ptr -> fx_media_disable_burst_cache = file_ptr -> fx_file_disable_burst_cache; + status = _fx_utility_logical_sector_read(media_ptr, file_ptr -> fx_file_current_logical_sector, + destination_ptr, (ULONG) sectors, FX_DATA_SECTOR); + media_ptr -> fx_media_disable_burst_cache = FX_FALSE; + + /* Check for good completion status. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + } + + /* Now adjust the various file pointers. */ + + /* Increment the current logical sector. Subtract one from + the sector count because we are going to use the logical + offset to do additional sector/cluster arithmetic below. */ + file_ptr -> fx_file_current_logical_sector = + file_ptr -> fx_file_current_logical_sector + + (sectors - 1); + + /* Move the relative sector and cluster as well. */ + file_ptr -> fx_file_current_relative_cluster = file_ptr -> fx_file_current_relative_cluster + + (file_ptr -> fx_file_current_relative_sector + (sectors - 1)) / + media_ptr -> fx_media_sectors_per_cluster; + + file_ptr -> fx_file_current_relative_sector = + (file_ptr -> fx_file_current_relative_sector + + (sectors - 1)) % media_ptr -> fx_media_sectors_per_cluster; + + /* Increment the logical sector byte offset. */ + file_ptr -> fx_file_current_logical_offset = + media_ptr -> fx_media_bytes_per_sector; + + file_ptr -> fx_file_current_physical_cluster = cluster; + + /* Adjust the remaining bytes. */ + bytes_remaining = bytes_remaining - + (((ULONG)media_ptr -> fx_media_bytes_per_sector) * sectors); + + /* Adjust the pointer to the destination buffer. */ + destination_ptr = destination_ptr + + (((ULONG)media_ptr -> fx_media_bytes_per_sector) * sectors); + } + + /* At this point, we have either read a partial sector or have successfully + read one or more whole sectors. Determine if we are at the end of + the current logical sector. */ + if (file_ptr -> fx_file_current_logical_offset >= + media_ptr -> fx_media_bytes_per_sector) + { + + /* Determine if we are at the exact physical end of the file at the end of reading. */ + if ((bytes_remaining == 0) && ((file_ptr -> fx_file_current_file_offset + (ULONG64)request_size) >= + file_ptr -> fx_file_current_available_size)) + { + + /* Skip the following file parameter adjustments. The next write will + detect the logical offset out of the range of the sector and reset + all of the pertinent information. */ + break; + } + + /* We need to move to the next logical sector, but first + determine if the next logical sector is within the same + cluster. */ + + /* Increment the current relative sector in the cluster. */ + file_ptr -> fx_file_current_relative_sector++; + + /* Determine if this is in a new cluster. */ + if (file_ptr -> fx_file_current_relative_sector >= + media_ptr -> fx_media_sectors_per_cluster) + { + + /* Read the FAT entry of the current cluster to find + the next cluster. */ + status = _fx_utility_FAT_entry_read(media_ptr, + file_ptr -> fx_file_current_physical_cluster, &next_cluster); + + /* Determine if an error is present. */ + if ((status != FX_SUCCESS) || (next_cluster < FX_FAT_ENTRY_START) || + (next_cluster > media_ptr -> fx_media_fat_reserved)) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Send error message back to caller. */ + if (status != FX_SUCCESS) + { + return(status); + } + else + { + return(FX_FILE_CORRUPT); + } + } + + /* Otherwise, we have a new cluster. Save it in the file + control block and calculate a new logical sector value. */ + file_ptr -> fx_file_current_physical_cluster = next_cluster; + file_ptr -> fx_file_current_relative_cluster++; + file_ptr -> fx_file_current_logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + + ((((ULONG64)next_cluster) - FX_FAT_ENTRY_START) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)); + file_ptr -> fx_file_current_relative_sector = 0; + } + else + { + + /* Still within the same cluster so just increment the + logical sector. */ + file_ptr -> fx_file_current_logical_sector++; + } + + /* In either case, we are now positioned at a new sector so + clear the logical sector offset. */ + file_ptr -> fx_file_current_logical_offset = 0; + } + } + + /* Adjust the current file offset accordingly. */ + file_ptr -> fx_file_current_file_offset = + file_ptr -> fx_file_current_file_offset + (ULONG64)request_size; + + /* Store the number of bytes actually read. */ + *actual_size = request_size; + + /* Update the trace event with the bytes read. */ + FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_FILE_READ, 0, 0, 0, request_size) + + /* Update the last accessed date. */ + file_ptr -> fx_file_dir_entry.fx_dir_entry_last_accessed_date = _fx_system_date; + + /* Release media protection. */ + FX_UNPROTECT + + /* Return a successful status to the caller. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_file_relative_seek.c b/common/src/fx_file_relative_seek.c new file mode 100644 index 0000000..6de3248 --- /dev/null +++ b/common/src/fx_file_relative_seek.c @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_file_relative_seek PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function positions the internal file pointers to the specified */ +/* byte relative offset such that the next read or write operation is */ +/* performed there. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* byte_offset Byte offset of the seek */ +/* seek_from Direction for relative seek, */ +/* legal values are: */ +/* */ +/* FX_SEEK_BEGIN */ +/* FX_SEEK_END */ +/* FX_SEEK_FORWARD */ +/* FX_SEEK_BACK */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_extended_relative_seek Seek to specified position */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), and */ +/* Added conditional to */ +/* disable one line function, */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +#ifndef FX_DISABLE_ONE_LINE_FUNCTION +UINT _fx_file_relative_seek(FX_FILE *file_ptr, ULONG byte_offset, UINT seek_from) +{ + + return(_fx_file_extended_relative_seek(file_ptr, (ULONG64)byte_offset, seek_from)); +} +#endif /* FX_DISABLE_ONE_LINE_FUNCTION */ + diff --git a/common/src/fx_file_rename.c b/common/src/fx_file_rename.c new file mode 100644 index 0000000..f847f6a --- /dev/null +++ b/common/src/fx_file_rename.c @@ -0,0 +1,550 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_file.h" +#include "fx_utility.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_file_rename PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function first attempts to find the specified file. If found, */ +/* the rename request is valid and the directory entry will be changed */ +/* to the new file name. Otherwise, if the file is not found, the */ +/* appropriate error code is returned to the caller. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* old_file_name Old file name pointer */ +/* new_file_name New file name pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_entry_write Write the new directory entry */ +/* _fx_directory_free_search Search for a free directory */ +/* entry in target directory */ +/* _fx_directory_name_extract Extract the new filename */ +/* _fx_directory_search Search for the file name in */ +/* the directory structure */ +/* _fx_fault_tolerant_transaction_start Start fault tolerant */ +/* transaction */ +/* _fx_fault_tolerant_transaction_end End fault tolerant transaction*/ +/* _fx_fault_tolerant_recover Recover FAT chain */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_file_rename(FX_MEDIA *media_ptr, CHAR *old_file_name, CHAR *new_file_name) +{ + +ULONG i; +CHAR *work_ptr; +CHAR alpha, beta; +UINT status; + +#ifndef FX_DONT_UPDATE_OPEN_FILES +ULONG open_count; +FX_FILE *search_ptr; +#endif +CHAR *new_name_ptr; +FX_DIR_ENTRY old_dir_entry, new_dir_entry; +FX_DIR_ENTRY search_directory; +#ifdef FX_RENAME_PATH_INHERIT +UINT j; +#endif +UCHAR not_a_file_attr; + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_file_renames++; +#endif + + /* Setup pointers to media name buffers. */ + old_dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + new_dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN * 2; + search_directory.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN * 3; + + /* Clear the short name strings. */ + old_dir_entry.fx_dir_entry_short_name[0] = 0; + new_dir_entry.fx_dir_entry_short_name[0] = 0; + search_directory.fx_dir_entry_short_name[0] = 0; + + /* Determine if the supplied name is less than the maximum supported name size. The + maximum name (FX_MAX_LONG_NAME_LEN) is defined in fx_api.h. */ + i = 0; + work_ptr = (CHAR *)new_file_name; + while (*work_ptr) + { + + /* Determine if the character designates a new path. */ + if ((*work_ptr == '\\') || (*work_ptr == '/')) + { + /* Yes, reset the name size. */ + i = 0; + } + /* Check for leading spaces. */ + else if ((*work_ptr != ' ') || (i != 0)) + { + + /* No leading spaces, increment the name size. */ + i++; + } + + /* Move to the next character. */ + work_ptr++; + } + + /* Determine if the supplied name is valid. */ + if ((i == 0) || (i >= FX_MAX_LONG_NAME_LEN)) + { + + /* Return an invalid name value. */ + return(FX_INVALID_NAME); + } + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_RENAME, media_ptr, old_file_name, new_file_name, 0, FX_TRACE_FILE_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Start transaction. */ + _fx_fault_tolerant_transaction_start(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Check for write protect at the media level (set by driver). */ + if (media_ptr -> fx_media_driver_write_protect) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return write protect error. */ + return(FX_WRITE_PROTECT); + } + + /* Search the system for the supplied file name. */ + status = _fx_directory_search(media_ptr, old_file_name, &old_dir_entry, &search_directory, FX_NULL); + + /* Determine if the search was successful. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + not_a_file_attr = FX_DIRECTORY | FX_VOLUME; + + /* Check to make sure the found entry is a file. */ + if (old_dir_entry.fx_dir_entry_attributes & not_a_file_attr) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the not a file error code. */ + return(FX_NOT_A_FILE); + } + +#ifdef FX_RENAME_PATH_INHERIT + + /* Determine if the source file name has a path and the target file name does not. */ + if (((old_file_name[0] == '/') || (old_file_name[0] == '\\')) && (new_file_name[0] != '/') && (new_file_name[0] != '\\')) + { + + /* In this case, we need to prepend the path of the old file name to that of the new file name. */ + + /* Setup pointer to the rename buffer. */ + work_ptr = (CHAR *)media_ptr -> fx_media_rename_buffer; + + /* First, copy the path of the old file name. */ + i = 0; + j = 0; + while ((old_file_name[i]) && (i < FX_MAXIMUM_PATH)) + { + + /* Copy a character into the rename buffer. */ + *work_ptr++ = old_file_name[i]; + + /* Determine if this character is directory separator. */ + if ((old_file_name[i] == '/') || (old_file_name[i] == '\\')) + { + + /* Yes, directory separator has been found - remember the index. */ + j = i; + } + + /* Move to next position in the old file name. */ + i++; + } + + /* At this point, we have the path stored in the rename buffer. */ + + /* Position past the last slash or backslash. */ + j++; + + /* Reset the working pointer to the position after the last directory separator. */ + work_ptr = (CHAR *)&(media_ptr -> fx_media_rename_buffer[j]); + + /* Now copy the new file name into the rename buffer. */ + i = 0; + while ((new_file_name[i]) && (j < FX_MAXIMUM_PATH)) + { + + /* Copy a character into the rename buffer. */ + *work_ptr++ = new_file_name[i]; + + /* Move to next character. */ + i++; + j++; + } + + /* Determine if the path was successfully prepended. */ + if (new_file_name[i]) + { + + /* No, there was not enough room in the destination buffer. */ + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the invalid path error code. */ + return(FX_INVALID_PATH); + } + + /* Place a NULL at the end of the string. */ + *work_ptr = (CHAR)FX_NULL; + + /* At this point, we have successfully prepended the path in the new file name, override + the new file name so it is used from now on. */ + new_file_name = (CHAR *)media_ptr -> fx_media_rename_buffer; + } +#endif + + /* Search the target directory for the same file name. */ + status = _fx_directory_search(media_ptr, new_file_name, &new_dir_entry, &search_directory, &new_name_ptr); + + /* Determine if the name already exists. */ + if (status == FX_SUCCESS) + { + + /* Determine if the new name simply has an ASCII case change. If so, simply let the processing + continue. */ + i = 0; + do + { + + /* Pickup an old name and new name character and convert to upper case if necessary. */ + alpha = old_file_name[i]; + if ((alpha >= 'a') && (alpha <= 'z')) + { + + /* Lower case, convert to upper case! */ + alpha = (CHAR)((INT)alpha - 0x20); + } + beta = new_file_name[i]; + if ((beta >= 'a') && (beta <= 'z')) + { + + /* Lower case, convert to upper case! */ + beta = (CHAR)((INT)beta - 0x20); + } + + /* Now compare the characters. */ + if ((alpha != beta) || (alpha == 0)) + { + + /* Get out of this loop! */ + break; + } + + /* Move to next character. */ + i++; + } while (i < (FX_MAXIMUM_PATH-1)); + + /* Now determine if the names match. */ + if (alpha != beta) + { + + /* No, the names do not match so simply return an error + to the caller. */ + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the not a file error code. */ + return(FX_ALREADY_CREATED); + } + } + + /* Change the file name and look for extra stuff at the end. */ + if (_fx_directory_name_extract(new_name_ptr, &new_dir_entry.fx_dir_entry_name[0])) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Invalid name, return error status. */ + return(FX_INVALID_NAME); + } + + /* Search for a free spot in the target directory. */ + status = _fx_directory_free_search(media_ptr, &search_directory, &new_dir_entry); + + /* Determine if a free spot was found. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + /* Extract the new file name. */ + _fx_directory_name_extract(new_name_ptr, &new_dir_entry.fx_dir_entry_name[0]); + + /* Determine if a long name is present. */ + if (new_dir_entry.fx_dir_entry_long_name_present) + { + + /* Yes, clear the short file name. */ + new_dir_entry.fx_dir_entry_short_name[0] = 0; + } + + /* Save the updated directory parameters. */ + new_dir_entry.fx_dir_entry_attributes = old_dir_entry.fx_dir_entry_attributes; + new_dir_entry.fx_dir_entry_cluster = old_dir_entry.fx_dir_entry_cluster; + new_dir_entry.fx_dir_entry_file_size = old_dir_entry.fx_dir_entry_file_size; + + /* Save the reserved field. */ + new_dir_entry.fx_dir_entry_reserved = old_dir_entry.fx_dir_entry_reserved; + + /* Set time and date stamps. */ + new_dir_entry.fx_dir_entry_created_time_ms = old_dir_entry.fx_dir_entry_created_time_ms; + new_dir_entry.fx_dir_entry_created_time = old_dir_entry.fx_dir_entry_created_time; + new_dir_entry.fx_dir_entry_created_date = old_dir_entry.fx_dir_entry_created_date; + new_dir_entry.fx_dir_entry_last_accessed_date = old_dir_entry.fx_dir_entry_last_accessed_date; + new_dir_entry.fx_dir_entry_time = old_dir_entry.fx_dir_entry_time; + new_dir_entry.fx_dir_entry_date = old_dir_entry.fx_dir_entry_date; + + + /* Is there a leading dot? */ + if (new_dir_entry.fx_dir_entry_name[0] == '.') + { + + /* Yes, toggle the hidden attribute bit. */ + new_dir_entry.fx_dir_entry_attributes |= FX_HIDDEN; + } + +#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE + + /* Invalidate the directory cache. */ + media_ptr -> fx_media_last_found_name[0] = FX_NULL; +#endif + + /* Now write out the directory entry. */ + status = _fx_directory_entry_write(media_ptr, &new_dir_entry); + + /* Determine if the write was successful. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + +#ifndef FX_DONT_UPDATE_OPEN_FILES + + /* Search the opened files to update any currently opened files. */ + open_count = media_ptr -> fx_media_opened_file_count; + search_ptr = media_ptr -> fx_media_opened_file_list; + while (open_count) + { + + /* Look at each opened file to see if it matches the file being renamed. */ + if ((search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector == + old_dir_entry.fx_dir_entry_log_sector) && + (search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset == + old_dir_entry.fx_dir_entry_byte_offset)) + { + + /* Yes, the file being renamed is already open. Update the file's + information so that it is kept current. */ + search_ptr -> fx_file_dir_entry.fx_dir_entry_cluster = new_dir_entry.fx_dir_entry_cluster; + search_ptr -> fx_file_dir_entry.fx_dir_entry_file_size = new_dir_entry.fx_dir_entry_file_size; + search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector = new_dir_entry.fx_dir_entry_log_sector; + search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset = new_dir_entry.fx_dir_entry_byte_offset; + + /* Copy the new name into the file's name buffer. */ + i = 0; + while (i < (FX_MAX_LONG_NAME_LEN - 1)) + { + + /* Copy byte of the new name. */ + search_ptr -> fx_file_dir_entry.fx_dir_entry_name[i] = new_dir_entry.fx_dir_entry_name[i]; + + /* Move to the next character. */ + i++; + + /* Determine if we are at the end of the name. */ + if (new_dir_entry.fx_dir_entry_name[i] == FX_NULL) + { + + /* Determine if we are not at the maximum name size. */ + if (i < (FX_MAX_LONG_NAME_LEN - 1)) + { + + /* Get out of the loop. */ + break; + } + } + } + + /* Set the NULL termination in the copy of the new name. */ + search_ptr -> fx_file_dir_entry.fx_dir_entry_name[i] = FX_NULL; + } + + /* Adjust the pointer and decrement the search count. */ + search_ptr = search_ptr -> fx_file_opened_next; + open_count--; + } +#endif + + /* Now we are ready to remove the old directory entry. */ + old_dir_entry.fx_dir_entry_name[0] = (CHAR)FX_DIR_ENTRY_FREE; + old_dir_entry.fx_dir_entry_short_name[0] = (CHAR)FX_DIR_ENTRY_FREE; + + /* Now wipe out the old directory entry. */ + status = _fx_directory_entry_write(media_ptr, &old_dir_entry); + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + /* End transaction. */ + status = _fx_fault_tolerant_transaction_end(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* File rename is complete, return status. */ + return(status); +} + diff --git a/common/src/fx_file_seek.c b/common/src/fx_file_seek.c new file mode 100644 index 0000000..fe73e1a --- /dev/null +++ b/common/src/fx_file_seek.c @@ -0,0 +1,83 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_file_seek PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function positions the internal file pointers to the specified */ +/* byte offset such that the next read or write operation will be */ +/* performed there. If the byte offset is greater than the size, the */ +/* file pointers will be positioned to the end of the file. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* byte_offset Byte offset into the file */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_extended_seek Seek to specified position */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), and */ +/* Added conditional to */ +/* disable one line function, */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +#ifndef FX_DISABLE_ONE_LINE_FUNCTION +UINT _fx_file_seek(FX_FILE *file_ptr, ULONG byte_offset) +{ + + return(_fx_file_extended_seek(file_ptr, (ULONG64) byte_offset)); +} +#endif /* FX_DISABLE_ONE_LINE_FUNCTION */ diff --git a/common/src/fx_file_truncate.c b/common/src/fx_file_truncate.c new file mode 100644 index 0000000..5622cf1 --- /dev/null +++ b/common/src/fx_file_truncate.c @@ -0,0 +1,83 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_file_truncate PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the file to the specified size, if smaller than */ +/* the current file size. If the new file size is less than the */ +/* current file read/write position, the internal file pointers will */ +/* also be modified. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* size New size of the file in bytes */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_extended_truncate Truncate the file size */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), and */ +/* Added conditional to */ +/* disable one line function, */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +#ifndef FX_DISABLE_ONE_LINE_FUNCTION +UINT _fx_file_truncate(FX_FILE *file_ptr, ULONG size) +{ + + return(_fx_file_extended_truncate(file_ptr, (ULONG64)size)); +} +#endif /* FX_DISABLE_ONE_LINE_FUNCTION */ diff --git a/common/src/fx_file_truncate_release.c b/common/src/fx_file_truncate_release.c new file mode 100644 index 0000000..03e029d --- /dev/null +++ b/common/src/fx_file_truncate_release.c @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_file_truncate_release PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the file to the specified size, if smaller than */ +/* the current file size. If the new file size is less than the */ +/* current file read/write position, the internal file pointers will */ +/* also be modified. Any unused clusters are released back to the */ +/* media. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* size New size of the file in bytes */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_extended_truncate_release Truncate the file size and */ +/* released unused clusters */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), and */ +/* Added conditional to */ +/* disable one line function, */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +#ifndef FX_DISABLE_ONE_LINE_FUNCTION +UINT _fx_file_truncate_release(FX_FILE *file_ptr, ULONG size) +{ + + return(_fx_file_extended_truncate_release(file_ptr, (ULONG64)size)); +} +#endif /* FX_DISABLE_ONE_LINE_FUNCTION */ + diff --git a/common/src/fx_file_write.c b/common/src/fx_file_write.c new file mode 100644 index 0000000..55fde81 --- /dev/null +++ b/common/src/fx_file_write.c @@ -0,0 +1,1435 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_file.h" +#include "fx_directory.h" +#include "fx_utility.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_file_write PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes the specified number of bytes into the file's */ +/* data area. The status of the write operation is returned to the */ +/* caller. In addition, various internal file pointers in the file */ +/* control block are also updated. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* buffer_ptr Buffer pointer */ +/* size Number of bytes to write */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_entry_write Update the file's size */ +/* _fx_utility_FAT_entry_read Read a FAT entry */ +/* _fx_utility_FAT_entry_write Write a FAT entry */ +/* _fx_utility_FAT_flush Flush written FAT entries */ +/* _fx_utility_logical_sector_flush Flush written logical sectors */ +/* _fx_utility_logical_sector_read Read a logical sector */ +/* _fx_utility_logical_sector_write Write a logical sector */ +/* _fx_utility_memory_copy Fast memory copy routine */ +/* _fx_fault_tolerant_transaction_start Start fault tolerant */ +/* transaction */ +/* _fx_fault_tolerant_transaction_end End fault tolerant transaction*/ +/* _fx_fault_tolerant_recover Recover FAT chain */ +/* _fx_fault_tolerant_reset_log_file Reset the log file */ +/* _fx_fault_tolerant_set_FAT_chain Set data of FAT chain */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), verified */ +/* memcpy usage, */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_file_write(FX_FILE *file_ptr, VOID *buffer_ptr, ULONG size) +{ + +UINT status; +ULONG64 bytes_remaining; +ULONG i; +ULONG copy_bytes; +ULONG bytes_per_cluster; +UCHAR *source_ptr; +ULONG first_new_cluster; +ULONG last_cluster; +ULONG cluster, next_cluster; +ULONG FAT_index; +ULONG FAT_value; +ULONG clusters; +ULONG total_clusters; +UINT sectors; +FX_MEDIA *media_ptr; + + +#ifdef FX_FAULT_TOLERANT_DATA +FX_INT_SAVE_AREA +#endif + +#ifndef FX_DONT_UPDATE_OPEN_FILES +ULONG open_count; +FX_FILE *search_ptr; +#endif + +#ifdef TX_ENABLE_EVENT_TRACE +TX_TRACE_BUFFER_ENTRY *trace_event; +ULONG trace_timestamp; +#endif + +#ifdef FX_ENABLE_FAULT_TOLERANT +UCHAR data_append = FX_FALSE; /* Whether or not new data would extend beyond the size of the original file. + Operations such as append or overwrite could cause the new file to exceed its + original size, resulting in data-appending operation. */ +ULONG copy_head_cluster = 0; /* The first cluster of the original chain that needs to be replaced. */ +ULONG copy_tail_cluster = 0; /* The last cluster of the original chain that needs to be replaced. */ +ULONG insertion_back; /* The insertion point (back) */ +ULONG insertion_front = 0; /* The insertion point (front) */ +ULONG replace_clusters = 0; /* The number of clusters to be replaced. */ +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + + /* First, determine if the file is still open. */ + if (file_ptr -> fx_file_id != FX_FILE_ID) + { + + /* Return the file not open error status. */ + return(FX_NOT_OPEN); + } + + /* Setup pointer to media structure. */ + media_ptr = file_ptr -> fx_file_media_ptr; + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_file_writes++; +#endif + + + if (file_ptr -> fx_file_current_file_offset + size > 0xFFFFFFFFULL) + { + + /* Return the no more space error, since the new file size would be larger than + the 32-bit field to represent it in the file's directory entry. */ + return(FX_NO_MORE_SPACE); + } + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Initialize next cluster of tail. */ + insertion_back = media_ptr -> fx_media_fat_last; +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_WRITE, file_ptr, buffer_ptr, size, 0, FX_TRACE_FILE_EVENTS, &trace_event, &trace_timestamp) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Check for write protect at the media level (set by driver). */ + if (media_ptr -> fx_media_driver_write_protect) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return write protect error. */ + return(FX_WRITE_PROTECT); + } + + /* Make sure this file is open for writing. */ + if (file_ptr -> fx_file_open_mode != FX_OPEN_FOR_WRITE) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the access error exception - a write was attempted from + a file opened for reading! */ + return(FX_ACCESS_ERROR); + } + +#ifdef FX_ENABLE_FAULT_TOLERANT + + /* Start transaction. */ + _fx_fault_tolerant_transaction_start(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Calculate the number of bytes per cluster. */ + bytes_per_cluster = ((ULONG)media_ptr -> fx_media_bytes_per_sector) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster); + + /* Check for invalid value. */ + if (bytes_per_cluster == 0) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Invalid media, return error. */ + return(FX_MEDIA_INVALID); + } + + /* Initialized first new cluster. */ + first_new_cluster = 0; + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Calculate clusters need to be replaced when fault tolerant is enabled. */ + if (media_ptr -> fx_media_fault_tolerant_enabled) + { + + if (file_ptr -> fx_file_current_file_offset == file_ptr -> fx_file_current_file_size) + { + + /* Appending data. No need to replace existing clusters. */ + replace_clusters = 0; + } + else if (file_ptr -> fx_file_current_file_offset == file_ptr -> fx_file_maximum_size_used) + { + + /* Similar to append data. No actual data after fx_file_maximum_size_used. + * No need to replace existing clusters. */ + replace_clusters = 0; + } + else if (((file_ptr -> fx_file_current_file_offset / media_ptr -> fx_media_bytes_per_sector) == + ((file_ptr -> fx_file_current_file_offset + size - 1) / media_ptr -> fx_media_bytes_per_sector)) && + ((file_ptr -> fx_file_current_file_offset + size) <= file_ptr -> fx_file_current_file_size)) + { + + /* Overwriting data in one sector. No need to replace existing clusters. */ + replace_clusters = 0; + } + else if ((file_ptr -> fx_file_current_available_size - file_ptr -> fx_file_current_file_offset) < size) + { + + /* Replace all clusters from current_cluster. */ + replace_clusters = (UINT)(file_ptr -> fx_file_total_clusters - file_ptr -> fx_file_current_relative_cluster); + copy_head_cluster = file_ptr -> fx_file_current_physical_cluster; + + data_append = FX_TRUE; + } + else + { + + /* Replace clusters from current_cluster to the end of written cluster. */ + replace_clusters = (UINT)((file_ptr -> fx_file_current_file_offset + size + bytes_per_cluster - 1) / bytes_per_cluster - + file_ptr -> fx_file_current_relative_cluster); + copy_head_cluster = file_ptr -> fx_file_current_physical_cluster; + data_append = FX_FALSE; + } + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Next, determine if there is enough room to write the specified number of + bytes to the clusters already allocated to this file. */ + if (((file_ptr -> fx_file_current_available_size - file_ptr -> fx_file_current_file_offset) < size) +#ifdef FX_ENABLE_FAULT_TOLERANT + || (replace_clusters) +#endif /* FX_ENABLE_FAULT_TOLERANT */ + ) + { + + /* The new request will not fit within the currently allocated clusters. */ + + /* Calculate the number of additional clusters that must be allocated for this + write request. */ +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Find the number of clusters that need to be replaced. */ + if (media_ptr -> fx_media_fault_tolerant_enabled) + { + + /* Mark the recovery phase. */ + media_ptr -> fx_media_fault_tolerant_state |= FX_FAULT_TOLERANT_STATE_SET_FAT_CHAIN; + + bytes_remaining = file_ptr -> fx_file_current_file_offset + size; + + if (replace_clusters > 0) + { + + + /* Find previous cluster of copy head cluster. */ + /* The previous cluster is not initialized. Find it. */ + if (copy_head_cluster != file_ptr -> fx_file_first_physical_cluster) + { + + /* The copy head cluster is not the first cluster of file. */ + /* Copy head is not the first cluster of file. */ + if (file_ptr -> fx_file_current_relative_cluster < file_ptr -> fx_file_consecutive_cluster) + { + + /* Clusters before current cluster are consecutive. */ + insertion_front = copy_head_cluster - 1; + bytes_remaining -= file_ptr -> fx_file_current_relative_cluster * bytes_per_cluster; + } + else + { + + /* Skip consecutive clusters first. */ + cluster = file_ptr -> fx_file_first_physical_cluster; + + /* Loop the link of FAT to find the previous cluster. */ + while ((cluster >= FX_FAT_ENTRY_START) && (cluster < media_ptr -> fx_media_fat_reserved)) + { + + /* Reduce remaining bytes. */ + bytes_remaining -= bytes_per_cluster; + + /* Read the current cluster entry from the FAT. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &FAT_value); + + /* Check the return value. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + if (FAT_value == copy_head_cluster) + { + break; + } + + /* Move to next cluster. */ + cluster = FAT_value; + } + + if ((cluster >= FX_FAT_ENTRY_START) && (cluster < media_ptr -> fx_media_fat_reserved)) + { + + /* Find the previous cluster. */ + insertion_front = cluster; + } + else + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(FX_NOT_FOUND); + } + } + } + + /* Find copy tail cluster. */ + if (bytes_remaining <= bytes_per_cluster) + { + + /* Only one cluster is modified. */ + copy_tail_cluster = copy_head_cluster; + } + else if (data_append == FX_FALSE) + { + + /* Search from copy head cluster. */ + cluster = copy_head_cluster; + FAT_value = FX_FAT_ENTRY_START; + + /* Loop the link of FAT to find the previous cluster. */ + while ((cluster >= FX_FAT_ENTRY_START) && (cluster < media_ptr -> fx_media_fat_reserved)) + { + + if (bytes_remaining <= bytes_per_cluster) + { + break; + } + + /* Reduce remaining bytes. */ + bytes_remaining -= bytes_per_cluster; + + /* Read the current cluster entry from the FAT. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &FAT_value); + + /* Check the return value. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* Move to next cluster. */ + cluster = FAT_value; + } + + /* Find the previous cluster. */ + copy_tail_cluster = FAT_value; + } + + /* Get the cluster next to copy tail. */ + if (data_append == FX_FALSE) + { + + /* Read FAT entry. */ + status = _fx_utility_FAT_entry_read(media_ptr, copy_tail_cluster, &insertion_back); + + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + } + else + { + insertion_back = media_ptr -> fx_media_fat_last; + } + } + else + { + insertion_back = media_ptr -> fx_media_fat_last; + } + } + + if (file_ptr -> fx_file_current_available_size - file_ptr -> fx_file_current_file_offset < size) + { +#endif /* FX_ENABLE_FAULT_TOLERANT */ + /* Calculate clusters that are needed for data append except ones overwritten. */ + clusters = (UINT)((size + (bytes_per_cluster - 1) - + (file_ptr -> fx_file_current_available_size - file_ptr -> fx_file_current_file_offset)) / + bytes_per_cluster); +#ifdef FX_ENABLE_FAULT_TOLERANT + } + else + { + clusters = 0; + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Determine if we have enough space left. */ +#ifdef FX_ENABLE_FAULT_TOLERANT + if (clusters + replace_clusters > media_ptr -> fx_media_available_clusters) +#else + if (clusters > media_ptr -> fx_media_available_clusters) +#endif /* FX_ENABLE_FAULT_TOLERANT */ + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Out of disk space. */ + return(FX_NO_MORE_SPACE); + } + + /* Update the file total cluster count. */ + file_ptr -> fx_file_total_clusters = file_ptr -> fx_file_total_clusters + clusters; + + /* Check for wrap-around when updating the available size. */ + if (file_ptr -> fx_file_current_available_size + (ULONG64)bytes_per_cluster * (ULONG64)clusters > 0xFFFFFFFFULL) + { + + /* 32-bit wrap around condition is present. Just set the available file size to all ones, which is + the maximum file size. */ + file_ptr -> fx_file_current_available_size = 0xFFFFFFFFULL; + } + else + { + + /* Normal condition, update the available size. */ + file_ptr -> fx_file_current_available_size = + file_ptr -> fx_file_current_available_size + (ULONG64)bytes_per_cluster * (ULONG64)clusters; + } + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Account for newly allocated clusters. */ + clusters += replace_clusters; +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Decrease the available clusters in the media control block. */ + media_ptr -> fx_media_available_clusters = media_ptr -> fx_media_available_clusters - clusters; + + + /* Search for the additional clusters we need. */ + total_clusters = media_ptr -> fx_media_total_clusters; + +#ifdef FX_ENABLE_FAULT_TOLERANT + if (replace_clusters > 0) + { + + /* Reset consecutive cluster. */ + file_ptr -> fx_file_consecutive_cluster = 1; + + last_cluster = insertion_front; + + } + else +#endif /* FX_ENABLE_FAULT_TOLERANT */ + { + last_cluster = file_ptr -> fx_file_last_physical_cluster; + } + + FAT_index = media_ptr -> fx_media_cluster_search_start; + + /* Loop to find the needed clusters. */ + while (clusters) + { + + /* Decrease the cluster count. */ + clusters--; + + /* Loop to find the first available cluster. */ + do + { + + /* Make sure we stop looking after one pass through the FAT table. */ + if (!total_clusters) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Something is wrong with the media - the desired clusters were + not found in the FAT table. */ + return(FX_NO_MORE_SPACE); + } + + /* Read FAT entry. */ + status = _fx_utility_FAT_entry_read(media_ptr, FAT_index, &FAT_value); + + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + /* Decrement the total cluster count. */ + total_clusters--; + + /* Determine if the FAT entry is free. */ + if (FAT_value == FX_FREE_CLUSTER) + { + + /* Move cluster search pointer forward. */ + media_ptr -> fx_media_cluster_search_start = FAT_index + 1; + + /* Determine if this needs to be wrapped. */ + if (media_ptr -> fx_media_cluster_search_start >= (media_ptr -> fx_media_total_clusters + FX_FAT_ENTRY_START)) + { + + /* Wrap the search to the beginning FAT entry. */ + media_ptr -> fx_media_cluster_search_start = FX_FAT_ENTRY_START; + } + + /* Break this loop. */ + break; + } + else + { + + /* FAT entry is not free... Advance the FAT index. */ + FAT_index++; + + /* Determine if we need to wrap the FAT index around. */ + if (FAT_index >= (media_ptr -> fx_media_total_clusters + FX_FAT_ENTRY_START)) + { + + /* Wrap the search to the beginning FAT entry. */ + FAT_index = FX_FAT_ENTRY_START; + } + } + } while (FX_TRUE); + + /* Determine if we have found the first new cluster yet. */ + if (first_new_cluster == 0) + { + + /* Remember the first new cluster. */ + first_new_cluster = FAT_index; + +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled) + { + + /* Set the undo log now. */ + if (copy_head_cluster == 0) + { + status = _fx_fault_tolerant_set_FAT_chain(media_ptr, FX_FALSE, file_ptr -> fx_file_current_physical_cluster, + first_new_cluster, media_ptr -> fx_media_fat_last, media_ptr -> fx_media_fat_last); + } + else + { + + status = _fx_fault_tolerant_set_FAT_chain(media_ptr, FX_FALSE, insertion_front, first_new_cluster, + copy_head_cluster, insertion_back); + } + + /* Check for good completion status. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + } + + /* Make a quick check to see if an empty, cluster-less file + is being written to for the first time. */ + if (last_cluster) + { + + /* Check for the file's cluster. We won't perform this link until the + entire FAT chain is built. */ + if (last_cluster != file_ptr -> fx_file_last_physical_cluster) + { + + /* Normal condition - link the last cluster with the new + found cluster. */ + status = _fx_utility_FAT_entry_write(media_ptr, last_cluster, FAT_index); + } + + /* Check for a bad FAT write status. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + /* Determine if we are adding a sector after a write filled the previously + allocated cluster exactly. */ + if ((file_ptr -> fx_file_current_relative_sector >= + (media_ptr -> fx_media_sectors_per_cluster - 1)) && + (file_ptr -> fx_file_current_logical_offset >= + media_ptr -> fx_media_bytes_per_sector)) + { + + /* Yes, we need to adjust all of the pertinent file parameters for + writing into this newly allocated cluster. */ + file_ptr -> fx_file_current_physical_cluster = FAT_index; + file_ptr -> fx_file_current_relative_cluster++; + file_ptr -> fx_file_current_relative_sector = 0; + file_ptr -> fx_file_current_logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG64)(FAT_index - FX_FAT_ENTRY_START)) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)); + file_ptr -> fx_file_current_logical_offset = 0; + } + } + else + { + + /* This is the first cluster allocated for the file. Just + remember it as being the first and setup the other file + pointers accordingly. */ + file_ptr -> fx_file_first_physical_cluster = FAT_index; + file_ptr -> fx_file_current_physical_cluster = FAT_index; + file_ptr -> fx_file_current_relative_cluster = 0; + file_ptr -> fx_file_current_logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG64)(FAT_index - FX_FAT_ENTRY_START)) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)); +#ifdef FX_ENABLE_FAULT_TOLERANT + if (file_ptr -> fx_file_last_physical_cluster == 0) +#endif /* FX_ENABLE_FAULT_TOLERANT */ + { + file_ptr -> fx_file_current_logical_offset = 0; + file_ptr -> fx_file_current_file_offset = 0; + } + + /* Also remember this as the first cluster in the directory + entry. */ + file_ptr -> fx_file_dir_entry.fx_dir_entry_cluster = FAT_index; + } + + /* Otherwise, remember the new FAT index as the last. */ + last_cluster = FAT_index; + + /* Move to the next FAT entry. */ + FAT_index = media_ptr -> fx_media_cluster_search_start; + } + +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled) + { + + /* Link the last cluster back to original FAT. */ + status = _fx_utility_FAT_entry_write(media_ptr, last_cluster, insertion_back); + } + else +#endif /* FX_ENABLE_FAULT_TOLERANT */ + { + + /* Place an end-of-file marker on the last cluster. */ + status = _fx_utility_FAT_entry_write(media_ptr, last_cluster, media_ptr -> fx_media_fat_last); + } + + /* Check for a bad FAT write status. */ + if (status != FX_SUCCESS) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + + /* Determine if the file already had clusters. */ + if (file_ptr -> fx_file_last_physical_cluster) + { + + /* Now, link the file's old last cluster to the first cluster of the new chain. */ +#ifdef FX_ENABLE_FAULT_TOLERANT + if (insertion_front) + { + status = _fx_utility_FAT_entry_write(media_ptr, insertion_front, first_new_cluster); + } + else if ((media_ptr -> fx_media_fault_tolerant_enabled == FX_FALSE) || + ((replace_clusters == 0) && (first_new_cluster))) + { + status = _fx_utility_FAT_entry_write(media_ptr, file_ptr -> fx_file_last_physical_cluster, first_new_cluster); + } +#else + status = _fx_utility_FAT_entry_write(media_ptr, file_ptr -> fx_file_last_physical_cluster, first_new_cluster); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Check for a bad FAT write status. */ + if (status != FX_SUCCESS) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + } + +#ifdef FX_FAULT_TOLERANT + + + /* Ensure the new FAT chain is properly written to the media. */ + + /* Flush the cached individual FAT entries */ + _fx_utility_FAT_flush(media_ptr); +#endif + +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled) + { + + /* Clear undo phase. */ + media_ptr -> fx_media_fault_tolerant_state &= (UCHAR)(~FX_FAULT_TOLERANT_STATE_SET_FAT_CHAIN & 0xff); + + /* Update the last cluster? */ + if (insertion_back == media_ptr -> fx_media_fat_last) + { + + /* Yes. Update the file control block with the last physical cluster. */ + file_ptr -> fx_file_last_physical_cluster = last_cluster; + } + } + else +#endif /* FX_ENABLE_FAULT_TOLERANT */ + { + + /* Update the file control block with the last physical cluster. */ + file_ptr -> fx_file_last_physical_cluster = last_cluster; + } + } + + /* Check for a need to increment to the next sector within a previously + allocated cluster. */ + if (file_ptr -> fx_file_current_logical_offset >= + media_ptr -> fx_media_bytes_per_sector) + { + + /* Update the sector specific file parameters to start at the + next logical sector. */ + file_ptr -> fx_file_current_logical_sector++; + file_ptr -> fx_file_current_relative_sector++; + file_ptr -> fx_file_current_logical_offset = 0; + } + + /* At this point there is enough room to perform the file write operation. */ + + /* Setup local buffer pointer. */ + source_ptr = (UCHAR *)buffer_ptr; + + /* Setup the remaining number of bytes to write. */ + bytes_remaining = size; + +#ifdef FX_ENABLE_FAULT_TOLERANT + if (replace_clusters > 0) + { + + /* Force update current cluster and sector. */ + file_ptr -> fx_file_current_physical_cluster = first_new_cluster; + file_ptr -> fx_file_current_logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG64)(first_new_cluster - FX_FAT_ENTRY_START)) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) + + file_ptr -> fx_file_current_relative_sector; + + /* Copy sectors in replaced cluster but not overwritten at the front. */ + for (i = 0; i < file_ptr -> fx_file_current_relative_sector; i++) + { + status = _fx_utility_logical_sector_read(media_ptr, + ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG)(copy_head_cluster - FX_FAT_ENTRY_START)) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) + i, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DATA_SECTOR); + + /* Check for good completion status. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* Write back the current logical sector. */ + status = _fx_utility_logical_sector_write(media_ptr, + ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG)(file_ptr -> fx_file_current_physical_cluster - FX_FAT_ENTRY_START)) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) + i, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DATA_SECTOR); + + /* Check for good completion status. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + } + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Loop to write all of the bytes. */ + while (bytes_remaining) + { + + /* Determine if a beginning or ending partial write is required. */ + if ((file_ptr -> fx_file_current_logical_offset) || + (bytes_remaining < media_ptr -> fx_media_bytes_per_sector)) + { + + /* A partial sector write is required. */ + + /* Read the current logical sector. */ +#ifdef FX_ENABLE_FAULT_TOLERANT + if (replace_clusters > 0) + { + if (file_ptr -> fx_file_current_physical_cluster == first_new_cluster) + { + + /* It's at beginning. */ + status = _fx_utility_logical_sector_read(media_ptr, + ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG)(copy_head_cluster - FX_FAT_ENTRY_START)) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) + + file_ptr -> fx_file_current_relative_sector, + media_ptr -> fx_media_memory_buffer, (ULONG)1, FX_DATA_SECTOR); + } + else if (data_append == FX_FALSE) + { + + /* It's at ending. */ + status = _fx_utility_logical_sector_read(media_ptr, + ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG)(copy_tail_cluster - FX_FAT_ENTRY_START)) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) + + file_ptr -> fx_file_current_relative_sector, + media_ptr -> fx_media_memory_buffer, (ULONG)1, FX_DATA_SECTOR); + } + else + { + + /* It's at ending. */ + status = _fx_utility_logical_sector_read(media_ptr, + file_ptr -> fx_file_current_logical_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DATA_SECTOR); + } + } + else +#endif /* FX_ENABLE_FAULT_TOLERANT */ + { + status = _fx_utility_logical_sector_read(media_ptr, + file_ptr -> fx_file_current_logical_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DATA_SECTOR); + } + + /* Check for good completion status. */ + if (status != FX_SUCCESS) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* Copy the appropriate number of bytes into the destination buffer. */ + copy_bytes = media_ptr -> fx_media_bytes_per_sector - + file_ptr -> fx_file_current_logical_offset; + + /* Check to see if only a portion of the sector needs to be + copied. */ + if (copy_bytes > bytes_remaining) + { + + /* Adjust the number of bytes to copy. */ + copy_bytes = (ULONG)bytes_remaining; + } + + /* Actually perform the memory copy. */ + _fx_utility_memory_copy(source_ptr, ((UCHAR *)media_ptr -> fx_media_memory_buffer) + /* Use case of memcpy is verified. */ + file_ptr -> fx_file_current_logical_offset, + copy_bytes); + + /* Write back the current logical sector. */ + status = _fx_utility_logical_sector_write(media_ptr, file_ptr -> fx_file_current_logical_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DATA_SECTOR); + + /* Check for good completion status. */ + if (status != FX_SUCCESS) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + + /* Increment the logical sector byte offset. */ + file_ptr -> fx_file_current_logical_offset = + file_ptr -> fx_file_current_logical_offset + copy_bytes; + + /* Adjust the remaining bytes to read. */ + bytes_remaining = bytes_remaining - copy_bytes; + + /* Adjust the pointer to the source buffer. */ + source_ptr = source_ptr + copy_bytes; + } + else + { + + /* Attempt to write multiple sectors directly to the media. */ + + /* Calculate the number of whole sectors to write. */ + sectors = (UINT)(bytes_remaining / media_ptr -> fx_media_bytes_per_sector); + + next_cluster = cluster = file_ptr -> fx_file_current_physical_cluster; + + for (i = (media_ptr -> fx_media_sectors_per_cluster - + file_ptr -> fx_file_current_relative_sector); i < sectors; i += media_ptr -> fx_media_sectors_per_cluster) + { + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &next_cluster); + + /* Determine if an error is present. */ + if ((status != FX_SUCCESS) || (next_cluster < FX_FAT_ENTRY_START) || + (next_cluster > media_ptr -> fx_media_fat_reserved)) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Send error message back to caller. */ + if (status != FX_SUCCESS) + { + return(status); + } + else + { + return(FX_FILE_CORRUPT); + } + } + + if (next_cluster != cluster + 1) + { + break; + } + else + { + cluster = next_cluster; + } + } + + if (i < sectors) + { + sectors = i; + } + + /* Perform the data write directly from the user's buffer of + the appropriate number of sectors. */ + status = _fx_utility_logical_sector_write(media_ptr, file_ptr -> fx_file_current_logical_sector, + source_ptr, (ULONG) sectors, FX_DATA_SECTOR); + + /* Check for good completion status. */ + if (status != FX_SUCCESS) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* Now adjust the various file pointers. */ + + /* Increment the current logical sector. Subtract one from + the sector count because we are going to use the logical + offset to do additional sector/cluster arithmetic below. */ + file_ptr -> fx_file_current_logical_sector = + file_ptr -> fx_file_current_logical_sector + + (sectors - 1); + + /* Move the relative cluster and sector as well. */ + file_ptr -> fx_file_current_relative_cluster = file_ptr -> fx_file_current_relative_cluster + + (file_ptr -> fx_file_current_relative_sector + (sectors - 1)) / + media_ptr -> fx_media_sectors_per_cluster; + + file_ptr -> fx_file_current_relative_sector = + (file_ptr -> fx_file_current_relative_sector + (sectors - 1)) % + media_ptr -> fx_media_sectors_per_cluster; + + /* Increment the logical sector byte offset. */ + file_ptr -> fx_file_current_logical_offset = + media_ptr -> fx_media_bytes_per_sector; + + file_ptr -> fx_file_current_physical_cluster = cluster; + + /* Adjust the remaining bytes. */ + bytes_remaining = bytes_remaining - + (((ULONG)media_ptr -> fx_media_bytes_per_sector) * sectors); + + /* Adjust the pointer to the source buffer. */ + source_ptr = source_ptr + + (((ULONG)media_ptr -> fx_media_bytes_per_sector) * sectors); + } + + /* At this point, we have either written a partial sector or have successfully + written one or more whole sectors. Determine if we are at the end of + the current logical sector. */ + if (file_ptr -> fx_file_current_logical_offset >= + media_ptr -> fx_media_bytes_per_sector) + { + + /* Determine if we are at the exact physical end of the file. */ + if ((bytes_remaining == 0) && + ((file_ptr -> fx_file_current_file_offset + size) >= + file_ptr -> fx_file_current_available_size)) + { + + /* Skip the following file parameter adjustments. The next write will + detect the logical offset out of the range of the sector and reset + all of the pertinent information. */ + break; + } + + /* We need to move to the next logical sector, but first + determine if the next logical sector is within the same + cluster. */ + + /* Increment the current relative sector in the cluster. */ + file_ptr -> fx_file_current_relative_sector++; + + /* Determine if this is in a new cluster. */ + if (file_ptr -> fx_file_current_relative_sector >= + media_ptr -> fx_media_sectors_per_cluster) + { + + /* Yes, we need to move to the next cluster. */ + + /* Read the FAT entry of the current cluster to find + the next cluster. */ + status = _fx_utility_FAT_entry_read(media_ptr, + file_ptr -> fx_file_current_physical_cluster, &next_cluster); + + /* Determine if an error is present. */ + if ((status != FX_SUCCESS) || (next_cluster < FX_FAT_ENTRY_START) || + (next_cluster > media_ptr -> fx_media_fat_reserved)) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Send error message back to caller. */ + if (status != FX_SUCCESS) + { + return(status); + } + else + { + return(FX_FILE_CORRUPT); + } + } + + /* Otherwise, we have a new cluster. Save it in the file + control block and calculate a new logical sector value. */ + file_ptr -> fx_file_current_physical_cluster = next_cluster; + file_ptr -> fx_file_current_relative_cluster++; + file_ptr -> fx_file_current_logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + + ((((ULONG64)next_cluster) - FX_FAT_ENTRY_START) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)); + file_ptr -> fx_file_current_relative_sector = 0; + } + else + { + + /* Still within the same cluster so just increment the + logical sector. */ + file_ptr -> fx_file_current_logical_sector++; + } + + /* In either case, we are now positioned at a new sector so + clear the logical sector offset. */ + file_ptr -> fx_file_current_logical_offset = 0; + } + } + +#ifdef FX_ENABLE_FAULT_TOLERANT + if (((replace_clusters > 0) && (data_append == FX_FALSE)) && + (file_ptr -> fx_file_current_logical_offset || file_ptr -> fx_file_current_relative_sector)) + { + + /* Copy sectors in replaced cluster but not overwritten at the end. */ + if (file_ptr -> fx_file_current_logical_offset == 0) + { + i = file_ptr -> fx_file_current_relative_sector; + } + else + { + i = file_ptr -> fx_file_current_relative_sector + 1; + } + for (; i < media_ptr -> fx_media_sectors_per_cluster; i++) + { + status = _fx_utility_logical_sector_read(media_ptr, + ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG)(copy_tail_cluster - FX_FAT_ENTRY_START)) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) + i, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DATA_SECTOR); + + /* Check for good completion status. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* Write back the current logical sector. */ + status = _fx_utility_logical_sector_write(media_ptr, + ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG)(file_ptr -> fx_file_current_physical_cluster - FX_FAT_ENTRY_START)) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) + i, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DATA_SECTOR); + + /* Check for good completion status. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + } + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Adjust the current file offset accordingly. */ + file_ptr -> fx_file_current_file_offset = + file_ptr -> fx_file_current_file_offset + size; + + /* Copy the new file size into the directory entry. */ + file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size = + file_ptr -> fx_file_current_file_size; + + /* Determine if this write was done past the previous file size. */ + if (file_ptr -> fx_file_current_file_offset > + file_ptr -> fx_file_current_file_size) + { + + /* Yes, we have written past the previous end of the file. Update + the file size. */ + file_ptr -> fx_file_current_file_size = file_ptr -> fx_file_current_file_offset; + +#ifndef FX_DONT_UPDATE_OPEN_FILES + + /* Search the opened files list to see if the same file is opened for reading. */ + open_count = media_ptr -> fx_media_opened_file_count; + search_ptr = media_ptr -> fx_media_opened_file_list; + while (open_count) + { + + /* Is this file the same file opened for reading? */ + if ((search_ptr != file_ptr) && + (search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector == + file_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector) && + (search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset == + file_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset)) + { + + /* Yes, the same file is opened for reading. */ + + /* Setup the new size. */ + search_ptr -> fx_file_current_file_size = file_ptr -> fx_file_current_file_offset; + + /* Setup the new directory entry. */ + search_ptr -> fx_file_dir_entry.fx_dir_entry_cluster = file_ptr -> fx_file_dir_entry.fx_dir_entry_cluster; + search_ptr -> fx_file_dir_entry.fx_dir_entry_file_size = file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size; + search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector = file_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector; + search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset = file_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset; + + /* Setup the last cluster. This really isn't used during reading, but it is nice to keep things + consistent. */ + search_ptr -> fx_file_last_physical_cluster = file_ptr -> fx_file_last_physical_cluster; + + /* Update the available clusters as well. */ + search_ptr -> fx_file_current_available_size = file_ptr -> fx_file_current_available_size; + + /* Determine if an empty file was previously opened. */ + if (search_ptr -> fx_file_total_clusters == 0) + { + + /* Setup initial parameters. */ + search_ptr -> fx_file_total_clusters = file_ptr -> fx_file_total_clusters; + search_ptr -> fx_file_current_physical_cluster = file_ptr -> fx_file_first_physical_cluster; + search_ptr -> fx_file_current_relative_cluster = 0; + search_ptr -> fx_file_current_logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG64)(file_ptr -> fx_file_first_physical_cluster - FX_FAT_ENTRY_START)) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)); + search_ptr -> fx_file_current_relative_sector = 0; + search_ptr -> fx_file_current_logical_offset = 0; + search_ptr -> fx_file_current_file_offset = 0; + } + } + + /* Adjust the pointer and decrement the search count. */ + search_ptr = search_ptr -> fx_file_opened_next; + open_count--; + } +#endif + } + + /* Finally, mark this file as modified. */ + file_ptr -> fx_file_modified = FX_TRUE; + +#ifdef FX_FAULT_TOLERANT_DATA + + /* Ensure that all file data is flushed out. */ + + /* Flush the internal logical sector cache. */ + _fx_utility_logical_sector_flush(media_ptr, 1, media_ptr -> fx_media_total_sectors, FX_FALSE); + + /* Lockout interrupts for time/date access. */ + FX_DISABLE_INTS + + /* Set the new time and date. */ + file_ptr -> fx_file_dir_entry.fx_dir_entry_time = _fx_system_time; + file_ptr -> fx_file_dir_entry.fx_dir_entry_date = _fx_system_date; + + /* Restore interrupts. */ + FX_RESTORE_INTS + +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled) + { + + /* Copy the new file size into the directory entry. */ + file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size = file_ptr -> fx_file_current_file_size; + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Write the directory entry to the media. */ + status = _fx_directory_entry_write(media_ptr, &(file_ptr -> fx_file_dir_entry)); + + /* Check for a good status. */ + if (status != FX_SUCCESS) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Error writing the directory. */ + return(status); + } +#endif + + /* Update the trace event with the bytes written. */ + FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_FILE_WRITE, 0, 0, 0, size) + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* End transaction. */ + status = _fx_fault_tolerant_transaction_end(media_ptr); + + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + /* Update maximum size used if necessary. */ + if (file_ptr -> fx_file_current_file_offset > file_ptr -> fx_file_maximum_size_used) + { + file_ptr -> fx_file_maximum_size_used = file_ptr -> fx_file_current_file_offset; + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Invoke file write callback. */ + if (file_ptr -> fx_file_write_notify) + { + file_ptr -> fx_file_write_notify(file_ptr); + } + + /* Release media protection. */ + FX_UNPROTECT + + /* Return a successful status to the caller. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_file_write_notify_set.c b/common/src/fx_file_write_notify_set.c new file mode 100644 index 0000000..abc4b49 --- /dev/null +++ b/common/src/fx_file_write_notify_set.c @@ -0,0 +1,83 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_file_write_notify_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the notify function called whenever file is */ +/* is written to. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* file_write_notify Notify function called when */ +/* file is written to */ +/* */ +/* OUTPUT */ +/* */ +/* FX_SUCCESS */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_file_write_notify_set(FX_FILE *file_ptr, VOID (*file_write_notify)(FX_FILE *file)) +{ + + /* Set the notify function. */ + file_ptr -> fx_file_write_notify = file_write_notify; + + /* Return successful status. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_media_abort.c b/common/src/fx_media_abort.c new file mode 100644 index 0000000..c6dc86f --- /dev/null +++ b/common/src/fx_media_abort.c @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_media.h" +#include "fx_file.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_media_abort PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function marks all open files for the specified media as */ +/* aborted and then removes the media control block from the open */ +/* media list and marks it as aborted as well. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* tx_mutex_delete Delete the mutex */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_media_abort(FX_MEDIA *media_ptr) +{ + +FX_INT_SAVE_AREA +ULONG open_count; +FX_FILE *file_ptr; + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_aborts++; +#endif + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_MEDIA_ABORT, media_ptr, 0, 0, 0, FX_TRACE_MEDIA_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Loop through the media's open files. */ + open_count = media_ptr -> fx_media_opened_file_count; + file_ptr = media_ptr -> fx_media_opened_file_list; + while (open_count) + { + + /* Mark the file as aborted. */ + file_ptr -> fx_file_id = FX_FILE_ABORTED_ID; + + /* Adjust the pointer and decrement the file opened count. */ + file_ptr = file_ptr -> fx_file_opened_next; + open_count--; + } + + /* Build the "abort" I/O driver request. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_ABORT; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_ABORT, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Call the specified I/O driver with the abort request. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Now remove this media from the open list. */ + + /* Lockout interrupts for media removal. */ + FX_DISABLE_INTS + + /* See if the media is the only one on the media opened list. */ + if (_fx_system_media_opened_count == ((ULONG) 1)) + { + + /* Only opened media, just set the opened list to NULL. */ + _fx_system_media_opened_ptr = FX_NULL; + } + else + { + + /* Otherwise, not the only opened media, link-up the neighbors. */ + (media_ptr -> fx_media_opened_next) -> fx_media_opened_previous = + media_ptr -> fx_media_opened_previous; + (media_ptr -> fx_media_opened_previous) -> fx_media_opened_next = + media_ptr -> fx_media_opened_next; + + /* See if we have to update the opened list head pointer. */ + if (_fx_system_media_opened_ptr == media_ptr) + { + + /* Yes, move the head pointer to the next opened media. */ + _fx_system_media_opened_ptr = media_ptr -> fx_media_opened_next; + } + } + + /* Decrement the opened media counter. */ + _fx_system_media_opened_count--; + + /* Finally, Indicate that this media is aborted. */ + media_ptr -> fx_media_id = FX_MEDIA_ABORTED_ID; + + /* Restore interrupt posture. */ + FX_RESTORE_INTS + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD + +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(& (media_ptr -> fx_media_protect)); +#endif +#endif + +#ifdef FX_DONT_CREATE_MUTEX + + /* Release media protection. */ + FX_UNPROTECT +#endif + + /* Return status to the caller. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_media_boot_info_extract.c b/common/src/fx_media_boot_info_extract.c new file mode 100644 index 0000000..b168676 --- /dev/null +++ b/common/src/fx_media_boot_info_extract.c @@ -0,0 +1,203 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_media.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_media_boot_info_extract PORTABLE C */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function extracts and validates the information from the boot */ +/* record found in the memory buffer. If the boot record is invalid, */ +/* an FX_MEDIA_INVALID status is returned to the caller. */ +/* */ +/* The FAT boot sector (512 bytes) that is operated on by this */ +/* function must look like the following: */ +/* */ +/* Byte Offset Meaning Size */ +/* */ +/* 0x000 Jump Instructions 3 */ +/* 0x003 OEM Name 8 */ +/* 0x00B *Bytes per Sector 2 */ +/* 0x00D *Sectors per Cluster 1 */ +/* 0x00E *Reserved Sectors 2 */ +/* 0x010 *Number of FATs 1 */ +/* 0x011 *Max Root Dir Entries 2 */ +/* 0x013 *Number of Sectors 2 */ +/* 0x015 Media Type 1 */ +/* 0x016 *Sectors per FAT 2 */ +/* 0x018 *Sectors per Track 2 */ +/* 0x01A *Number of Heads 2 */ +/* 0x01C *Hidden Sectors 4 */ +/* 0x020 *Huge Sectors 4 */ +/* 0x024 Drive Number 1 */ +/* 0x025 Reserved 1 */ +/* 0x026 Boot Signature 1 */ +/* 0x027 Volume ID 4 */ +/* 0x02B Volume Label 11 */ +/* 0x036 File System Type 8 */ +/* ... ... ... */ +/* 0x1FE **Signature (0x55aa) 2 */ +/* */ +/* * Denotes which elements of the boot record */ +/* FileX uses. */ +/* */ +/* **Denotes the element is checked by the I/O */ +/* driver. This eliminates the need for a minimum */ +/* 512-byte buffer for FileX. */ +/* */ +/* Note: All values above are in little endian format, i.e. the LSB is */ +/* in the lowest address. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_16_unsigned_read Read a UINT from buffer */ +/* _fx_utility_32_unsigned_read Read a ULONG from buffer */ +/* _fx_utility_64_unsigned_read Read a ULONG64 from memory */ +/* */ +/* CALLED BY */ +/* */ +/* _fx_media_open Media open function */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s), added */ +/* check for bimap cache size, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +UINT _fx_media_boot_info_extract(FX_MEDIA *media_ptr) +{ + +UCHAR *boot_sector; + + + /* Move the buffer pointer into a local copy. */ + boot_sector = media_ptr -> fx_media_driver_buffer; + + /* Extract the number of bytes per sector. */ + media_ptr -> fx_media_bytes_per_sector = _fx_utility_16_unsigned_read(&boot_sector[FX_BYTES_SECTOR]); + if (media_ptr -> fx_media_bytes_per_sector == 0) + return(FX_MEDIA_INVALID); + + + /* FAT12/16/32 volume. */ + /* Extract the number of sectors per track. */ + media_ptr -> fx_media_sectors_per_track = _fx_utility_16_unsigned_read(&boot_sector[FX_SECTORS_PER_TRK]); + + /* Extract the number of heads. */ + media_ptr -> fx_media_heads = _fx_utility_16_unsigned_read(&boot_sector[FX_HEADS]); + + /* Extract the total number of sectors. */ + media_ptr -> fx_media_total_sectors = _fx_utility_16_unsigned_read(&boot_sector[FX_SECTORS]); + if (media_ptr -> fx_media_total_sectors == 0) + { + media_ptr -> fx_media_total_sectors = _fx_utility_32_unsigned_read(&boot_sector[FX_HUGE_SECTORS]); + } + + if (media_ptr -> fx_media_total_sectors == 0) + { + return(FX_MEDIA_INVALID); + } + + /* Extract the number of reserved sectors before the first FAT. */ + media_ptr -> fx_media_reserved_sectors = _fx_utility_16_unsigned_read(&boot_sector[FX_RESERVED_SECTORS]); + if (media_ptr -> fx_media_reserved_sectors == 0) + { + return(FX_MEDIA_INVALID); + } + + /* Extract the number of sectors per cluster. */ + media_ptr -> fx_media_sectors_per_cluster = ((UINT)boot_sector[FX_SECTORS_CLUSTER] & 0xFF); + + /* There should always be at least one reserved sector, representing the boot record itself. */ + if (media_ptr -> fx_media_sectors_per_cluster == 0) + { + return(FX_MEDIA_INVALID); + } + + /* Extract the number of sectors per FAT. */ + media_ptr -> fx_media_sectors_per_FAT = _fx_utility_16_unsigned_read(&boot_sector[FX_SECTORS_PER_FAT]); + if (media_ptr -> fx_media_sectors_per_FAT == 0) + { + media_ptr -> fx_media_sectors_per_FAT = _fx_utility_32_unsigned_read(&boot_sector[FX_SECTORS_PER_FAT_32]); + } + + if (media_ptr -> fx_media_sectors_per_FAT == 0) + { + return(FX_MEDIA_INVALID); + } + + /* Extract the number of FATs. */ + media_ptr -> fx_media_number_of_FATs = ((UINT)boot_sector[FX_NUMBER_OF_FATS] & 0xFF); + if (media_ptr -> fx_media_number_of_FATs == 0) + { + return(FX_BOOT_ERROR); + } + + /* Extract the number of hidden sectors. */ +#ifdef FX_DRIVER_USE_64BIT_LBA + media_ptr -> fx_media_hidden_sectors = _fx_utility_64_unsigned_read(&boot_sector[FX_HIDDEN_SECTORS]); +#else + media_ptr -> fx_media_hidden_sectors = _fx_utility_32_unsigned_read(&boot_sector[FX_HIDDEN_SECTORS]); +#endif + /* Extract the number of root directory entries. */ + media_ptr -> fx_media_root_directory_entries = _fx_utility_16_unsigned_read(&boot_sector[FX_ROOT_DIR_ENTRIES]); + + /* Extract root directory starting cluster (32 bit only) and compute start sector */ + media_ptr -> fx_media_root_cluster_32 = _fx_utility_32_unsigned_read(&boot_sector[FX_ROOT_CLUSTER_32]); + + + /* Return a successful status. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_media_cache_invalidate.c b/common/src/fx_media_cache_invalidate.c new file mode 100644 index 0000000..27c556d --- /dev/null +++ b/common/src/fx_media_cache_invalidate.c @@ -0,0 +1,134 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_media_cache_invalidate PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function examines the logical cache, flushing written sectors */ +/* out to the media and invalidating all sectors in the logical cache. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_FAT_flush Flush cached FAT entries */ +/* _fx_utility_FAT_map_flush Flush primary FAT changes to */ +/* secondary FAT(s) */ +/* _fx_utility_logical_sector_flush Flush logical sector cache */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_media_cache_invalidate(FX_MEDIA *media_ptr) +{ + +UINT status; +UINT i; + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_flushes++; +#endif + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_MEDIA_CACHE_INVALIDATE, media_ptr, 0, 0, 0, FX_TRACE_MEDIA_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + /* Flush the cached individual FAT entries */ + _fx_utility_FAT_flush(media_ptr); + + /* Flush changed sector(s) in the primary FAT to secondary FATs. */ + _fx_utility_FAT_map_flush(media_ptr); + + /* Clear the FAT cache entry array. */ + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + + /* Clear entry in the FAT cache. */ + media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Clear the secondary FAT update map. */ + for (i = 0; i < FX_FAT_MAP_SIZE; i++) + { + + /* Clear bit map entry for secondary FAT update. */ + media_ptr -> fx_media_fat_secondary_update_map[i] = 0; + } + + /* Call the logical sector flush to invalidate the logical sector cache. */ + status = _fx_utility_logical_sector_flush(media_ptr, ((ULONG64) 1), (ULONG64) (media_ptr -> fx_media_total_sectors), FX_TRUE); + + /* Release media protection. */ + FX_UNPROTECT + + /* If we get here, return successful status to the caller. */ + return(status); +} + diff --git a/common/src/fx_media_check.c b/common/src/fx_media_check.c new file mode 100644 index 0000000..122fc13 --- /dev/null +++ b/common/src/fx_media_check.c @@ -0,0 +1,712 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_media.h" +#include "fx_utility.h" + + +/* Define parameters for FileX media check utility. */ + +#ifndef FX_MAX_DIRECTORY_NESTING +#define FX_MAX_DIRECTORY_NESTING 20 +#endif + + +/* Define data structures local to the FileX media check utility. */ + +typedef struct CURRENT_DIRECTORY_ENTRY_STRUCT +{ + ULONG current_directory_entry; + ULONG current_total_entries; + ULONG current_start_cluster; +} CURRENT_DIRECTORY_ENTRY; + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_media_check PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks the specified media for basic structural */ +/* errors, including cross linking, invalid FAT chains, and lost */ +/* clusters. The function also provides the capability to correct */ +/* errors. */ +/* */ +/* The algorithm is to follow all sub-directories immediately and */ +/* check their contents. The maximum depth of sub-directories is */ +/* determined by the constant FX_MAX_DIRECTORY_NESTING. */ +/* By default, this is set at 20. Basically, the FAT chain of every */ +/* file and sub-directory is traversed. If valid, clusters are marked */ +/* in a logical FAT bit map to signal they are already in-use. The */ +/* algorithm should detect any broken or cross linked FAT condition. */ +/* */ +/* As for memory usage, the scratch memory supplied to the media check */ +/* function is used to hold several directory entries, a data */ +/* structure to "stack" the current directory entry position before */ +/* diving into the sub-directory, and finally the logical FAT bit map. */ +/* The basic data structures take from 512-1024 bytes and the logical */ +/* FAT bit map requires as many bits as there are clusters in your */ +/* device. For example, a device with 8000 clusters would require */ +/* 1000 bytes to represent. */ +/* */ +/* On the performance end of things, the traversal is reasonably fast */ +/* and is basically linear with the number of files and their sizes. */ +/* The lost cluster checking at the end of media check is a bit more */ +/* performance comprehensive. It basically checks that each unused */ +/* cluster is actually marked as free. You might decide to bypass this */ +/* step if no other errors are found... or there might be ways to */ +/* optimize the search by reading whole FAT sectors. You probably just */ +/* need to see how it behaves in your system. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to a previously */ +/* opened media */ +/* scratch_memory_ptr Pointer to memory area for */ +/* media check to use (as */ +/* mentioned above) */ +/* scratch_memory_size Size of the scratch memory */ +/* error_correction_option Specifies which - if any - */ +/* errors are corrected by */ +/* the media check function. */ +/* Setting the following bit */ +/* causes that error to be */ +/* corrected: */ +/* */ +/* 0x01 -> Fix FAT Chain Errors*/ +/* 0x02 -> Fix Directory Entry */ +/* Errors */ +/* 0x04 -> Fix Lost Clusters */ +/* */ +/* errors_detected Specifies the destination */ +/* ULONG to place the error */ +/* report from media check. */ +/* This has a similar bit map */ +/* as before: */ +/* */ +/* 0x01 -> FAT Chain Error(s) */ +/* 0x02 -> Directory Entry */ +/* Error(s) */ +/* 0x04 -> Lost Cluster(s) */ +/* */ +/* OUTPUT */ +/* */ +/* FX_SUCCESS Media check performed its */ +/* operation successfully. */ +/* This does not mean that */ +/* there were no errors. The */ +/* errors_detected variable */ +/* needs to be examined. */ +/* FX_MEDIA_NOT_OPEN The media was not open. */ +/* FX_NOT_ENOUGH_MEMORY The scratch memory was not */ +/* large enough or the nesting */ +/* depth was greater than the */ +/* maximum specified. */ +/* FX_IO_ERROR I/O Error reading/writing to */ +/* the media. */ +/* FX_ERROR_NOT_FIXED Fundamental problem with */ +/* media that couldn't be fixed*/ +/* */ +/* CALLS */ +/* */ +/* _fx_media_cache_invalidate Invalidate the cache */ +/* _fx_media_check_FAT_chain_check Walk the supplied FAT chain */ +/* _fx_media_check_lost_cluster_check Check for lost clusters */ +/* _fx_directory_entry_read Directory entry read */ +/* _fx_directory_entry_write Directory entry write */ +/* _fx_media_flush Flush changes to the media */ +/* _fx_utility_FAT_entry_write Write value to FAT entry */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_media_check(FX_MEDIA *media_ptr, UCHAR *scratch_memory_ptr, ULONG scratch_memory_size, ULONG error_correction_option, ULONG *errors_detected) +{ + +CURRENT_DIRECTORY_ENTRY *current_directory; +ULONG total_entries, last_cluster, valid_clusters; +ULONG bytes_per_cluster, i, current_errors; +UINT status, long_name_size; +UINT current_directory_index; +UCHAR *logical_fat, *working_ptr; +ALIGN_TYPE address_mask; +FX_DIR_ENTRY *temp_dir_ptr, *source_dir_ptr, *dir_entry_ptr; + +#ifdef TX_ENABLE_EVENT_TRACE +TX_TRACE_BUFFER_ENTRY *trace_event; +ULONG trace_timestamp; +#endif + + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_MEDIA_CHECK, media_ptr, scratch_memory_ptr, scratch_memory_size, 0, FX_TRACE_MEDIA_EVENTS, &trace_event, &trace_timestamp) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Determine if there are any opened files. */ + if (media_ptr -> fx_media_opened_file_count) + { + + /* There are opened files... this is an error! */ + + /* Release protection. */ + FX_UNPROTECT + + /* Return an error. */ + return(FX_ACCESS_ERROR); + } + + /* Invalidate the cache. */ + _fx_media_cache_invalidate(media_ptr); + + /* Initialize the reported error flag. */ + *errors_detected = 0; + + /* Calculate the long name size, rounded up to something that is evenly divisible by 4. */ + long_name_size = (((FX_MAX_LONG_NAME_LEN + 3) >> 2) << 2); + + /* Calculate the number of bytes per cluster. */ + bytes_per_cluster = media_ptr -> fx_media_sectors_per_cluster * media_ptr -> fx_media_bytes_per_sector; + + /* Setup address mask. */ + address_mask = sizeof(ULONG) - 1; + address_mask = ~address_mask; + + /* Setup working pointer. */ + working_ptr = scratch_memory_ptr + (sizeof(ULONG) - 1); + working_ptr = (UCHAR *)(((ALIGN_TYPE)working_ptr) & address_mask); + + /* Memory is set aside for two FX_DIR_ENTRY structures */ + dir_entry_ptr = (FX_DIR_ENTRY *)working_ptr; + + /* Adjust the scratch memory pointer forward. */ + working_ptr = &working_ptr[sizeof(FX_DIR_ENTRY)]; + + /* Setup the name buffer for the first directory entry. */ + dir_entry_ptr -> fx_dir_entry_name = (CHAR *)working_ptr; + + /* Adjust the scratch memory pointer forward. */ + working_ptr = working_ptr + long_name_size + (sizeof(ULONG) - 1); + working_ptr = (UCHAR *)(((ALIGN_TYPE)working_ptr) & address_mask); + + /* Setup the source directory entry. */ + source_dir_ptr = (FX_DIR_ENTRY *)working_ptr; + + /* Adjust the scratch memory pointer forward. */ + working_ptr = &working_ptr[sizeof(FX_DIR_ENTRY)]; + + /* Setup the name buffer for the source directory entry. */ + source_dir_ptr -> fx_dir_entry_name = (CHAR *)working_ptr; + + /* Adjust the scratch memory pointer forward. */ + working_ptr = working_ptr + long_name_size + (sizeof(ULONG) - 1); + working_ptr = (UCHAR *)(((ALIGN_TYPE)working_ptr) & address_mask); + + /* Setup the current directory stack memory. */ + current_directory = (CURRENT_DIRECTORY_ENTRY *)working_ptr; + + /* Allocate space for the size of the directory entry stack. This basically + defines the maximum level of sub-directories supported. */ + working_ptr = &working_ptr[(FX_MAX_DIRECTORY_NESTING * sizeof(CURRENT_DIRECTORY_ENTRY))]; + + /* Setup the initial current directory entry. */ + current_directory_index = 0; + + /* Adjust the size to account for the header information. */ + if (scratch_memory_size < (ULONG)((working_ptr - scratch_memory_ptr))) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the not enough memory error. */ + return(FX_NOT_ENOUGH_MEMORY); + } + + /* Adjust the scratch memory size. */ + scratch_memory_size = scratch_memory_size - (ULONG)(working_ptr - scratch_memory_ptr); + + /* Memory is set aside for logical FAT - one bit per cluster */ + logical_fat = (UCHAR *)working_ptr; + + /* Determine if there is enough memory. */ + if (scratch_memory_size < ((media_ptr -> fx_media_total_clusters >> 3) + 1)) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the not enough memory error. */ + return(FX_NOT_ENOUGH_MEMORY); + } + + /* Initialize the logical FAT table. */ + for (i = 0; i < ((media_ptr -> fx_media_total_clusters >> 3) + 1); i++) + { + /* Clear the logical FAT entry, which actually represents eight clusters. */ + logical_fat[i] = 0; + } + + +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled) + { + ULONG cluster, cluster_number; + + /* Mark the cluster used by fault tolerant as valid. */ + for (cluster = media_ptr -> fx_media_fault_tolerant_start_cluster; + cluster < media_ptr -> fx_media_fault_tolerant_start_cluster + media_ptr -> fx_media_fault_tolerant_clusters; + cluster++) + { + + cluster_number = cluster; + + + logical_fat[cluster_number >> 3] = (UCHAR)(logical_fat[cluster_number >> 3] | (1 << (cluster_number & 7))); + } + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* If FAT32 is present, determine if the root directory is coherent. */ + if (media_ptr -> fx_media_32_bit_FAT) + { + + /* A 32-bit FAT is present. We need to walk the clusters of the root directory to determine if + it is intact. */ + current_errors = _fx_media_check_FAT_chain_check(media_ptr, media_ptr -> fx_media_root_cluster_32, + &last_cluster, &valid_clusters, logical_fat); + + /* Make them part of the errors reported to the caller. */ + *errors_detected = *errors_detected | current_errors; + + /* Update the trace event with the errors detected. */ + FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_MEDIA_CHECK, 0, 0, 0, *errors_detected) + + /* Determine if the I/O error is set. */ + if (current_errors & FX_IO_ERROR) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* File I/O Error. */ + return(FX_IO_ERROR); + } + + /* Check the status. */ + if (*errors_detected) + { + + /* Determine if we can fix the FAT32 root directory error. */ + if ((valid_clusters) && (error_correction_option & FX_FAT_CHAIN_ERROR)) + { + + /* Make the chain end at the last cluster. */ + status = _fx_utility_FAT_entry_write(media_ptr, last_cluster, media_ptr -> fx_media_fat_last); + + /* Determine if the write was successful. */ + if (status) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + /* Adjust the total entries in the root directory. */ + media_ptr -> fx_media_root_directory_entries = (valid_clusters * bytes_per_cluster) / FX_DIR_ENTRY_SIZE; + } + else + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return an error. */ + return(FX_ERROR_NOT_FIXED); + } + } + } + + /* Pickup total entries in the root directory. */ + total_entries = media_ptr -> fx_media_root_directory_entries; + + /* Set temp directory pointer to NULL. */ + temp_dir_ptr = FX_NULL; + + /* Put the root directory information in the entry stack */ + current_directory[current_directory_index].current_total_entries = total_entries; + current_directory[current_directory_index].current_start_cluster = media_ptr -> fx_media_fat_last; + current_directory[current_directory_index].current_directory_entry = 0; + + /* Now we shall do the checking in depth first manner. */ + do + { + + /* Pickup the directory index. */ + i = current_directory[current_directory_index].current_directory_entry; + + /* Loop to process remaining directory entries. */ + while (i < current_directory[current_directory_index].current_total_entries) + { + + /* Read a directory entry. */ + status = _fx_directory_entry_read(media_ptr, temp_dir_ptr, &i, dir_entry_ptr); + + /* Determine if the read was successful. */ + if (status) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + /* Check for the last entry. */ + if (dir_entry_ptr -> fx_dir_entry_name[0] == (CHAR)FX_DIR_ENTRY_DONE) + { + + /* Last entry in this directory - no need to check further. */ + break; + } + + /* Is the entry free? */ + if ((dir_entry_ptr -> fx_dir_entry_name[0] == (CHAR)FX_DIR_ENTRY_FREE) && (dir_entry_ptr -> fx_dir_entry_short_name[0] == 0)) + { + + /* A deleted entry */ + i++; + continue; + } + + + /* Look for any cross links or errors in the FAT chain of current directory entry. */ + current_errors = _fx_media_check_FAT_chain_check(media_ptr, dir_entry_ptr -> fx_dir_entry_cluster, + &last_cluster, &valid_clusters, logical_fat); + + /* Make them part of the errors reported to the caller. */ + *errors_detected = *errors_detected | current_errors; + + /* Update the trace event with the errors detected. */ + FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_MEDIA_CHECK, 0, 0, 0, *errors_detected) + + /* Determine if the I/O error is set. */ + if (current_errors & FX_IO_ERROR) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* File I/O Error. */ + return(FX_IO_ERROR); + } + + /* Check for errors. */ + if (*errors_detected) + { + + /* Determine if we can fix the FAT chain. */ + if (error_correction_option & FX_FAT_CHAIN_ERROR) + { + + /* Determine if there is a valid cluster to write the EOF to. */ + if (valid_clusters) + { + + /* Write EOF in the last FAT entry. */ + status = _fx_utility_FAT_entry_write(media_ptr, last_cluster, media_ptr -> fx_media_fat_last); + + /* Determine if the write was successful. */ + if (status) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + } + } + } + + /* Determine if we need to update the size of the directory entry. */ + if (dir_entry_ptr -> fx_dir_entry_file_size > (valid_clusters * bytes_per_cluster)) + { + + /* Yes, update the directory entry's size. */ + dir_entry_ptr -> fx_dir_entry_file_size = valid_clusters * bytes_per_cluster; + + /* Determine if the new file size is zero. */ + if (dir_entry_ptr -> fx_dir_entry_file_size == 0) + { + + /* Consider this a directory error. */ + *errors_detected = *errors_detected | FX_DIRECTORY_ERROR; + + /* Update the trace event with the errors detected. */ + FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_MEDIA_CHECK, 0, 0, 0, *errors_detected) + + /* Clear the starting cluster number of this directory entry. */ + dir_entry_ptr -> fx_dir_entry_cluster = 0; + + /* If directory fixing is required, delete this directory entry as well. */ + if (error_correction_option & FX_DIRECTORY_ERROR) + { + + /* Mark the entry as deleted. */ + dir_entry_ptr -> fx_dir_entry_name[0] = (CHAR)FX_DIR_ENTRY_FREE; + dir_entry_ptr -> fx_dir_entry_short_name[0] = (CHAR)FX_DIR_ENTRY_FREE; + } + } + + /* Only update the directory if the FAT chain was actually updated. */ + if (error_correction_option & FX_FAT_CHAIN_ERROR) + { + + /* Update the directory entry. */ + status = _fx_directory_entry_write(media_ptr, dir_entry_ptr); + + /* Determine if the write was successful. */ + if (status) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + } + } + + /* Determine if the entry is a sub-directory. */ + if ((dir_entry_ptr -> fx_dir_entry_attributes & FX_DIRECTORY) + && (valid_clusters == 0)) + { + + /* Consider this a directory error. */ + *errors_detected = *errors_detected | FX_DIRECTORY_ERROR; + + /* Update the trace event with the errors detected. */ + FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_MEDIA_CHECK, 0, 0, 0, *errors_detected) + + /* Determine if we can fix the error. */ + if (error_correction_option & FX_DIRECTORY_ERROR) + { + + /* Yes, make the directory entry free. */ + dir_entry_ptr -> fx_dir_entry_name[0] = (CHAR)FX_DIR_ENTRY_FREE; + dir_entry_ptr -> fx_dir_entry_short_name[0] = (CHAR)FX_DIR_ENTRY_FREE; + + /* Delete the sub-directory entry. */ + status = _fx_directory_entry_write(media_ptr, dir_entry_ptr); + + /* Determine if the write was successful. */ + if (status) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + /* Move to next entry. */ + i++; + continue; + } + } + + /* Determine if the entry is a directory. */ + if (dir_entry_ptr -> fx_dir_entry_attributes & FX_DIRECTORY) + { + + /* Current entry is a directory. The algorithm is designed to follow all + sub-directories immediately, i.e., a depth first search. */ + + /* First, save the next entry position. */ + current_directory[current_directory_index].current_directory_entry = i + 1; + + /* Push the current directory entry on the stack. */ + current_directory_index++; + + /* Check for current directory stack overflow. */ + if (current_directory_index >= FX_MAX_DIRECTORY_NESTING) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Current directory stack overflow. Return error. */ + return(FX_NOT_ENOUGH_MEMORY); + } + + /* Otherwise, setup the new directory entry. */ + current_directory[current_directory_index].current_total_entries = (valid_clusters * bytes_per_cluster) / FX_DIR_ENTRY_SIZE; + current_directory[current_directory_index].current_start_cluster = dir_entry_ptr -> fx_dir_entry_cluster; + current_directory[current_directory_index].current_directory_entry = 2; + + /* Setup new source directory. */ + source_dir_ptr -> fx_dir_entry_cluster = dir_entry_ptr -> fx_dir_entry_cluster; + source_dir_ptr -> fx_dir_entry_file_size = current_directory[current_directory_index].current_total_entries; + source_dir_ptr -> fx_dir_entry_last_search_cluster = 0; + temp_dir_ptr = source_dir_ptr; + + /* Skip the first two entries of sub-directories. */ + i = 2; + + } + else + { + + /* Regular file entry. */ + + /* Check for an invalid file size. */ + if (((valid_clusters * bytes_per_cluster) - dir_entry_ptr -> fx_dir_entry_file_size) > bytes_per_cluster) + { + + /* There are more clusters allocated than needed for the file's size. Indicate that this error + is present. */ + *errors_detected = *errors_detected | FX_FILE_SIZE_ERROR; + + /* Update the trace event with the errors detected. */ + FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_MEDIA_CHECK, 0, 0, 0, *errors_detected) + + /* For now, don't shorten the cluster chain. */ + } + + /* Look into the next entry in the current directory. */ + i++; + } + } + + /* Once we get here, we have exhausted the current directory and need to return to the previous + directory. */ + + /* Check for being at the root directory. */ + if (current_directory_index == 0) + { + + /* Yes, we have now exhausted searching the root directory so we are done! */ + break; + } + + /* Backup to the place we left off in the previous directory. */ + current_directory_index--; + + /* Determine if we are now back at the root directory. */ + if (current_directory[current_directory_index].current_start_cluster == media_ptr -> fx_media_fat_last) + { + + /* The search directory should be NULL since it is the root directory. */ + temp_dir_ptr = FX_NULL; + } + else + { + + /* Otherwise, we are returning to a sub-directory. Setup the search directory + appropriately. */ + source_dir_ptr -> fx_dir_entry_cluster = current_directory[current_directory_index].current_start_cluster; + source_dir_ptr -> fx_dir_entry_file_size = current_directory[current_directory_index].current_total_entries; + source_dir_ptr -> fx_dir_entry_last_search_cluster = 0; + temp_dir_ptr = source_dir_ptr; + } + } while (1); + + + /* At this point, all the files and sub-directories have been examined. We now need to check for + lost clusters in the logical FAT. A lost cluster is basically anything that is not reported in + the logical FAT that has a non-zero value in the real FAT. */ + current_errors = _fx_media_check_lost_cluster_check(media_ptr, logical_fat, media_ptr -> fx_media_total_clusters, error_correction_option); + + /* Incorporate the error returned by the lost FAT check. */ + *errors_detected = *errors_detected | current_errors; + + /* Update the trace event with the errors detected. */ + FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_MEDIA_CHECK, 0, 0, 0, *errors_detected) + + /* Determine if the I/O error is set. */ + if (current_errors & FX_IO_ERROR) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* File I/O Error. */ + return(FX_IO_ERROR); + } + + /* Determine if there was any error and update was selected. */ + if ((*errors_detected) && (error_correction_option)) + { + + /* Flush any unwritten items to the media. */ + _fx_media_flush(media_ptr); + } + + /* Release media protection. */ + FX_UNPROTECT + + /* At this point, we have completed the diagnostic of the media, return success! */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_media_check_FAT_chain_check.c b/common/src/fx_media_check_FAT_chain_check.c new file mode 100644 index 0000000..e9aeb43 --- /dev/null +++ b/common/src/fx_media_check_FAT_chain_check.c @@ -0,0 +1,162 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_media_check_FAT_chain_check PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function walks the supplied FAT chain looking for cross links */ +/* (FAT entries already used in another FAT chain) and abnormal FAT */ +/* entries and invalid chain termination. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to a previously */ +/* opened media */ +/* starting_cluster Starting cluster of FAT chain */ +/* last_valid_cluster Last valid cluster of chain */ +/* total_valid_clusters Total number of valid clusters*/ +/* logical_fat Pointer to the logical FAT */ +/* bit map */ +/* */ +/* OUTPUT */ +/* */ +/* error Error code */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_FAT_entry_read Read a FAT entry */ +/* */ +/* CALLED BY */ +/* */ +/* _fx_check_media Check media function */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +ULONG _fx_media_check_FAT_chain_check(FX_MEDIA *media_ptr, ULONG starting_cluster, ULONG *last_valid_cluster, ULONG *total_valid_clusters, UCHAR *logical_fat) +{ + +ULONG prev_cluster, cluster, next_clust = 0; +ULONG cluster_number; +ULONG count, error; +UINT status; + + + /* Initialize the error code. */ + error = 0; + + /* Setup at the first cluster. */ + cluster = starting_cluster; + + /* Initialize the previous cluster. */ + prev_cluster = 0; + + /* Initialize the count to 0. */ + count = 0; + + /* Loop to walk through the FAT chain. */ + while ((cluster >= (ULONG)FX_FAT_ENTRY_START) && + (cluster < (FX_FAT_ENTRY_START + media_ptr -> fx_media_total_clusters))) + { + + cluster_number = cluster; + + + /* Determine if this cluster is already in the logical FAT bit map. */ + if (logical_fat[cluster_number >> 3] & (1 << (cluster_number & 7))) + { + + /* Yes, the cluster is already being used by another file or + sub-directory. */ + error = FX_FAT_CHAIN_ERROR; + break; + } + + /* Now read the contents of the cluster. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &next_clust); + + /* Check the return status. */ + if (status != FX_SUCCESS) + { + + /* Yes, the cluster is already being used by another file or + sub-directory. */ + error = FX_IO_ERROR; + break; + } + + /* Determine if the link is circular or the count is greater than the + total clusters. */ + if ((cluster == next_clust) || + (next_clust < (ULONG)FX_FAT_ENTRY_START) || + ((next_clust >= (FX_FAT_ENTRY_START + media_ptr -> fx_media_total_clusters)) && + (next_clust != media_ptr -> fx_media_fat_last))) + { + + error = FX_FAT_CHAIN_ERROR; + break; + } + + /* Everything is good with the chain at this point. Mark it as valid. */ + logical_fat[cluster_number >> 3] = (UCHAR)(logical_fat[cluster_number >> 3] | (1 << (cluster_number & 7))); + + /* Move the cluster variable forward. */ + prev_cluster = cluster; + cluster = next_clust; + + /* Increment the number of valid clusters. */ + count++; + } + + /* Return the last valid cluster and the total valid cluster count. */ + *last_valid_cluster = prev_cluster; + *total_valid_clusters = count; + + /* Return error code. */ + return(error); +} + diff --git a/common/src/fx_media_check_lost_cluster_check.c b/common/src/fx_media_check_lost_cluster_check.c new file mode 100644 index 0000000..d81788e --- /dev/null +++ b/common/src/fx_media_check_lost_cluster_check.c @@ -0,0 +1,169 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_media_check_lost_cluster_check PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function examines all clusters to see if there are any unused */ +/* clusters that are also unavailable. If specified, this routine will */ +/* also mark the cluster as available. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to a previously */ +/* opened media */ +/* logical_fat Pointer to the logical FAT */ +/* bit map */ +/* total_clusters Total number of clusters */ +/* error_correction_option Option for correcting lost */ +/* cluster errors */ +/* */ +/* OUTPUT */ +/* */ +/* error Error code */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_FAT_entry_read Read a FAT entry */ +/* _fx_utility_FAT_entry_write Write a FAT entry */ +/* */ +/* CALLED BY */ +/* */ +/* _fx_check_media Check media function */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +ULONG _fx_media_check_lost_cluster_check(FX_MEDIA *media_ptr, UCHAR *logical_fat, ULONG total_clusters, ULONG error_correction_option) +{ + +ULONG cluster, next_cluster = 0; +ULONG fat_last; +ULONG error; +UINT status; + + + /* Calculate the FAT reserved and last sector values. */ + if (media_ptr -> fx_media_32_bit_FAT) + { + fat_last = FX_LAST_CLUSTER_1_32; + } + else + { + fat_last = FX_LAST_CLUSTER_1; + } + + /* Initialize the error. */ + error = 0; + + /* Loop through all the clusters to see if any clusters NOT in the logical sector FAT have + a non zero value. */ + for (cluster = FX_FAT_ENTRY_START; cluster < total_clusters; cluster++) + { + + /* Determine if this cluster is in the logical FAT. */ + if (logical_fat[cluster >> 3] & (1 << (cluster & 7))) + { + + /* Yes, the cluster is in use by a file or sub-directory. Just continue the loop. */ + continue; + } + + /* Otherwise, the cluster is not in use. */ + + /* Read the contents of what should be a free cluster. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &next_cluster); + + /* Check for a good status. */ + if (status) + { + + /* Set the error code. */ + error = error | FX_IO_ERROR; + + /* Return error code. */ + return(error); + } + + /* Determine if the contents of the cluster is valid. */ + if (((next_cluster > (ULONG)FX_FREE_CLUSTER) && (next_cluster < media_ptr -> fx_media_fat_reserved)) || + (next_cluster >= fat_last)) + { + + /* Lost cluster is present. */ + + /* Set the error code status. */ + error = FX_LOST_CLUSTER_ERROR; + + /* Determine if the lost cluster should be recovered. */ + if (error_correction_option & FX_LOST_CLUSTER_ERROR) + { + + /* Make the cluster available again. */ + status = _fx_utility_FAT_entry_write(media_ptr, cluster, FX_FREE_CLUSTER); + + /* Check for a good status. */ + if (status) + { + + /* Increment the available clusters. */ + media_ptr -> fx_media_available_clusters++; + + /* Set the error code. */ + error = error | FX_IO_ERROR; + + /* Return error code. */ + return(error); + } + } + } + } + + /* Return error code. */ + return(error); +} + diff --git a/common/src/fx_media_close.c b/common/src/fx_media_close.c new file mode 100644 index 0000000..62ca823 --- /dev/null +++ b/common/src/fx_media_close.c @@ -0,0 +1,425 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_media.h" +#include "fx_file.h" +#include "fx_directory.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_media_close PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function examines the list of open files for this media and */ +/* closes each file. If a file has been written to, the file's */ +/* directory information is also written out to the media. After */ +/* the files have been closed, the internal logical sector is */ +/* flushed and a flush command is sent to the attached driver. */ +/* Finally, this media control block is removed from the list of */ +/* opened media control blocks and is marked as closed. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_entry_write Write the directory entry */ +/* _fx_media_abort Abort the media on error */ +/* _fx_utility_FAT_flush Flush cached FAT entries */ +/* _fx_utility_FAT_map_flush Flush primary FAT changes to */ +/* secondary FAT(s) */ +/* _fx_utility_logical_sector_flush Flush logical sector cache */ +/* _fx_utility_16_unsigned_read Read a 16-bit value */ +/* _fx_utility_32_unsigned_read Read a 32-bit value */ +/* _fx_utility_32_unsigned_write Write a 32-bit value */ +/* tx_mutex_delete Delete protection mutex */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), and */ +/* added conditional to */ +/* disable file close */ +/* and cache, */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_media_close(FX_MEDIA *media_ptr) +{ + +FX_INT_SAVE_AREA + +#ifndef FX_DISABLE_FILE_CLOSE +ULONG open_count; +FX_FILE *file_ptr; +#endif /* FX_DISABLE_FILE_CLOSE */ +UINT status; + + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_MEDIA_CLOSE, media_ptr, 0, 0, 0, FX_TRACE_MEDIA_EVENTS, 0, 0) + + /* If trace is enabled, unregister this object. */ + FX_TRACE_OBJECT_UNREGISTER(media_ptr) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + +#ifndef FX_DISABLE_FILE_CLOSE + /* Loop through the media's open files. */ + open_count = media_ptr -> fx_media_opened_file_count; + file_ptr = media_ptr -> fx_media_opened_file_list; + while (open_count) + { + + /* Look at each opened file to see if the same file is opened + for writing and has been written to. */ + if ((file_ptr -> fx_file_open_mode == FX_OPEN_FOR_WRITE) && + (file_ptr -> fx_file_modified)) + { + + /* Lockout interrupts for time/date access. */ + FX_DISABLE_INTS + + /* Set the new time and date. */ + file_ptr -> fx_file_dir_entry.fx_dir_entry_time = _fx_system_time; + file_ptr -> fx_file_dir_entry.fx_dir_entry_date = _fx_system_date; + + /* Restore interrupt posture. */ + FX_RESTORE_INTS + + /* Copy the new file size into the directory entry. */ + file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size = + file_ptr -> fx_file_current_file_size; + + /* Write the directory entry to the media. */ + status = _fx_directory_entry_write(media_ptr, &(file_ptr -> fx_file_dir_entry)); + + /* Determine if the status was unsuccessful. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Call the media abort routine. */ + _fx_media_abort(media_ptr); + + /* Return the error status. */ + return(FX_IO_ERROR); + } + + /* Clear the file modified flag. */ + file_ptr -> fx_file_modified = FX_FALSE; + } + + /* Mark the file as closed. */ + file_ptr -> fx_file_id = FX_FILE_CLOSED_ID; + + /* Adjust the pointer and decrement the opened count. */ + file_ptr = file_ptr -> fx_file_opened_next; + open_count--; + } +#endif /* FX_DISABLE_FILE_CLOSE */ + + /* Flush the cached individual FAT entries */ + _fx_utility_FAT_flush(media_ptr); + + /* Flush changed sector(s) in the primary FAT to secondary FATs. */ + _fx_utility_FAT_map_flush(media_ptr); + + + /* Flush the internal logical sector cache. */ + status = _fx_utility_logical_sector_flush(media_ptr, ((ULONG64) 1), (ULONG64) (media_ptr -> fx_media_total_sectors), FX_FALSE); + + /* Determine if the flush was unsuccessful. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Call the media abort routine. */ + _fx_media_abort(media_ptr); + + /* Return the error status. */ + return(FX_IO_ERROR); + } + + /* Determine if the media needs to have the additional information sector updated. This will + only be the case for 32-bit FATs. The logic here only needs to be done if the last reported + available cluster count is different that the currently available clusters. */ + if ((media_ptr -> fx_media_FAT32_additional_info_sector) && + (media_ptr -> fx_media_FAT32_additional_info_last_available != media_ptr -> fx_media_available_clusters) && + (media_ptr -> fx_media_driver_write_protect == FX_FALSE)) + { + + UCHAR *buffer_ptr; + ULONG signature; + + +#ifndef FX_DISABLE_CACHE + /* Setup a pointer to the first cached entry's buffer. */ + buffer_ptr = (media_ptr -> fx_media_sector_cache_list_ptr) -> fx_cached_sector_memory_buffer; + + /* Invalidate this cache entry. */ + (media_ptr -> fx_media_sector_cache_list_ptr) -> fx_cached_sector = (~(ULONG64)0); + (media_ptr -> fx_media_sector_cache_list_ptr) -> fx_cached_sector_valid = FX_FALSE; +#else + buffer_ptr = media_ptr -> fx_media_memory_buffer; +#endif /* FX_DISABLE_CACHE */ + + /* Read the FAT32 additional information sector from the device. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_READ; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = buffer_ptr; + media_ptr -> fx_media_driver_logical_sector = media_ptr -> fx_media_FAT32_additional_info_sector; + media_ptr -> fx_media_driver_sectors = 1; + media_ptr -> fx_media_driver_sector_type = FX_DIRECTORY_SECTOR; + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of driver read sector(s) requests. */ + media_ptr -> fx_media_driver_read_requests++; +#endif + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_READ, media_ptr, media_ptr -> fx_media_FAT32_additional_info_sector, 1, buffer_ptr, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Invoke the driver to read the FAT32 additional information sector. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Determine if the FAT32 sector was read correctly. */ + if (media_ptr -> fx_media_driver_status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Call the media abort routine. */ + _fx_media_abort(media_ptr); + + /* Return the error status. */ + return(FX_IO_ERROR); + } + + /* Setup a pointer into the FAT32 additional information sector. */ + buffer_ptr = media_ptr -> fx_media_driver_buffer; + + /* Pickup the first signature long word. */ + signature = _fx_utility_32_unsigned_read(&buffer_ptr[0]); + + /* Determine if the signature is correct. */ + if (signature == 0x41615252) + { + + /* Yes, the first signature is correct, now pickup the next signature. */ + signature = _fx_utility_32_unsigned_read(&buffer_ptr[484]); + + /* Determine if this signature is correct. */ + if (signature == 0x61417272) + { + + /* Yes, we have a good FAT32 additional information sector. */ + + /* Set the free cluster count to the available clusters in the media control block. */ + _fx_utility_32_unsigned_write(&buffer_ptr[488], media_ptr -> fx_media_available_clusters); + + /* Set the next free cluster number hint to starting search cluster in the media control block. */ + _fx_utility_32_unsigned_write(&buffer_ptr[492], media_ptr -> fx_media_cluster_search_start); + + /* Now write the sector back out to the media. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_WRITE; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = buffer_ptr; + media_ptr -> fx_media_driver_logical_sector = media_ptr -> fx_media_FAT32_additional_info_sector; + media_ptr -> fx_media_driver_sectors = 1; + media_ptr -> fx_media_driver_sector_type = FX_DIRECTORY_SECTOR; + + /* Set the system write flag since we are writing a directory sector. */ + media_ptr -> fx_media_driver_system_write = FX_TRUE; + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of driver write sector(s) requests. */ + media_ptr -> fx_media_driver_write_requests++; +#endif + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, media_ptr -> fx_media_FAT32_additional_info_sector, 1, buffer_ptr, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Invoke the driver to write the FAT32 additional information sector. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Clear the system write flag. */ + media_ptr -> fx_media_driver_system_write = FX_FALSE; + + /* Determine if the FAT32 sector was written correctly. */ + if (media_ptr -> fx_media_driver_status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Call the media abort routine. */ + _fx_media_abort(media_ptr); + + /* Return the sector IO error status. */ + return(FX_IO_ERROR); + } + + /* Successful update of the FAT32 additional information sector. Update the + last written available cluster count. */ + media_ptr -> fx_media_FAT32_additional_info_last_available = media_ptr -> fx_media_available_clusters; + } + } + } + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of driver flush requests. */ + media_ptr -> fx_media_driver_flush_requests++; +#endif + + /* Build the "flush" I/O driver request. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_FLUSH; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_FLUSH, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Call the specified I/O driver with the flush request. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Build the "uninitialize" I/O driver request. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_UNINIT; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_UNINIT, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Call the specified I/O driver with the uninitialize request. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Now remove this media from the open list. */ + + /* Lockout interrupts for media removal. */ + FX_DISABLE_INTS + + /* See if the media is the only one on the media opened list. */ + if (_fx_system_media_opened_count == ((ULONG) 1)) + { + + /* Only opened media, just set the opened list to NULL. */ + _fx_system_media_opened_ptr = FX_NULL; + } + else + { + + /* Otherwise, not the only opened media, link-up the neighbors. */ + (media_ptr -> fx_media_opened_next) -> fx_media_opened_previous = + media_ptr -> fx_media_opened_previous; + (media_ptr -> fx_media_opened_previous) -> fx_media_opened_next = + media_ptr -> fx_media_opened_next; + + /* See if we have to update the opened list head pointer. */ + if (_fx_system_media_opened_ptr == media_ptr) + { + + /* Yes, move the head pointer to the next opened media. */ + _fx_system_media_opened_ptr = media_ptr -> fx_media_opened_next; + } + } + + /* Decrement the opened media counter. */ + _fx_system_media_opened_count--; + + /* Finally, Indicate that this media is closed. */ + media_ptr -> fx_media_id = FX_MEDIA_CLOSED_ID; + + /* Restore interrupt posture. */ + FX_RESTORE_INTS + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD + +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(& (media_ptr -> fx_media_protect)); +#endif +#endif + + /* Invoke media close callback. */ + if (media_ptr -> fx_media_close_notify) + { + media_ptr -> fx_media_close_notify(media_ptr); + } + +#ifdef FX_DONT_CREATE_MUTEX + + /* Release media protection. */ + FX_UNPROTECT +#endif + + /* Return success status to the caller. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_media_close_notify_set.c b/common/src/fx_media_close_notify_set.c new file mode 100644 index 0000000..556882a --- /dev/null +++ b/common/src/fx_media_close_notify_set.c @@ -0,0 +1,82 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_media_close_notif_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the notify function called when media is open. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* media_close_notify Notify function called when */ +/* media is closed */ +/* */ +/* OUTPUT */ +/* */ +/* FX_SUCCESS */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_media_close_notify_set(FX_MEDIA *media_ptr, VOID (*media_close_notify)(FX_MEDIA *media)) +{ + + /* Set the notify function. */ + media_ptr -> fx_media_close_notify = media_close_notify; + + /* Return successful status. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_media_extended_space_available.c b/common/src/fx_media_extended_space_available.c new file mode 100644 index 0000000..6ff37d3 --- /dev/null +++ b/common/src/fx_media_extended_space_available.c @@ -0,0 +1,115 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_media.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_media_extended_space_available PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the number of bytes available in the */ +/* specified media. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* available_bytes_ptr Pointer to available bytes */ +/* destination */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_media_extended_space_available(FX_MEDIA *media_ptr, ULONG64 *available_bytes_ptr) +{ + +ULONG64 available_bytes; +ULONG bytes_per_cluster; +ULONG available_clusters; + + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_MEDIA_SPACE_AVAILABLE, media_ptr, available_bytes_ptr, media_ptr -> fx_media_available_clusters, 0, FX_TRACE_MEDIA_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Pickup the number of free clusters. */ + available_clusters = media_ptr -> fx_media_available_clusters; + + /* Derive the bytes per cluster. */ + bytes_per_cluster = media_ptr -> fx_media_bytes_per_sector * media_ptr -> fx_media_sectors_per_cluster; + + /* Calculate the free space. */ + available_bytes = ((ULONG64)available_clusters) * ((ULONG64)bytes_per_cluster); + + /* Return the value. */ + *available_bytes_ptr = available_bytes; + + /* Release media protection. */ + FX_UNPROTECT + + /* Return a successful status to the caller. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_media_flush.c b/common/src/fx_media_flush.c new file mode 100644 index 0000000..dbd3c0c --- /dev/null +++ b/common/src/fx_media_flush.c @@ -0,0 +1,352 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_media.h" +#include "fx_file.h" +#include "fx_directory.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_media_flush PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function examines the list of open files for this media and */ +/* "flushes" each written open file to the underlying media. After */ +/* the open files have been flushed, the internal logical sector is */ +/* flushed. Finally, the attached driver is sent a flush command so */ +/* that it can flush its sector cache (if any) to the media. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_entry_write Write the directory entry */ +/* _fx_utility_FAT_flush Flush cached FAT entries */ +/* _fx_utility_FAT_map_flush Flush primary FAT changes to */ +/* secondary FAT(s) */ +/* _fx_utility_logical_sector_flush Flush logical sector cache */ +/* _fx_utility_32_unsigned_read Read 32-bit unsigned */ +/* _fx_utility_32_unsigned_write Write 32-bit unsigned */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), and */ +/* added conditional to */ +/* disable cache, */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_media_flush(FX_MEDIA *media_ptr) +{ + +UINT status; +ULONG open_count; +FX_FILE *file_ptr; +FX_INT_SAVE_AREA + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_flushes++; +#endif + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_MEDIA_FLUSH, media_ptr, 0, 0, 0, FX_TRACE_MEDIA_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Check for write protect at the media level (set by driver). */ + if (media_ptr -> fx_media_driver_write_protect) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return write protect error. */ + return(FX_WRITE_PROTECT); + } + + /* Loop through the media's open files. */ + open_count = media_ptr -> fx_media_opened_file_count; + file_ptr = media_ptr -> fx_media_opened_file_list; + while (open_count) + { + + /* Look at each opened file to see if the same file is opened + for writing and has been written to. */ + if ((file_ptr -> fx_file_open_mode == FX_OPEN_FOR_WRITE) && + (file_ptr -> fx_file_modified)) + { + + /* Protect against update. */ + FX_DISABLE_INTS + + /* Set the new time and date. */ + file_ptr -> fx_file_dir_entry.fx_dir_entry_time = _fx_system_time; + file_ptr -> fx_file_dir_entry.fx_dir_entry_date = _fx_system_date; + + /* Restore interrupts. */ + FX_RESTORE_INTS + + /* Copy the new file size into the directory entry. */ + file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size = + file_ptr -> fx_file_current_file_size; + + /* Write the directory entry to the media. */ + status = _fx_directory_entry_write(media_ptr, &(file_ptr -> fx_file_dir_entry)); + + /* Check for a good status. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Error writing the directory. */ + return(status); + } + + /* Clear the file modified flag. */ + file_ptr -> fx_file_modified = FX_FALSE; + } + + /* Adjust the pointer and decrement the opened count. */ + file_ptr = file_ptr -> fx_file_opened_next; + open_count--; + } + + /* Flush the cached individual FAT entries */ + _fx_utility_FAT_flush(media_ptr); + + /* Flush changed sector(s) in the primary FAT to secondary FATs. */ + _fx_utility_FAT_map_flush(media_ptr); + + + /* Flush the internal logical sector cache. */ + status = _fx_utility_logical_sector_flush(media_ptr, ((ULONG64) 1), (ULONG64) (media_ptr -> fx_media_total_sectors), FX_FALSE); + + /* Check for a good status. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Error writing the directory. */ + return(status); + } + + /* Determine if the media needs to have the additional information sector updated. This will + only be the case for 32-bit FATs. The logic here only needs to be done if the last reported + available cluster count is different that the currently available clusters. */ + if ((media_ptr -> fx_media_FAT32_additional_info_sector) && + (media_ptr -> fx_media_FAT32_additional_info_last_available != media_ptr -> fx_media_available_clusters)) + { + + UCHAR *buffer_ptr; + ULONG signature; + +#ifndef FX_DISABLE_CACHE + + /* Setup a pointer to the first cached entry's buffer. */ + buffer_ptr = (media_ptr -> fx_media_sector_cache_list_ptr) -> fx_cached_sector_memory_buffer; + + /* Invalidate this cache entry. */ + (media_ptr -> fx_media_sector_cache_list_ptr) -> fx_cached_sector = (~(ULONG64)0); + (media_ptr -> fx_media_sector_cache_list_ptr) -> fx_cached_sector_valid = FX_FALSE; +#else + buffer_ptr = media_ptr -> fx_media_memory_buffer; +#endif /* FX_DISABLE_CACHE */ + + /* Read the FAT32 additional information sector from the device. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_READ; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = buffer_ptr; + media_ptr -> fx_media_driver_logical_sector = media_ptr -> fx_media_FAT32_additional_info_sector; + media_ptr -> fx_media_driver_sectors = 1; + media_ptr -> fx_media_driver_sector_type = FX_DIRECTORY_SECTOR; + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of driver read sector(s) requests. */ + media_ptr -> fx_media_driver_read_requests++; +#endif + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_READ, media_ptr, media_ptr -> fx_media_FAT32_additional_info_sector, 1, buffer_ptr, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Invoke the driver to read the FAT32 additional information sector. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Determine if the FAT32 sector was read correctly. */ + if (media_ptr -> fx_media_driver_status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(FX_IO_ERROR); + } + + /* Setup a pointer into the FAT32 additional information sector. */ + buffer_ptr = media_ptr -> fx_media_driver_buffer; + + /* Pickup the first signature long word. */ + signature = _fx_utility_32_unsigned_read(&buffer_ptr[0]); + + /* Determine if the signature is correct. */ + if (signature == 0x41615252) + { + + /* Yes, the first signature is correct, now pickup the next signature. */ + signature = _fx_utility_32_unsigned_read(&buffer_ptr[484]); + + /* Determine if this signature is correct. */ + if (signature == 0x61417272) + { + + /* Yes, we have a good FAT32 additional information sector. */ + + /* Set the free cluster count to the available clusters in the media control block. */ + _fx_utility_32_unsigned_write(&buffer_ptr[488], media_ptr -> fx_media_available_clusters); + + /* Set the next free cluster number hint to starting search cluster in the media control block. */ + _fx_utility_32_unsigned_write(&buffer_ptr[492], media_ptr -> fx_media_cluster_search_start); + + /* Now write the sector back out to the media. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_WRITE; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = buffer_ptr; + media_ptr -> fx_media_driver_logical_sector = media_ptr -> fx_media_FAT32_additional_info_sector; + media_ptr -> fx_media_driver_sectors = 1; + media_ptr -> fx_media_driver_sector_type = FX_DIRECTORY_SECTOR; + + /* Set the system write flag since we are writing a directory sector. */ + media_ptr -> fx_media_driver_system_write = FX_TRUE; + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of driver write sector(s) requests. */ + media_ptr -> fx_media_driver_write_requests++; +#endif + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, media_ptr -> fx_media_FAT32_additional_info_sector, 1, buffer_ptr, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Invoke the driver to write the FAT32 additional information sector. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Clear the system write flag. */ + media_ptr -> fx_media_driver_system_write = FX_FALSE; + + /* Determine if the FAT32 sector was written correctly. */ + if (media_ptr -> fx_media_driver_status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the sector IO error status. */ + return(FX_IO_ERROR); + } + + /* Successful update of the FAT32 additional information sector. Update the + last written available cluster count. */ + media_ptr -> fx_media_FAT32_additional_info_last_available = media_ptr -> fx_media_available_clusters; + } + } + } + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of driver flush requests. */ + media_ptr -> fx_media_driver_flush_requests++; +#endif + + /* Build the "flush" I/O driver request. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_FLUSH; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_FLUSH, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Call the specified I/O driver with the flush request. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Determine if the I/O driver flushed successfully. */ + if (media_ptr -> fx_media_driver_status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the driver error status. */ + return(FX_IO_ERROR); + } + + /* Release media protection. */ + FX_UNPROTECT + + /* If we get here, return successful status to the caller. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_media_format.c b/common/src/fx_media_format.c new file mode 100644 index 0000000..c80c53e --- /dev/null +++ b/common/src/fx_media_format.c @@ -0,0 +1,695 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" +#include "fx_utility.h" + + +/* Define global variables necessary for formatting. */ + +/* Define OEM Name. This name must be 8 characters long and be blank padded. + The default may be changed by modifying this file or calling the + fx_media_format_oem_name_set utility prior to calling fx_media_format. */ + +UCHAR _fx_media_format_oem_name[8] = "EL FILEX"; + + +/* Define the default media type. This default may be changed by modifying + this file or calling the fx_media_format_type_set utility prior to calling + fx_media_format. */ + +UCHAR _fx_media_format_media_type = 0xF8; + + +/* Define the default volume ID. This default may be changed by modifying + this file or calling the fx_media_format_volume_id_set utility prior to calling + fx_media_format. */ + +ULONG _fx_media_format_volume_id = 1; + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_media_format PORTABLE C */ +/* 6.1.11 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates a FAT12/16/32 format with raw calls to the */ +/* I/O driver. It can and must be called before the fx_media_open */ +/* and is designed to utilize the same underlying FileX driver. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to media control block*/ +/* (does not need to be opened)*/ +/* driver Pointer to FileX driver (must */ +/* be able to field requests */ +/* prior to opening) */ +/* driver_info_ptr Optional information pointer */ +/* memory_ptr Pointer to memory used by the */ +/* FileX for this media. */ +/* memory_size Size of media memory - must */ +/* at least 512 bytes and */ +/* one sector size. */ +/* volume_name Name of the volume */ +/* number_of_fats Number of FAT tables */ +/* directory_entries Number of directory entries */ +/* hidden_sectors Number of hidden sectors */ +/* total_sectors Total number of sectors */ +/* bytes_per_sector Number of bytes per sector */ +/* sectors_per_cluster Number of sectors per cluster */ +/* heads Number of heads */ +/* sectors_per_track Number of sectors per track */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* Media driver */ +/* _fx_utility_16_unsigned_write Write 16-bit unsigned */ +/* _fx_utility_32_unsigned_write Write 32-bit unsigned */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), and */ +/* added conditional to */ +/* disable force memset, */ +/* resulting in version 6.1 */ +/* 03-02-2021 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1.5 */ +/* 08-02-2021 Bhupendra Naphade Modified comment(s), and */ +/* updated boot write logic, */ +/* resulting in version 6.1.8 */ +/* 04-25-2022 Bhupendra Naphade Modified comment(s), and */ +/* updated reserved FAT entry */ +/* value, */ +/* resulting in version 6.1.11 */ +/* */ +/**************************************************************************/ +UINT _fx_media_format(FX_MEDIA *media_ptr, VOID (*driver)(FX_MEDIA *media), VOID *driver_info_ptr, UCHAR *memory_ptr, UINT memory_size, + CHAR *volume_name, UINT number_of_fats, UINT directory_entries, UINT hidden_sectors, + ULONG total_sectors, UINT bytes_per_sector, UINT sectors_per_cluster, + UINT heads, UINT sectors_per_track) +{ + +UCHAR *byte_ptr; +UINT reserved_sectors, i, j, root_sectors, total_clusters, bytes_needed; +UINT sectors_per_fat, f, s; + + + /* Create & write bootrecord from drive geometry information. */ + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_MEDIA_FORMAT, media_ptr, directory_entries, total_sectors, sectors_per_cluster, FX_TRACE_MEDIA_EVENTS, 0, 0) + + /* Validate bytes per sector value: greater than zero and no more than 4096. */ + if((bytes_per_sector == 0) || (bytes_per_sector > 4096)) + return(FX_SECTOR_INVALID); + + /* Validate sectors per cluster value: greater than zero and no more than 128. */ + if((sectors_per_cluster == 0) || (sectors_per_cluster > 128)) + return(FX_SECTOR_INVALID); + + /* Setup driver pointer and memory information. */ + media_ptr -> fx_media_driver_entry = driver; + media_ptr -> fx_media_memory_buffer = (UCHAR *)memory_ptr; + media_ptr -> fx_media_memory_size = memory_size; + + /* Store geometry information in media record - driver needs this. */ + media_ptr -> fx_media_bytes_per_sector = bytes_per_sector; + media_ptr -> fx_media_sectors_per_track = sectors_per_track; + media_ptr -> fx_media_heads = heads; + media_ptr -> fx_media_hidden_sectors = hidden_sectors; + + /* Initialize the supplied media I/O driver. First, build the + initialize driver request. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_INIT; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_info = driver_info_ptr; + media_ptr -> fx_media_driver_write_protect = FX_FALSE; + media_ptr -> fx_media_driver_free_sector_update = FX_FALSE; + media_ptr -> fx_media_driver_data_sector_read = FX_FALSE; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_INIT, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Call the specified I/O driver with the initialize request. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Determine if the I/O driver initialized successfully. */ + if (media_ptr -> fx_media_driver_status != FX_SUCCESS) + { + + /* Return the driver error status. */ + return(FX_IO_ERROR); + } + + /* Setup driver buffer memory. */ + media_ptr -> fx_media_driver_buffer = memory_ptr; + + /* Move the buffer pointer into a local copy. */ + byte_ptr = media_ptr -> fx_media_driver_buffer; + +#ifndef FX_DISABLE_FORCE_MEMORY_OPERATION + /* Clear the buffer record out, assuming it is large enough for one sector. */ + for (i = 0; i < bytes_per_sector; i++) + { + + /* Clear each byte of the boot record. */ + byte_ptr[i] = (UCHAR)0; + } +#else + _fx_utility_memory_set(byte_ptr, 0, bytes_per_sector); +#endif /* FX_DISABLE_FORCE_MEMORY_OPERATION */ + + /* Set jump instruction at the beginning of the sector. */ + byte_ptr[0] = (UCHAR)0xEB; + byte_ptr[1] = (UCHAR)0x34; + byte_ptr[2] = (UCHAR)0x90; + + /* Set the OEM name in the boot record. */ + for (i = 0; i < 8; i++) + { + + /* Copy a character from the OEM name. */ + byte_ptr[i + 3] = _fx_media_format_oem_name[i]; + } + + /* Set the media type in the boot record. */ + byte_ptr[FX_MEDIA_TYPE] = _fx_media_format_media_type; + + /* Set the number of bytes per sector. */ + _fx_utility_16_unsigned_write(&byte_ptr[FX_BYTES_SECTOR], bytes_per_sector); + + /* Set the number of sectors per track. */ + _fx_utility_16_unsigned_write(&byte_ptr[FX_SECTORS_PER_TRK], sectors_per_track); + + /* Set the number of heads. */ + _fx_utility_16_unsigned_write(&byte_ptr[FX_HEADS], heads); + +#ifdef FX_FORCE_512_BYTE_BOOT_SECTOR + + /* Calculate the number of reserved sectors. If sector size is smaller than 512 bytes, there will be + reserved sectors, otherwise assumed that only the sector containing bootrecord is reserved. */ + if (bytes_per_sector < 512) + { + reserved_sectors = 512 / bytes_per_sector; + } + else + { + reserved_sectors = 1; + } +#else + + /* The boot sector is the only reserved sector. */ + reserved_sectors = 1; +#endif + + + /* Calculate the maximum clusters.... This is actually greater than the actual since the FAT + sectors have yet to be accounted for. */ + total_clusters = (total_sectors - reserved_sectors - ((directory_entries * FX_DIR_ENTRY_SIZE) + (bytes_per_sector - 1)) / bytes_per_sector) / sectors_per_cluster; + + /* Calculate the maximum number of FAT sectors necessary for FAT12. */ + if (total_clusters % 2) + { + bytes_needed = (total_clusters + total_clusters / 2) + 1; + } + else + { + bytes_needed = (total_clusters + total_clusters / 2); + } + sectors_per_fat = bytes_needed / bytes_per_sector; + if (bytes_needed % bytes_per_sector) + { + sectors_per_fat++; + } + + /* Now adjust the total clusters by the number of sectors per FAT. */ + total_clusters = total_clusters - ((sectors_per_fat * number_of_fats) + (sectors_per_cluster - 1)) / sectors_per_cluster; + + /* Is the total cluster count greater than the FAT12 maximum? */ + if (total_clusters >= FX_12_BIT_FAT_SIZE) + { + + /* Yes, too big for FAT12, we need to evaluate for FAT16. */ + + /* Reset the maximum clusters.... This is actually greater than the actual since the FAT + sectors have yet to be accounted for. */ + total_clusters = (total_sectors - reserved_sectors - ((directory_entries * FX_DIR_ENTRY_SIZE) + (bytes_per_sector - 1)) / bytes_per_sector) / sectors_per_cluster; + + /* Calculate 16-bit FAT is present. Each cluster requires a 2 byte entry in the FAT table. */ + sectors_per_fat = (total_clusters * 2) / bytes_per_sector; + if ((total_clusters * 2) % bytes_per_sector) + { + sectors_per_fat++; + } + + /* Now adjust the total clusters by the number of sectors per FAT. */ + total_clusters = total_clusters - ((sectors_per_fat * number_of_fats) + (sectors_per_cluster - 1)) / sectors_per_cluster; + + /* Is the total cluster count greater than the FAT16 maximum? */ + if (total_clusters >= FX_16_BIT_FAT_SIZE) + { + + /* Yes, FAT32 is present. */ + + /* Allocate room for the FAT32 additional information sector. This contains useful information + such as the number of available clusters between successive mounting of the media. */ + if (bytes_per_sector == 512) + { + + /* Write sector number 1 to the additional information sector. */ + _fx_utility_16_unsigned_write(&byte_ptr[48], 1); + + /* Increment the reserved sectors count, since this will count as a reserved sector. */ + reserved_sectors++; + } + else + { + + /* Write value to indicate there is no additional information sector. */ + _fx_utility_16_unsigned_write(&byte_ptr[48], 0xFFFF); + } + + /* Allocate the first cluster to the root directory. */ + _fx_utility_32_unsigned_write(&byte_ptr[FX_ROOT_CLUSTER_32], FX_FAT_ENTRY_START); + + /* Determine if the number of root directory entries should be modified. */ + directory_entries = (sectors_per_cluster * bytes_per_sector) / FX_DIR_ENTRY_SIZE; + + /* Reset the total_clusters for the FAT32 calculation. */ + total_clusters = (total_sectors - reserved_sectors) / sectors_per_cluster; + + /* 32-bit FAT is present. Each cluster requires a 4 byte entry in the FAT table. */ + sectors_per_fat = (total_clusters * 4) / bytes_per_sector; + if ((total_clusters * 4) % bytes_per_sector) + { + sectors_per_fat++; + } + + /* Now adjust the total clusters by the number of sectors per FAT. */ + total_clusters = total_clusters - ((sectors_per_fat * number_of_fats) + (sectors_per_cluster - 1)) / sectors_per_cluster; + } + } + + /* Set sectors per FAT type. */ + if (total_clusters < FX_16_BIT_FAT_SIZE) + { + + /* Set the number of sectors per FAT12/16. */ + _fx_utility_16_unsigned_write(&byte_ptr[FX_SECTORS_PER_FAT], sectors_per_fat); + + /* Set the signature. */ + byte_ptr[FX_BOOT_SIG] = 0x29; + + /* Setup the volume ID. */ + _fx_utility_32_unsigned_write(&byte_ptr[FX_VOLUME_ID], _fx_media_format_volume_id); + } + else + { + + /* Set the number of sectors per FAT32. */ + _fx_utility_32_unsigned_write(&byte_ptr[FX_SECTORS_PER_FAT_32], sectors_per_fat); + + /* Set the signature. */ + byte_ptr[FX_BOOT_SIG_32] = 0x29; + + /* Setup the volume ID. */ + _fx_utility_32_unsigned_write(&byte_ptr[FX_VOLUME_ID_32], _fx_media_format_volume_id); + } + + /* Set the total number of sectors. */ + if (total_sectors < (ULONG)0xFFFF) + { + + /* Write the 16-bit total sector field. */ + _fx_utility_16_unsigned_write(&byte_ptr[FX_SECTORS], (UINT)(total_sectors)); + + /* Set the number of huge sectors. */ + _fx_utility_32_unsigned_write(&byte_ptr[FX_HUGE_SECTORS], 0); + } + else + { + + /* Write the 16-bit total sector field as 0. */ + _fx_utility_16_unsigned_write(&byte_ptr[FX_SECTORS], (UINT)0); + + /* Set the number of huge sectors. */ + _fx_utility_32_unsigned_write(&byte_ptr[FX_HUGE_SECTORS], total_sectors); + } + + /* Set the number of reserved sectors. */ + _fx_utility_16_unsigned_write(&byte_ptr[FX_RESERVED_SECTORS], reserved_sectors); + + /* Set the number of sectors per cluster */ + byte_ptr[FX_SECTORS_CLUSTER] = (UCHAR)sectors_per_cluster; + + /* Set the number of FATs. */ + byte_ptr[FX_NUMBER_OF_FATS] = (UCHAR)number_of_fats; + + /* Set the number of hidden sectors. */ + _fx_utility_32_unsigned_write(&byte_ptr[FX_HIDDEN_SECTORS], hidden_sectors); + + /* Determine if a FAT12 or FAT16 is present. If FAT32 is present, these fields are left alone! */ + if (total_clusters < FX_16_BIT_FAT_SIZE) + { + + /* Yes, set the number of root directory entries. */ + _fx_utility_16_unsigned_write(&byte_ptr[FX_ROOT_DIR_ENTRIES], directory_entries); + } + + /* Now setup the volume label. */ + if (total_clusters < FX_16_BIT_FAT_SIZE) + { + + /* FAT12/16 volume label offset. */ + j = FX_VOLUME_LABEL; + } + else + { + + /* FAT32 volume label offset. */ + j = FX_VOLUME_LABEL_32; + } + + i = 0; + while (i < 11) + { + + /* Determine if it is NULL. */ + if (volume_name[i] == 0) + { + + /* Yes, the copying is finished. */ + break; + } + + /* Otherwise, copy byte of volume name into boot record. */ + byte_ptr[j + i] = (UCHAR)volume_name[i]; + + /* Increment byte position. */ + i++; + } + + /* Now blank-pad the remainder of the volume name. */ +#ifndef FX_DISABLE_FORCE_MEMORY_OPERATION + while (i < 11) + { + + byte_ptr[j + i] = (UCHAR)' '; + i++; + } +#else + _fx_utility_memory_set(&byte_ptr[j + i], ' ', (11 - i)); +#endif /* FX_DISABLE_FORCE_MEMORY_OPERATION */ + + +#ifdef FX_FORCE_512_BYTE_BOOT_SECTOR + + /* Set bootrecord signature. */ + byte_ptr[510] = 0x55; + byte_ptr[511] = 0xAA; +#else + + /* Set bootrecord signature. */ + byte_ptr[bytes_per_sector - 2] = 0x55; + byte_ptr[bytes_per_sector - 1] = 0xAA; +#endif + + /* Select the boot record write command. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_BOOT_WRITE; + media_ptr -> fx_media_driver_system_write = FX_TRUE; + media_ptr -> fx_media_driver_sectors = 1; + media_ptr -> fx_media_driver_sector_type = FX_BOOT_SECTOR; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_BOOT_WRITE, media_ptr, memory_ptr, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Write out the bootrecord */ + (driver)(media_ptr); + + /* Clear the write flag. */ + media_ptr -> fx_media_driver_system_write = FX_FALSE; + + /* Determine if it was successful. */ + if (media_ptr -> fx_media_driver_status != FX_SUCCESS) + { + return(FX_IO_ERROR); + } + + /* Calculate the number of root sectors. */ + root_sectors = ((directory_entries * FX_DIR_ENTRY_SIZE) + bytes_per_sector - 1) / bytes_per_sector; + + /* Determine if FAT32 is present AND if the bytes per sector is large enough to have + a FSINFO sector. */ + if ((total_clusters >= FX_16_BIT_FAT_SIZE) && (bytes_per_sector == 512)) + { + +#ifndef FX_DISABLE_FORCE_MEMORY_OPERATION + /* Clear sector buffer. */ + for (i = 0; i < bytes_per_sector; i++) + { + byte_ptr[i] = (CHAR)0; + } +#else + _fx_utility_memory_set(byte_ptr, 0, bytes_per_sector); +#endif /* FX_DISABLE_FORCE_MEMORY_OPERATION */ + + /* Build the FSINFO fields. */ + + /* Build first signature word, used to help verify this is a FSINFO sector. */ + byte_ptr[0] = 0x52; + byte_ptr[1] = 0x52; + byte_ptr[2] = 0x61; + byte_ptr[3] = 0x41; + + /* Build the next signature word, this too is used to help verify that this is a FSINFO sector. */ + byte_ptr[484] = 0x72; + byte_ptr[485] = 0x72; + byte_ptr[486] = 0x41; + byte_ptr[487] = 0x61; + + /* Build the final signature word, this too is used to help verify that this is a FSINFO sector. */ + byte_ptr[508] = 0x55; + byte_ptr[509] = 0xAA; + + /* Setup the total available clusters on the media. We need to subtract 1 for the FAT32 root directory. */ + _fx_utility_32_unsigned_write(&byte_ptr[488], (total_clusters - 1)); + + /* Setup the starting free cluster to 3, since cluster 2 is reserved for the FAT32 root directory. */ + _fx_utility_32_unsigned_write(&byte_ptr[492], 3); + + /* Now write the FSINFO sector to the media. */ + media_ptr -> fx_media_driver_logical_sector = 1; + media_ptr -> fx_media_driver_request = FX_DRIVER_WRITE; + media_ptr -> fx_media_driver_sectors = 1; + media_ptr -> fx_media_driver_system_write = FX_TRUE; + media_ptr -> fx_media_driver_sector_type = FX_BOOT_SECTOR; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, 1, 1, memory_ptr, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Write out the sector. */ + (driver)(media_ptr); + + /* Clear the system write flag. */ + media_ptr -> fx_media_driver_system_write = FX_FALSE; + + /* Determine if it was successful. */ + if (media_ptr -> fx_media_driver_status != FX_SUCCESS) + { + return(FX_IO_ERROR); + } + } + + /* At this point we need set up first to FAT entries and clear the remaining FAT sectors area. */ + + /* Loop through number of FATs. The first is the only one used. */ + for (f = 0; f < number_of_fats; f++) + { + + /* Loop through all the sectors in this FAT. */ + for (s = 0; s < sectors_per_fat; s++) + { + + if (s == 0) + { + + /* Reserve the first two FAT table entries. */ + if (total_clusters < FX_12_BIT_FAT_SIZE) + { + + /* Reserve the first two FAT-12 entries. */ + byte_ptr[0] = _fx_media_format_media_type; + byte_ptr[1] = (UCHAR)0xFF; + byte_ptr[2] = (UCHAR)0xFF; + + /* Start clearing at FAT entry 3. */ + i = 3; + } + else if (total_clusters < FX_16_BIT_FAT_SIZE) + { + + /* Reserve the first two FAT-16 entries. */ + byte_ptr[0] = _fx_media_format_media_type; + byte_ptr[1] = (UCHAR)0xFF; + byte_ptr[2] = (UCHAR)0xFF; + byte_ptr[3] = (UCHAR)0xFF; + + /* Start clearing at FAT entry 3. */ + i = 4; + } + else + { + + /* Reserve the first two FAT-32 entries. */ + byte_ptr[0] = _fx_media_format_media_type; + byte_ptr[1] = (UCHAR)0xFF; + byte_ptr[2] = (UCHAR)0xFF; + byte_ptr[3] = (UCHAR)0x0F; + byte_ptr[4] = (UCHAR)0xFF; + byte_ptr[5] = (UCHAR)0xFF; + byte_ptr[6] = (UCHAR)0xFF; + byte_ptr[7] = (UCHAR)0x0F; + + /* Preallocate the first cluster for the root directory. */ + byte_ptr[8] = (UCHAR)0xFF; + byte_ptr[9] = (UCHAR)0xFF; + byte_ptr[10] = (UCHAR)0xFF; + byte_ptr[11] = (UCHAR)0x0F; + + /* Start clearing at FAT entry 3. */ + i = 12; + } + } + else + { + i = 0; + } + +#ifndef FX_DISABLE_FORCE_MEMORY_OPERATION + /* Clear remainder of sector buffer. */ + for (; i < bytes_per_sector; i++) + { + byte_ptr[i] = (CHAR)0; + } +#else + _fx_utility_memory_set(&byte_ptr[i], 0, (bytes_per_sector - i)); +#endif /* FX_DISABLE_FORCE_MEMORY_OPERATION */ + + /* Build sector write command. */ + media_ptr -> fx_media_driver_logical_sector = reserved_sectors + (f * sectors_per_fat) + s; + media_ptr -> fx_media_driver_request = FX_DRIVER_WRITE; + media_ptr -> fx_media_driver_sectors = 1; + media_ptr -> fx_media_driver_system_write = FX_TRUE; + media_ptr -> fx_media_driver_sector_type = FX_FAT_SECTOR; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, media_ptr -> fx_media_driver_logical_sector, 1, memory_ptr, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Write out the sector. */ + (driver)(media_ptr); + + /* Clear the system write flag. */ + media_ptr -> fx_media_driver_system_write = FX_FALSE; + + /* Determine if it was successful. */ + if (media_ptr -> fx_media_driver_status != FX_SUCCESS) + { + return(FX_IO_ERROR); + } + } + } + +#ifndef FX_DISABLE_FORCE_MEMORY_OPERATION + /* Clear sector buffer. */ + for (i = 0; i < bytes_per_sector; i++) + { + byte_ptr[i] = (CHAR)0; + } +#else + _fx_utility_memory_set(byte_ptr, 0, bytes_per_sector); +#endif /* FX_DISABLE_FORCE_MEMORY_OPERATION */ + + /* Now clear the root directory sectors. */ + for (s = 0; s < root_sectors; s++) + { + + /* Build sector write command. */ + media_ptr -> fx_media_driver_logical_sector = reserved_sectors + (number_of_fats * sectors_per_fat) + s; + media_ptr -> fx_media_driver_request = FX_DRIVER_WRITE; + media_ptr -> fx_media_driver_sectors = 1; + media_ptr -> fx_media_driver_system_write = FX_TRUE; + media_ptr -> fx_media_driver_sector_type = FX_DIRECTORY_SECTOR; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, media_ptr -> fx_media_driver_logical_sector, 1, memory_ptr, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Write out the sector. */ + (driver)(media_ptr); + + /* Clear the write flag. */ + media_ptr -> fx_media_driver_system_write = FX_FALSE; + + /* Determine if it was successful. */ + if (media_ptr -> fx_media_driver_status != FX_SUCCESS) + { + return(FX_IO_ERROR); + } + } + + /* Build the "uninitialize" I/O driver request. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_UNINIT; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_UNINIT, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Call the specified I/O driver with the uninitialize request. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Return success! */ + return(media_ptr -> fx_media_driver_status); +} + diff --git a/common/src/fx_media_format_oem_name_set.c b/common/src/fx_media_format_oem_name_set.c new file mode 100644 index 0000000..b626b1d --- /dev/null +++ b/common/src/fx_media_format_oem_name_set.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" + + +/* Define external reference to OEM name string. */ + +extern UCHAR _fx_media_format_oem_name[8]; +UINT fx_media_format_oem_name_set(UCHAR new_oem_name[8]); + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_media_format_oem_name_set PORTABLE C */ +/* 6.1.5 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function modifies the default OEM name for all formatting. The */ +/* new OEM name must be 8 characters long - blank padded if necessary. */ +/* */ +/* */ +/* INPUT */ +/* */ +/* new_oem_name Pointer to new OEM name */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* 03-02-2021 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1.5 */ +/* */ +/**************************************************************************/ +UINT fx_media_format_oem_name_set(UCHAR new_oem_name[8]) +{ + +UINT i; + + + /* Simply copy the new OEM name into the default location. */ + for (i = 0; i < 8; i++) + { + + /* Copy one character of the new OEM name. */ + _fx_media_format_oem_name[i] = new_oem_name[i]; + } + + /* Return success. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_media_format_type_set.c b/common/src/fx_media_format_type_set.c new file mode 100644 index 0000000..cab1ee9 --- /dev/null +++ b/common/src/fx_media_format_type_set.c @@ -0,0 +1,88 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" + + +/* Define external reference to media type. */ + +extern UCHAR _fx_media_format_media_type; +UINT fx_media_format_type_set(UCHAR new_media_type); + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_media_format_type_set PORTABLE C */ +/* 6.1.5 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function modifies the default media type for all formatting. */ +/* */ +/* */ +/* INPUT */ +/* */ +/* new_media_type New media type value */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* 03-02-2021 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1.5 */ +/* */ +/**************************************************************************/ +UINT fx_media_format_type_set(UCHAR new_media_type) +{ + + /* Simply copy the new media type into the default location. */ + _fx_media_format_media_type = new_media_type; + + /* Return success. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_media_format_volume_id_set.c b/common/src/fx_media_format_volume_id_set.c new file mode 100644 index 0000000..cd4f914 --- /dev/null +++ b/common/src/fx_media_format_volume_id_set.c @@ -0,0 +1,88 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" + + +/* Define external reference to media volume ID. */ + +extern ULONG _fx_media_format_volume_id; +UINT fx_media_format_volume_id_set(ULONG new_volume_id); + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_media_format_media_volume_id_set PORTABLE C */ +/* 6.1.5 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function modifies the default media volume ID for all */ +/* formatting. */ +/* */ +/* */ +/* INPUT */ +/* */ +/* new_volume_id New media volume Id value */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* 03-02-2021 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1.5 */ +/* */ +/**************************************************************************/ +UINT fx_media_format_volume_id_set(ULONG new_volume_id) +{ + + /* Simply copy the new media volume ID into the default location. */ + _fx_media_format_volume_id = new_volume_id; + + /* Return success. */ + return(FX_SUCCESS); +} diff --git a/common/src/fx_media_open.c b/common/src/fx_media_open.c new file mode 100644 index 0000000..f049251 --- /dev/null +++ b/common/src/fx_media_open.c @@ -0,0 +1,1011 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_media.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_media_open PORTABLE C */ +/* 6.2.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function opens the specified media with the supplied device */ +/* driver. The specified media must conform to the FAT compatible */ +/* file format, which is verified during the media open process. In */ +/* addition, the supplied FileX device driver must also conform to */ +/* the FileX device driver specification. */ +/* */ +/* The FAT boot sector (512 bytes) that is verified by this */ +/* function must look like the following: */ +/* */ +/* Byte Offset Meaning Size */ +/* */ +/* 0x000 Jump Instructions 3 */ +/* 0x003 OEM Name 8 */ +/* 0x00B *Bytes per Sector 2 */ +/* 0x00D *Sectors per Cluster 1 */ +/* 0x00E *Reserved Sectors 2 */ +/* 0x010 *Number of FATs 1 */ +/* 0x011 *Max Root Dir Entries 2 */ +/* 0x013 *Number of Sectors 2 */ +/* 0x015 Media Type 1 */ +/* 0x016 *Sectors per FAT 2 */ +/* 0x018 *Sectors per Track 2 */ +/* 0x01A *Number of Heads 2 */ +/* 0x01C *Hidden Sectors 4 */ +/* 0x020 *Huge Sectors 4 */ +/* 0x024 Drive Number 1 */ +/* 0x025 Reserved 1 */ +/* 0x026 Boot Signature 1 */ +/* 0x027 Volume ID 4 */ +/* 0x02B Volume Label 11 */ +/* 0x036 File System Type 8 */ +/* ... ... ... */ +/* 0x1FE **Signature (0x55aa) 2 */ +/* */ +/* * Denotes which elements of the boot record */ +/* FileX uses. */ +/* */ +/* **Denotes the element is checked by the I/O */ +/* driver. This eliminates the need for a minimum */ +/* 512-byte buffer for FileX. */ +/* */ +/* Note: All values above are in little endian format, i.e. the LSB is */ +/* in the lowest address. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* media_name Pointer to media name string */ +/* media_driver Media driver entry function */ +/* driver_info_ptr Optional information pointer */ +/* supplied to media driver */ +/* memory_ptr Pointer to memory used by the */ +/* FileX for this media. */ +/* memory_size Size of media memory - must */ +/* at least 512 bytes and */ +/* one sector size. */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* I/O Driver */ +/* _fx_utility_16_unsigned_read Read 16-bit unsigned value */ +/* _fx_utility_32_unsigned_read Read 32-bit unsigned value */ +/* _fx_utility_logical_sector_flush Invalidate log sector cache */ +/* _fx_media_boot_info_extract Extract media information */ +/* _fx_utility_FAT_entry_read Pickup FAT entry contents */ +/* tx_mutex_create Create protection mutex */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), and */ +/* added conditional to */ +/* disable force memset, */ +/* build options and cache, */ +/* resulting in version 6.1 */ +/* 01-31-2022 William E. Lamie Modified comment(s), fixed */ +/* errors without cache, */ +/* resulting in version 6.1.10 */ +/* 10-31-2022 Tiejun Zhou Modified comment(s), */ +/* fixed memory buffer when */ +/* cache is disabled, */ +/* resulting in version 6.2.0 */ +/* */ +/**************************************************************************/ +UINT _fx_media_open(FX_MEDIA *media_ptr, CHAR *media_name, + VOID (*media_driver)(FX_MEDIA *), VOID *driver_info_ptr, + VOID *memory_ptr, ULONG memory_size) +{ + +FX_MEDIA_PTR tail_ptr; +ULONG cluster_number; +ULONG FAT_entry, FAT_sector, FAT_read_sectors; +ULONG i, j; +#ifndef FX_DISABLE_CACHE +FX_CACHED_SECTOR *cache_entry_ptr; +#endif /* FX_DISABLE_CACHE */ +UINT status; +UINT additional_info_sector; +UCHAR *original_memory_ptr; +ULONG bytes_in_buffer; +FX_INT_SAVE_AREA + + +#ifndef FX_DISABLE_BUILD_OPTIONS + /* Reference the version ID and option words to ensure they are linked in. */ + if ((_fx_system_build_options_1 | _fx_system_build_options_2 | _fx_system_build_options_3) == 0 || + _fx_version_id[0] == 0) + { + + /* We should never get here! */ + return(FX_NOT_IMPLEMENTED); + } +#endif /* FX_DISABLE_BUILD_OPTIONS */ + +#ifdef FX_DISABLE_FORCE_MEMORY_OPERATION + _fx_utility_memory_set((UCHAR *)media_ptr, 0, sizeof(FX_MEDIA)); +#endif /* FX_DISABLE_FORCE_MEMORY_OPERATION */ +#ifdef FX_DISABLE_CACHE + media_ptr -> fx_media_memory_buffer_sector = (ULONG64)-1; +#endif /* FX_DISABLE_CACHE */ + + /* Save the basic information in the media control block. */ + media_ptr -> fx_media_name = media_name; + media_ptr -> fx_media_driver_entry = media_driver; + media_ptr -> fx_media_memory_buffer = (UCHAR *)memory_ptr; + media_ptr -> fx_media_memory_size = memory_size; +#ifndef FX_DISABLE_FORCE_MEMORY_OPERATION + media_ptr -> fx_media_disable_burst_cache = FX_FALSE; + media_ptr -> fx_media_FAT_type = 0; +#endif /* FX_DISABLE_FORCE_MEMORY_OPERATION */ + + /* Save the original memory pointer. */ + original_memory_ptr = (UCHAR *)memory_ptr; + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Clear the optional media statistics. */ + media_ptr -> fx_media_directory_attributes_reads = 0; + media_ptr -> fx_media_directory_attributes_sets = 0; + media_ptr -> fx_media_directory_creates = 0; + media_ptr -> fx_media_directory_default_gets = 0; + media_ptr -> fx_media_directory_default_sets = 0; + media_ptr -> fx_media_directory_deletes = 0; + media_ptr -> fx_media_directory_first_entry_finds = 0; + media_ptr -> fx_media_directory_first_full_entry_finds = 0; + media_ptr -> fx_media_directory_information_gets = 0; + media_ptr -> fx_media_directory_local_path_clears = 0; + media_ptr -> fx_media_directory_local_path_gets = 0; + media_ptr -> fx_media_directory_local_path_restores = 0; + media_ptr -> fx_media_directory_local_path_sets = 0; + media_ptr -> fx_media_directory_name_tests = 0; + media_ptr -> fx_media_directory_next_entry_finds = 0; + media_ptr -> fx_media_directory_next_full_entry_finds = 0; + media_ptr -> fx_media_directory_renames = 0; + media_ptr -> fx_media_file_allocates = 0; + media_ptr -> fx_media_file_attributes_reads = 0; + media_ptr -> fx_media_file_attributes_sets = 0; + media_ptr -> fx_media_file_best_effort_allocates = 0; + media_ptr -> fx_media_file_closes = 0; + media_ptr -> fx_media_file_creates = 0; + media_ptr -> fx_media_file_deletes = 0; + media_ptr -> fx_media_file_opens = 0; + media_ptr -> fx_media_file_reads = 0; + media_ptr -> fx_media_file_relative_seeks = 0; + media_ptr -> fx_media_file_renames = 0; + media_ptr -> fx_media_file_seeks = 0; + media_ptr -> fx_media_file_truncates = 0; + media_ptr -> fx_media_file_truncate_releases = 0; + media_ptr -> fx_media_file_writes = 0; + media_ptr -> fx_media_aborts = 0; + media_ptr -> fx_media_flushes = 0; + media_ptr -> fx_media_reads = 0; + media_ptr -> fx_media_writes = 0; + media_ptr -> fx_media_directory_entry_reads = 0; + media_ptr -> fx_media_directory_entry_writes = 0; + media_ptr -> fx_media_directory_searches = 0; +#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE + media_ptr -> fx_media_directory_search_cache_hits = 0; +#endif + media_ptr -> fx_media_directory_free_searches = 0; + media_ptr -> fx_media_fat_entry_reads = 0; + media_ptr -> fx_media_fat_entry_writes = 0; + media_ptr -> fx_media_fat_entry_cache_read_hits = 0; + media_ptr -> fx_media_fat_entry_cache_read_misses = 0; + media_ptr -> fx_media_fat_entry_cache_write_hits = 0; + media_ptr -> fx_media_fat_entry_cache_write_misses = 0; + media_ptr -> fx_media_fat_cache_flushes = 0; + media_ptr -> fx_media_fat_sector_reads = 0; + media_ptr -> fx_media_fat_sector_writes = 0; + media_ptr -> fx_media_logical_sector_reads = 0; + media_ptr -> fx_media_logical_sector_writes = 0; + media_ptr -> fx_media_logical_sector_cache_read_hits = 0; + media_ptr -> fx_media_logical_sector_cache_read_misses = 0; + media_ptr -> fx_media_driver_read_requests = 0; + media_ptr -> fx_media_driver_write_requests = 0; + media_ptr -> fx_media_driver_boot_read_requests = 0; + media_ptr -> fx_media_driver_boot_write_requests = 0; + media_ptr -> fx_media_driver_release_sectors_requests = 0; + media_ptr -> fx_media_driver_flush_requests = 0; +#endif +#ifdef FX_ENABLE_FAULT_TOLERANT + media_ptr -> fx_media_fault_tolerant_enabled = FX_FALSE; + media_ptr -> fx_media_fault_tolerant_state = 0; +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_MEDIA_OPEN, media_ptr, media_driver, memory_ptr, memory_size, FX_TRACE_MEDIA_EVENTS, 0, 0) + + /* Initialize the supplied media I/O driver. First, build the + initialize driver request. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_INIT; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_info = driver_info_ptr; + media_ptr -> fx_media_driver_write_protect = FX_FALSE; + media_ptr -> fx_media_driver_free_sector_update = FX_FALSE; + media_ptr -> fx_media_driver_data_sector_read = FX_FALSE; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_INIT, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Call the specified I/O driver with the initialize request. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Determine if the I/O driver initialized successfully. */ + if (media_ptr -> fx_media_driver_status != FX_SUCCESS) + { + + /* Return the driver error status. */ + return(FX_IO_ERROR); + } + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of driver boot read requests. */ + media_ptr -> fx_media_driver_boot_read_requests++; +#endif + + /* Read the boot sector from the device. Build the read boot sector + command. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_BOOT_READ; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = memory_ptr; + media_ptr -> fx_media_driver_sectors = 1; + media_ptr -> fx_media_driver_sector_type = FX_BOOT_SECTOR; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_BOOT_READ, media_ptr, memory_ptr, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Invoke the driver to read the boot sector. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Determine if the boot sector was read correctly. */ + if (media_ptr -> fx_media_driver_status != FX_SUCCESS) + { + + /* Build the "uninitialize" I/O driver request. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_UNINIT; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_UNINIT, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Call the specified I/O driver with the uninitialize request. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Return the boot sector error status. */ + return(FX_BOOT_ERROR); + } + + /* Extract and validate the media parameters from the boot sector. */ + if (_fx_media_boot_info_extract(media_ptr) != FX_SUCCESS) + { + + /* Build the "uninitialize" I/O driver request. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_UNINIT; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_UNINIT, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Call the specified I/O driver with the uninitialize request. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Return the invalid media error status. */ + return(FX_MEDIA_INVALID); + } + + /* Pickup the additional info sector number. This will only be used in FAT32 situations. */ + additional_info_sector = _fx_utility_16_unsigned_read(&media_ptr -> fx_media_driver_buffer[48]); + + /* Is there at least one? */ + if (memory_size < media_ptr -> fx_media_bytes_per_sector) + { + + /* Build the "uninitialize" I/O driver request. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_UNINIT; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_UNINIT, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Call the specified I/O driver with the uninitialize request. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Error in the buffer size supplied by user. */ + return(FX_BUFFER_ERROR); + } + +#ifndef FX_DISABLE_CACHE + /* Determine how many logical sectors can be cached with user's supplied + buffer area - there must be at least enough for one sector! */ + media_ptr -> fx_media_sector_cache_size = memory_size / media_ptr -> fx_media_bytes_per_sector; + + /* If trace is enabled, register this object. */ + FX_TRACE_OBJECT_REGISTER(FX_TRACE_OBJECT_TYPE_MEDIA, media_ptr, media_name, FX_MAX_FAT_CACHE, media_ptr -> fx_media_sector_cache_size) + + /* Adjust the internal cache to fit the fixed number of sector cache control blocks + built into the media control block. */ + if (media_ptr -> fx_media_sector_cache_size > FX_MAX_SECTOR_CACHE) + { + + /* Adjust the number of cache sectors downward. If this is insufficient, + the FX_MAX_SECTOR_CACHE constant in FX_API.H must be changed and the FileX + library must be rebuilt. */ + media_ptr -> fx_media_sector_cache_size = FX_MAX_SECTOR_CACHE; + } + + /* Otherwise, everything is okay. Initialize the data structures for managing the + logical sector cache. */ + i = (UINT)media_ptr -> fx_media_sector_cache_size; + cache_entry_ptr = media_ptr -> fx_media_sector_cache; + while (i--) + { + + /* Initialize each of the cache entries. */ + cache_entry_ptr -> fx_cached_sector_memory_buffer = (UCHAR *)memory_ptr; + cache_entry_ptr -> fx_cached_sector = (~(ULONG64)0); + cache_entry_ptr -> fx_cached_sector_buffer_dirty = FX_FALSE; + cache_entry_ptr -> fx_cached_sector_valid = FX_FALSE; + cache_entry_ptr -> fx_cached_sector_next_used = cache_entry_ptr + 1; + + /* Move to the next cache sector entry. */ + cache_entry_ptr++; + + /* Update the memory pointer to the next buffer slot. */ + memory_ptr = (VOID *)(((UCHAR *)memory_ptr) + media_ptr -> fx_media_bytes_per_sector); + } + + /* Backup to the last cache entry to set its next pointer to NULL. */ + cache_entry_ptr--; + cache_entry_ptr -> fx_cached_sector_next_used = FX_NULL; + + /* Remember the last memory address used by the caching logic. */ + media_ptr -> fx_media_sector_cache_end = ((UCHAR *)memory_ptr) - 1; + + /* Setup the head pointer of the list. */ + media_ptr -> fx_media_sector_cache_list_ptr = media_ptr -> fx_media_sector_cache; + + /* Setup the bit map that keeps track of the valid hashed cache logical sectors. */ + media_ptr -> fx_media_sector_cache_hashed_sector_valid = 0; + + /* Clear the counter of the number of outstanding dirty sectors. */ + media_ptr -> fx_media_sector_cache_dirty_count = 0; + + /* Determine if the logical sector cache should be managed by the hash function + instead of the linear search. The cache must be a power of 2 that is between the + minimum and maximum cache size. */ + if ((media_ptr -> fx_media_sector_cache_size >= FX_SECTOR_CACHE_HASH_ENABLE) && + ((media_ptr -> fx_media_sector_cache_size ^ (media_ptr -> fx_media_sector_cache_size - 1)) == + (media_ptr -> fx_media_sector_cache_size | (media_ptr -> fx_media_sector_cache_size - 1)))) + { + + + /* Set the logical sector cache hash flag. When this flag is set, the logical + sector cache is accessed with a hash function instead of a linear search. */ + media_ptr -> fx_media_sector_cache_hashed = FX_TRUE; + media_ptr -> fx_media_sector_cache_hash_mask = + ((media_ptr -> fx_media_sector_cache_size / FX_SECTOR_CACHE_DEPTH) - 1); + } + else + { + + /* Clear the logical sector cache flag. */ + media_ptr -> fx_media_sector_cache_hashed = FX_FALSE; + } +#else + media_ptr -> fx_media_memory_buffer = memory_ptr; +#endif /* FX_DISABLE_CACHE */ + +#ifndef FX_DISABLE_FORCE_MEMORY_OPERATION + /* Initialize the FAT cache entry array. */ + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + + /* Clear entry in the FAT cache. */ + media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_dirty = 0; + } + + /* Initialize the secondary FAT update map. */ + for (i = 0; i < FX_FAT_MAP_SIZE; i++) + { + + /* Clear bit map entry for secondary FAT update. */ + media_ptr -> fx_media_fat_secondary_update_map[i] = 0; + } +#endif /* FX_DISABLE_FORCE_MEMORY_OPERATION */ + + + /* Root_sector_start has been computed */ + media_ptr -> fx_media_root_sector_start = media_ptr -> fx_media_reserved_sectors + + (media_ptr -> fx_media_number_of_FATs * + media_ptr -> fx_media_sectors_per_FAT); + + /* Calculate the number of directory sectors. */ + media_ptr -> fx_media_root_sectors = + ((media_ptr -> fx_media_root_directory_entries * FX_DIR_ENTRY_SIZE) + + media_ptr -> fx_media_bytes_per_sector - 1) / + media_ptr -> fx_media_bytes_per_sector; + + /* Calculate the starting data sector. */ + media_ptr -> fx_media_data_sector_start = media_ptr -> fx_media_root_sector_start + + media_ptr -> fx_media_root_sectors; + + /* Calculate the total number of clusters. */ + media_ptr -> fx_media_total_clusters = (ULONG)((media_ptr -> fx_media_total_sectors - media_ptr -> fx_media_data_sector_start) / + media_ptr -> fx_media_sectors_per_cluster); + + /* Determine if a 12-bit FAT is in use. */ + if (media_ptr -> fx_media_total_clusters < FX_12_BIT_FAT_SIZE) + { + + /* Yes, 12-bit FAT is present. Set flag accordingly. */ + media_ptr -> fx_media_12_bit_FAT = FX_TRUE; + media_ptr -> fx_media_32_bit_FAT = FX_FALSE; + + /* No additional information sector in FAT12. */ + media_ptr -> fx_media_FAT32_additional_info_sector = 0; + + /* Set FAT last and FAT reserved. */ + media_ptr -> fx_media_fat_reserved = FX_RESERVED_1; + media_ptr -> fx_media_fat_last = FX_LAST_CLUSTER_2; + } + else if (media_ptr -> fx_media_total_clusters < FX_16_BIT_FAT_SIZE) + { + + /* A 16-bit FAT is present. Set flag accordingly. */ + media_ptr -> fx_media_12_bit_FAT = FX_FALSE; + media_ptr -> fx_media_32_bit_FAT = FX_FALSE; + + /* No additional information sector in FAT16. */ + media_ptr -> fx_media_FAT32_additional_info_sector = 0; + + /* Set FAT last and FAT reserved. */ + media_ptr -> fx_media_fat_reserved = FX_RESERVED_1; + media_ptr -> fx_media_fat_last = FX_LAST_CLUSTER_2; + } + else + { + + /* Yes, a 32-bit FAT is present. */ + media_ptr -> fx_media_12_bit_FAT = FX_FALSE; + media_ptr -> fx_media_32_bit_FAT = FX_TRUE; + + /* Save the additional information sector FAT32. This was read from the boot + sector earlier in this routine. */ + media_ptr -> fx_media_FAT32_additional_info_sector = additional_info_sector; + + /* Set FAT last and FAT reserved. */ + media_ptr -> fx_media_fat_reserved = FX_RESERVED_1_32; + media_ptr -> fx_media_fat_last = FX_LAST_CLUSTER_2_32; + } + + /* Determine if a 32-bit FAT is present. If so, calculate the size of the root directory (since + it is variable in FAT32. */ + if (media_ptr -> fx_media_32_bit_FAT == FX_TRUE) + { + + /* Root First cluster starts from at least cluster 2, or higher. */ + if (media_ptr -> fx_media_root_cluster_32 < FX_FAT_ENTRY_START) + { + return(FX_MEDIA_INVALID); + } + + /* Calculate logical number of root dir sector. */ + media_ptr -> fx_media_root_sector_start = media_ptr -> fx_media_data_sector_start + + (media_ptr -> fx_media_root_cluster_32 - FX_FAT_ENTRY_START) * + media_ptr -> fx_media_sectors_per_cluster; + + /* Calculate maximum possible value for fx_media_root_directory_entries */ + i = 0; + for (cluster_number = media_ptr -> fx_media_root_cluster_32;;) + { + + status = _fx_utility_FAT_entry_read(media_ptr, cluster_number, &FAT_entry); + i++; + /* Determine if the read was successful. */ + if (status != FX_SUCCESS) + { + + /* Build the "uninitialize" I/O driver request. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_UNINIT; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_UNINIT, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Call the specified I/O driver with the uninitialize request. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + return(FX_FAT_READ_ERROR); + } + + if ((cluster_number == FAT_entry) || (i > media_ptr -> fx_media_total_clusters)) + { + + /* Build the "uninitialize" I/O driver request. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_UNINIT; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_UNINIT, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Call the specified I/O driver with the uninitialize request. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + return(FX_FAT_READ_ERROR); + } + if (FAT_entry >= FX_RESERVED_1_32) + { + break; + } + cluster_number = FAT_entry; + } + + /* Calculate the number of directory entries. */ + media_ptr -> fx_media_root_directory_entries = (i * media_ptr -> fx_media_sectors_per_cluster * + media_ptr -> fx_media_bytes_per_sector) / FX_DIR_ENTRY_SIZE; + } + +#ifndef FX_DISABLE_FORCE_MEMORY_OPERATION + /* Calculate the number of available clusters. */ + media_ptr -> fx_media_available_clusters = 0; + + /* Set the cluster search start to an invalid value. */ + media_ptr -> fx_media_cluster_search_start = 0; +#endif /* FX_DISABLE_FORCE_MEMORY_OPERATION */ + + /* Determine if there is 32-bit FAT additional information sector. */ + if (media_ptr -> fx_media_FAT32_additional_info_sector) + { + + UCHAR *buffer_ptr; + ULONG signature; + + + /* Yes, read the FAT32 additional information sector to get the available cluster count and + the hint for the first available cluster. */ + +#ifndef FX_DISABLE_CACHE + /* Setup a pointer to the first cached entry's buffer. */ + buffer_ptr = (media_ptr -> fx_media_sector_cache_list_ptr) -> fx_cached_sector_memory_buffer; + + /* Invalidate this cache entry. */ + (media_ptr -> fx_media_sector_cache_list_ptr) -> fx_cached_sector = (~((ULONG64) 0)); + (media_ptr -> fx_media_sector_cache_list_ptr) -> fx_cached_sector_valid = FX_FALSE; +#else + buffer_ptr = media_ptr -> fx_media_memory_buffer; + media_ptr -> fx_media_memory_buffer_sector = (ULONG64)-1; +#endif /* FX_DISABLE_CACHE */ + + /* Read the FAT32 additional information sector from the device. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_READ; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = buffer_ptr; + media_ptr -> fx_media_driver_logical_sector = media_ptr -> fx_media_FAT32_additional_info_sector; + media_ptr -> fx_media_driver_sectors = 1; + media_ptr -> fx_media_driver_sector_type = FX_DIRECTORY_SECTOR; + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of driver read sector(s) requests. */ + media_ptr -> fx_media_driver_read_requests++; +#endif + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_READ, media_ptr, media_ptr -> fx_media_FAT32_additional_info_sector, 1, buffer_ptr, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Invoke the driver to read the FAT32 additional information sector. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Determine if the FAT32 sector was read correctly. */ + if (media_ptr -> fx_media_driver_status == FX_SUCCESS) + { + + /* Yes, setup a pointer into the FAT32 additional information sector. */ + buffer_ptr = media_ptr -> fx_media_driver_buffer; + + /* Pickup the first signature long word. */ + signature = _fx_utility_32_unsigned_read(&buffer_ptr[0]); + + /* Determine if the signature is correct. */ + if (signature == 0x41615252) + { + + /* Yes, the first signature is correct, now pickup the next signature. */ + signature = _fx_utility_32_unsigned_read(&buffer_ptr[484]); + + /* Determine if this signature is correct. */ + if (signature == 0x61417272) + { + + /* Yes, we have a good FAT32 additional information sector. */ + + /* Pickup the current available cluster count on the media. */ + media_ptr -> fx_media_available_clusters = _fx_utility_32_unsigned_read(&buffer_ptr[488]); + + /* Initialize the last reported available cluster count to the same value. */ + media_ptr -> fx_media_FAT32_additional_info_last_available = media_ptr -> fx_media_available_clusters; + + /* Pickup the hint for the starting free cluster search. */ + media_ptr -> fx_media_cluster_search_start = _fx_utility_32_unsigned_read(&buffer_ptr[492]); + + /* Perform a quick sanity check on the available cluster count and the starting free + cluster search. */ + if ((media_ptr -> fx_media_available_clusters > media_ptr -> fx_media_total_clusters) || + (media_ptr -> fx_media_cluster_search_start > media_ptr -> fx_media_total_clusters + FX_FAT_ENTRY_START) || + (media_ptr -> fx_media_cluster_search_start < FX_FAT_ENTRY_START)) + { + + /* Something is wrong, clear the available cluster count and search so the regular processing + is used. */ + media_ptr -> fx_media_available_clusters = 0; + media_ptr -> fx_media_cluster_search_start = 0; + + /* We don't invalidate the additional info sector here because only the data is bad. */ + } + } + else + { + + /* Signature is bad, invalidate the additional info sector. */ + media_ptr -> fx_media_FAT32_additional_info_sector = 0; + } + } + else + { + + /* Signature is bad, invalidate the additional info sector. */ + media_ptr -> fx_media_FAT32_additional_info_sector = 0; + } + } + else + { + + /* IO error trying to read additional information sector, invalidate the additional info sector. */ + media_ptr -> fx_media_FAT32_additional_info_sector = 0; + } + } + + /* Search the media to find the first available cluster as well as the total + available clusters. */ + + /* Determine what type of FAT is present. */ + if (media_ptr -> fx_media_12_bit_FAT) + { + + /* A 12-bit FAT is present. Utilize the FAT entry read utility to pickup + each FAT entry's contents. */ + + /* Loop to read each cluster entry in the first FAT. */ + for (cluster_number = FX_FAT_ENTRY_START; + cluster_number < (media_ptr -> fx_media_total_clusters) + FX_FAT_ENTRY_START; + cluster_number++) + { + + /* Read a FAT entry. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster_number, &FAT_entry); + + /* Determine if the read was successful. */ + if (status != FX_SUCCESS) + { + + /* Build the "uninitialize" I/O driver request. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_UNINIT; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_UNINIT, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Call the specified I/O driver with the uninitialize request. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + return(FX_FAT_READ_ERROR); + } + + /* Now determine if the FAT entry is available. */ + if (FAT_entry == FX_FREE_CLUSTER) + { + + /* Increment the number of available clusters. */ + media_ptr -> fx_media_available_clusters++; + + /* Determine if the starting free cluster has been found yet. */ + if (media_ptr -> fx_media_cluster_search_start == 0) + { + + /* Remember the first free cluster to start further searches from. */ + media_ptr -> fx_media_cluster_search_start = cluster_number; + } + } + } + } + else if (media_ptr -> fx_media_available_clusters == 0) + { + + /* A 16 or 32-bit FAT is present. Read directly into the logical sector + cache memory to optimize I/O on larger devices. Since we are looking for + values of zero, endian issues are not important. */ + + /* Invalidate the current logical sector cache. */ + _fx_utility_logical_sector_flush(media_ptr, ((ULONG64) 1), (ULONG64) (media_ptr -> fx_media_total_sectors), FX_TRUE); + + /* Reset the memory pointer. */ + media_ptr -> fx_media_memory_buffer = original_memory_ptr; + + /* Loop through all FAT sectors in the primary FAT. The first two entries are + examined in this loop, but they are always unavailable. */ + cluster_number = 0; +#ifndef FX_DISABLE_CACHE + for (i = 0; i < media_ptr -> fx_media_sectors_per_FAT; i = i + media_ptr -> fx_media_sector_cache_size) + { + + /* Calculate the starting next FAT sector. */ + FAT_sector = media_ptr -> fx_media_reserved_sectors + i; + + /* Calculate how many sectors to read. */ + FAT_read_sectors = media_ptr -> fx_media_sectors_per_FAT - i; + + /* Determine if there is not enough memory to read the remaining FAT sectors. */ + if (FAT_read_sectors > media_ptr -> fx_media_sector_cache_size) + { + FAT_read_sectors = media_ptr -> fx_media_sector_cache_size; + } +#else + for (i = 0; i < media_ptr -> fx_media_sectors_per_FAT; i++) + { + + /* Calculate the starting next FAT sector. */ + FAT_sector = media_ptr -> fx_media_reserved_sectors + i; + + /* Calculate how many sectors to read. */ + FAT_read_sectors = 1; +#endif /* FX_DISABLE_CACHE */ + + /* Read the FAT sectors directly from the driver. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_READ; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = media_ptr -> fx_media_memory_buffer; + media_ptr -> fx_media_driver_logical_sector = FAT_sector; + media_ptr -> fx_media_driver_sectors = FAT_read_sectors; + media_ptr -> fx_media_driver_sector_type = FX_FAT_SECTOR; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_READ, media_ptr, FAT_sector, FAT_read_sectors, media_ptr -> fx_media_memory_buffer, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Invoke the driver to read the FAT sectors. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Determine if the read was successful. */ + if (media_ptr -> fx_media_driver_status != FX_SUCCESS) + { + + /* Build the "uninitialize" I/O driver request. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_UNINIT; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_UNINIT, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Call the specified I/O driver with the uninitialize request. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + return(FX_FAT_READ_ERROR); + } + + /* Calculate the number of bytes in the buffer. */ + bytes_in_buffer = (media_ptr -> fx_media_bytes_per_sector * FAT_read_sectors); + + /* Walk through the sector cache memory to search for available clusters and the first + available if not already found. */ + for (j = 0; j < bytes_in_buffer;) + { + + /* Check for a 32-bit FAT. */ + if (media_ptr -> fx_media_32_bit_FAT) + { + + /* Pickup 32-bit FAT entry. */ + FAT_entry = *((ULONG *)&(media_ptr -> fx_media_memory_buffer[j])); + + /* Advance to next FAT entry. */ + j = j + 4; + } + else + { + + /* Process a 16-bit FAT entry. */ + FAT_entry = (((ULONG)(media_ptr -> fx_media_memory_buffer[j])) & 0xFF) | + ((((ULONG)(media_ptr -> fx_media_memory_buffer[j + 1])) & 0xFF) << 8); + + /* Advance to next FAT entry. */ + j = j + 2; + } + + /* Determine if the FAT entry is free. */ + if (FAT_entry == FX_FREE_CLUSTER) + { + + /* Entry is free, increment available clusters. */ + media_ptr -> fx_media_available_clusters++; + + /* Determine if the starting free cluster has been found yet. */ + if (media_ptr -> fx_media_cluster_search_start == 0) + { + + /* Remember the first free cluster to start further searches from. */ + media_ptr -> fx_media_cluster_search_start = cluster_number; + } + } + + /* Increment the cluster number. */ + cluster_number++; + + /* Determine if we have reviewed all FAT entries. */ + if (cluster_number >= (media_ptr -> fx_media_total_clusters + FX_FAT_ENTRY_START)) + { + + /* Yes, we have looked at all the FAT entries. */ + + /* Ensure that the outer loop terminates as well. */ + i = media_ptr -> fx_media_sectors_per_FAT; + break; + } + } + } + } + + /* If there were no free clusters, just set the search pointer to the + first cluster number. */ + if (media_ptr -> fx_media_cluster_search_start == 0) + { + media_ptr -> fx_media_cluster_search_start = FX_FAT_ENTRY_START; + } + + /* Setup the current working directory fields to default to the root + directory. */ + media_ptr -> fx_media_default_path.fx_path_directory.fx_dir_entry_name = + media_ptr -> fx_media_default_path.fx_path_name_buffer; + media_ptr -> fx_media_default_path.fx_path_directory.fx_dir_entry_short_name[0] = 0; + media_ptr -> fx_media_default_path.fx_path_directory.fx_dir_entry_name[0] = 0; + media_ptr -> fx_media_default_path.fx_path_string[0] = (CHAR)0; + media_ptr -> fx_media_default_path.fx_path_string[FX_MAXIMUM_PATH - 1] = (CHAR)0; + media_ptr -> fx_media_default_path.fx_path_current_entry = 0; + +#ifndef FX_MEDIA_DISABLE_SEARCH_CACHE + + /* Invalidate the previously found directory entry. */ + media_ptr -> fx_media_last_found_name[0] = 0; +#endif + +#ifndef FX_DISABLE_FORCE_MEMORY_OPERATION + /* Initialize the opened file linked list and associated counter. */ + media_ptr -> fx_media_opened_file_list = FX_NULL; + media_ptr -> fx_media_opened_file_count = 0; +#endif /* FX_DISABLE_FORCE_MEMORY_OPERATION */ + + /* Create the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD + +#ifndef FX_DONT_CREATE_MUTEX + + /* Create ThreadX mutex for protection. */ + tx_mutex_create(&(media_ptr -> fx_media_protect), "FileX Media Mutex", TX_NO_INHERIT); +#endif + +#endif + +#ifdef FX_DONT_CREATE_MUTEX + + /* Load the media ID field in the media control block. This allows the FX_PROTECT + call to succeed. */ + media_ptr -> fx_media_id = (ULONG)FX_MEDIA_ID; + + /* Protect against other threads accessing the media. */ + FX_PROTECT +#endif + + /* Lockout interrupts. */ + FX_DISABLE_INTS + + /* At this point, the media has been opened successfully. Place the + media on the linked list of currently opened media. */ + + /* Load the media ID field in the media control block. */ + media_ptr -> fx_media_id = (ULONG)FX_MEDIA_ID; + + /* Place the thread on the list of opened media. First, + check for an empty list. */ + if (_fx_system_media_opened_ptr) + { + + /* Pickup tail pointer. */ + tail_ptr = _fx_system_media_opened_ptr -> fx_media_opened_previous; + + /* Place the new media in the list. */ + _fx_system_media_opened_ptr -> fx_media_opened_previous = media_ptr; + tail_ptr -> fx_media_opened_next = media_ptr; + + /* Setup this media's opened links. */ + media_ptr -> fx_media_opened_previous = tail_ptr; + media_ptr -> fx_media_opened_next = _fx_system_media_opened_ptr; + } + else + { + + /* The opened media list is empty. Add the media to empty list. */ + _fx_system_media_opened_ptr = media_ptr; + media_ptr -> fx_media_opened_next = media_ptr; + media_ptr -> fx_media_opened_previous = media_ptr; + } + + /* Increment the opened media counter. */ + _fx_system_media_opened_count++; + + /* Invoke media open callback. */ + if (media_ptr -> fx_media_open_notify) + { + media_ptr -> fx_media_open_notify(media_ptr); + } + + /* Restore interrupts. */ + FX_RESTORE_INTS + +#ifdef FX_DONT_CREATE_MUTEX + + /* Release media protection. */ + FX_UNPROTECT +#endif + + /* Return a successful status. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_media_open_notify_set.c b/common/src/fx_media_open_notify_set.c new file mode 100644 index 0000000..0a383d0 --- /dev/null +++ b/common/src/fx_media_open_notify_set.c @@ -0,0 +1,82 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_media_open_notify_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the notify function called when media is open. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* media_open_notify Notify function called when */ +/* media is open */ +/* */ +/* OUTPUT */ +/* */ +/* FX_SUCCESS */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_media_open_notify_set(FX_MEDIA *media_ptr, VOID (*media_open_notify)(FX_MEDIA *media)) +{ + + /* Set the notify function. */ + media_ptr -> fx_media_open_notify = media_open_notify; + + /* Return successful status. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_media_read.c b/common/src/fx_media_read.c new file mode 100644 index 0000000..3e853f0 --- /dev/null +++ b/common/src/fx_media_read.c @@ -0,0 +1,127 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_media.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_media_read PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads the specified raw logical sector from the */ +/* specified media. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* logical_sector Logical sector to read */ +/* buffer_ptr Pointer to caller's buffer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_logical_sector_read Logical sector read utility */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_media_read(FX_MEDIA *media_ptr, ULONG logical_sector, VOID *buffer_ptr) +{ + +UINT status; + +#ifdef TX_ENABLE_EVENT_TRACE +TX_TRACE_BUFFER_ENTRY *trace_event; +ULONG trace_timestamp; +#endif + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_reads++; +#endif + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_MEDIA_READ, media_ptr, logical_sector, buffer_ptr, 0, FX_TRACE_MEDIA_EVENTS, &trace_event, &trace_timestamp) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Read the logical sector. */ + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) logical_sector, buffer_ptr, ((ULONG) 1), FX_DATA_SECTOR); + +#ifdef TX_ENABLE_EVENT_TRACE + + /* Check for successful status. */ + if (status == FX_SUCCESS) + { + + /* Update the trace event with the bytes read. */ + FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_MEDIA_READ, 0, 0, 0, media_ptr -> fx_media_bytes_per_sector) + } +#endif + + /* Release media protection. */ + FX_UNPROTECT + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fx_media_space_available.c b/common/src/fx_media_space_available.c new file mode 100644 index 0000000..7b619aa --- /dev/null +++ b/common/src/fx_media_space_available.c @@ -0,0 +1,107 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_media.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_media_space_available PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the number of bytes available in the */ +/* specified media. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* available_bytes_ptr Pointer to available bytes */ +/* destination */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* _fx_media_extended_space_available Actual space available service*/ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_media_space_available(FX_MEDIA *media_ptr, ULONG *available_bytes_ptr) +{ + +UINT status; +ULONG64 available_bytes; + + /* Call actual media space available service. */ + status = _fx_media_extended_space_available(media_ptr, &available_bytes); + + /* Check status. */ + if (status == FX_SUCCESS) + { + + /* Determine if more than 4GB available. */ + if (available_bytes > 0x00000000FFFFFFFF) + { + + /* Yes, we must have more than 4GB available... report the maximum we can fit + in 32bits, which is 4GB. */ + *available_bytes_ptr = 0xFFFFFFFF; + } + else + { + *available_bytes_ptr = (ULONG)(available_bytes); + } + } + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fx_media_volume_get.c b/common/src/fx_media_volume_get.c new file mode 100644 index 0000000..50d1dfc --- /dev/null +++ b/common/src/fx_media_volume_get.c @@ -0,0 +1,89 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_media.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_media_volume_get PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads the volume name stored in the media's boot */ +/* record or root directory. */ +/* */ +/* Note, this API is deprecated as fx_media_volume_get_extended should */ +/* be used. The maximum written size to volume_name could be 12. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* volume_name Pointer to destination for */ +/* the volume name (maximum */ +/* 11 characters + NULL) */ +/* volume_source Source of volume */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_media_volume_get_extended Actual media volume get */ +/* service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_media_volume_get(FX_MEDIA *media_ptr, CHAR *volume_name, UINT volume_source) +{ + + /* Call the extended version with 12 bytes volume name length. */ + return(_fx_media_volume_get_extended(media_ptr, volume_name, 12, volume_source)); +} + diff --git a/common/src/fx_media_volume_get_extended.c b/common/src/fx_media_volume_get_extended.c new file mode 100644 index 0000000..4b3c39b --- /dev/null +++ b/common/src/fx_media_volume_get_extended.c @@ -0,0 +1,323 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_media.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_media_volume_get_extended PORTABLE C */ +/* 6.1.11 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads the volume name stored in the media's boot */ +/* record or root directory. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* volume_name Pointer to destination for */ +/* the volume name (maximum */ +/* 11 characters + NULL) */ +/* volume_name_buffer_length Buffer length for volume_name */ +/* volume_source Source of volume */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_logical_sector_read Read directory sector */ +/* _fx_directory_entry_read Directory entry read */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* 04-25-2022 Bhupendra Naphade Modified comment(s), and */ +/* updated check for */ +/* volume name, */ +/* resulting in version 6.1.11 */ +/* */ +/**************************************************************************/ +UINT _fx_media_volume_get_extended(FX_MEDIA *media_ptr, CHAR *volume_name, UINT volume_name_buffer_length, UINT volume_source) +{ + +UINT status, offset; +ULONG i; +INT j; + +FX_DIR_ENTRY dir_entry; + + + /* Clear the volume name. */ + volume_name[0] = FX_NULL; + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_MEDIA_VOLUME_GET, media_ptr, volume_name, volume_source, 0, FX_TRACE_MEDIA_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + + /* Ensure the volume name is NULL initially. */ + volume_name[0] = FX_NULL; + + if (volume_source == FX_DIRECTORY_SECTOR) + { + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer; + + /* Clear the short name string. */ + dir_entry.fx_dir_entry_short_name[0] = 0; + + /* Attempt to find the volume name in the root directory. */ + i = 0; + do + { + + /* Read an entry from the root directory. */ + status = _fx_directory_entry_read(media_ptr, FX_NULL, &i, &dir_entry); + + /* Check for error status. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return to caller. */ + return(status); + } + + /* Check for a volume name. */ + if ((dir_entry.fx_dir_entry_attributes & FX_VOLUME) && ((UCHAR)dir_entry.fx_dir_entry_name[0] != (UCHAR)FX_DIR_ENTRY_FREE)) + { + + /* Yes, we have found a previously set volume name. */ + break; + } + + /* Move to next directory entry. */ + i++; + } while (i < media_ptr -> fx_media_root_directory_entries); + + /* Determine if a volume entry has been found. */ + if (i < media_ptr -> fx_media_root_directory_entries) + { + + /* Read the logical directory sector. */ + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) dir_entry.fx_dir_entry_log_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR); + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return error code. */ + return(status); + } + + /* Offset to volume label entry. */ + offset = dir_entry.fx_dir_entry_byte_offset; + + + /* Skip trailing space characters of volume name. */ + for (j = 10; j >= 0; j--) + { + + /* Check for space character. */ + if (media_ptr -> fx_media_memory_buffer[offset + (UINT)j] != (UCHAR)' ') + { + + /* Last character found. */ + break; + } + } + + /* Check if the buffer is too short for the name. */ + if (j >= (INT)volume_name_buffer_length - 1) + { + + /* Buffer too short, return error. */ + status = FX_BUFFER_ERROR; + + /* Set character count to fit for the buffer. */ + j = (INT)volume_name_buffer_length - 2; + } + + /* NULL terminate the volume name. */ + volume_name[j + 1] = FX_NULL; + + /* Pickup the remaining characters of the volume name from the boot sector. */ + for (; j >= 0; j--) + { + + /* Pickup byte of volume name. */ + volume_name[j] = (CHAR)media_ptr -> fx_media_memory_buffer[offset + (UINT)j]; + } + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the completion status. */ + return(status); + } + } + + /* Read volume name from boot record. */ + /* Read the logical directory sector 0 - we just do this to get a memory_buffer pointer */ + status = _fx_utility_logical_sector_read(media_ptr, ((ULONG64) 0), + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DATA_SECTOR); + + /* Check the return status. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of driver read boot sector requests. */ + media_ptr -> fx_media_driver_boot_read_requests++; +#endif + + /* Build the driver request to read the boot record. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_BOOT_READ; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = media_ptr -> fx_media_memory_buffer; + media_ptr -> fx_media_driver_sectors = 1; + media_ptr -> fx_media_driver_sector_type = FX_BOOT_SECTOR; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_BOOT_READ, media_ptr, media_ptr -> fx_media_memory_buffer, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Invoke the driver to read the boot sector. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Determine if the request is successful. */ + if (media_ptr -> fx_media_driver_status) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* An error occurred in the driver. */ + return(media_ptr -> fx_media_driver_status); + } + + /* Calculate the offset to the volume name based on the type of FAT. */ + if (media_ptr -> fx_media_32_bit_FAT) + { + + /* FAT32 offset to volume name. */ + offset = FX_VOLUME_LABEL_32; + } + else + { + + /* FAT12/16 offset to volume name. */ + offset = FX_VOLUME_LABEL; + } + + /* Skip trailing space characters of volume name. */ + for (j = 10; j >= 0; j--) + { + + /* Check for space character. */ + if (media_ptr -> fx_media_memory_buffer[offset + (UINT)j] != (UCHAR)' ') + { + + /* Last character found. */ + break; + } + } + + /* Check if the buffer is too short for the name. */ + if (j >= (INT)volume_name_buffer_length - 1) + { + + /* Buffer too short, return error. */ + status = FX_BUFFER_ERROR; + + /* Set character count to fit for the buffer. */ + j = (INT)volume_name_buffer_length - 2; + } + + /* NULL terminate the volume name. */ + volume_name[j + 1] = FX_NULL; + + /* Pickup the remaining characters of the volume name from the boot sector. */ + for (; j >= 0; j--) + { + + /* Pickup byte of volume name. */ + volume_name[j] = (CHAR)media_ptr -> fx_media_memory_buffer[offset + (UINT)j]; + } + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the completion status. */ + return(status); +} + diff --git a/common/src/fx_media_volume_set.c b/common/src/fx_media_volume_set.c new file mode 100644 index 0000000..b296908 --- /dev/null +++ b/common/src/fx_media_volume_set.c @@ -0,0 +1,411 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_directory.h" +#include "fx_media.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_media_volume_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the volume name to the name supplied by the */ +/* caller. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* volume_name New volume name */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_entry_read Read a directory entry */ +/* _fx_utility_logical_sector_read Read directory sector */ +/* _fx_utility_logical_sector_write Write directory sector */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_media_volume_set(FX_MEDIA *media_ptr, CHAR *volume_name) +{ + +ULONG i, j; +FX_DIR_ENTRY dir_entry, dir_entry1; +UINT status, offset; +UCHAR *work_ptr; +CHAR alpha; + + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + dir_entry.fx_dir_entry_log_sector = 0; + dir_entry.fx_dir_entry_byte_offset = 0; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_MEDIA_VOLUME_SET, media_ptr, volume_name, 0, 0, FX_TRACE_MEDIA_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + + /* First, check for an invalid volume name. */ + if (volume_name[0] == 0) + { + + /* Yes, volume name is invalid. Return an error. */ + return(FX_INVALID_NAME); + } + + /* Read the logical directory sector 0 - we just do this to get a memory_buffer pointer */ + status = _fx_utility_logical_sector_read(media_ptr, ((ULONG64) 0), + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DATA_SECTOR); + + /* Check the read status. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of driver read boot sector requests. */ + media_ptr -> fx_media_driver_boot_read_requests++; +#endif + + /* Build a driver request to read the boot record. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_BOOT_READ; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = media_ptr -> fx_media_memory_buffer; + media_ptr -> fx_media_driver_sectors = 1; + media_ptr -> fx_media_driver_sector_type = FX_BOOT_SECTOR; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_BOOT_READ, media_ptr, media_ptr -> fx_media_memory_buffer, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Invoke the driver to read the boot sector. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Determine if the request is successful. */ + if (media_ptr -> fx_media_driver_status) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* An error occurred in the driver. */ + return(media_ptr -> fx_media_driver_status); + } + + /* Calculate the offset based on the FAT present. */ + if (media_ptr -> fx_media_32_bit_FAT) + { + + /* FAT32 is present. */ + offset = FX_VOLUME_LABEL_32; + } + else + { + + /* FAT12/16 is present. */ + offset = FX_VOLUME_LABEL; + } + + /* Loop to store the volume name. */ + for (i = 0; volume_name[i]; i++) + { + + /* Have we reached the end? */ + if (i == 11) + { + + break; + } + + /* Pickup volume name byte. */ + alpha = volume_name[i]; + + /* Determine if alpha needs to be converted to upper case. */ + if ((alpha >= 'a') && (alpha <= 'z')) + { + + /* Convert alpha to upper case. */ + alpha = (CHAR)((INT)alpha - 0x20); + } + + /* Store a byte of the volume name. */ + media_ptr -> fx_media_memory_buffer[offset + i] = (UCHAR)alpha; + } + + /* Now pad with spaces. */ + for (; i < 11; i++) + { + + /* Append space character to volume name. */ + media_ptr -> fx_media_memory_buffer[offset + i] = 0x20; + } + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of driver write boot sector requests. */ + media_ptr -> fx_media_driver_boot_write_requests++; +#endif + + /* Write the boot sector with the new volume name. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_BOOT_WRITE; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = media_ptr -> fx_media_memory_buffer; + media_ptr -> fx_media_driver_sectors = 1; + media_ptr -> fx_media_driver_sector_type = FX_BOOT_SECTOR; + + /* Set the system write flag since we are writing the boot sector. */ + media_ptr -> fx_media_driver_system_write = FX_TRUE; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_BOOT_WRITE, media_ptr, media_ptr -> fx_media_memory_buffer, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Invoke the driver to write the boot sector. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Clear the system write flag. */ + media_ptr -> fx_media_driver_system_write = FX_FALSE; + + /* Determine if the request is successful. */ + if (media_ptr -> fx_media_driver_status) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* An error occurred in the driver. */ + return(media_ptr -> fx_media_driver_status); + } + + /* Setup pointer to media name buffer. */ + dir_entry1.fx_dir_entry_name = media_ptr -> fx_media_name_buffer; + + /* Clear the short name string. */ + dir_entry1.fx_dir_entry_short_name[0] = 0; + + /* Now we need to find the copy of the volume name in the root directory. */ + i = 0; + j = media_ptr -> fx_media_root_directory_entries + 1; + do + { + + /* Read an entry from the root directory. */ + status = _fx_directory_entry_read(media_ptr, FX_NULL, &i, &dir_entry1); + + /* Check for error status. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return to caller. */ + return(status); + } + + /* Determine if this is an empty entry. */ + if ((dir_entry1.fx_dir_entry_name[0] == (CHAR)FX_DIR_ENTRY_FREE) && (dir_entry1.fx_dir_entry_short_name[0] == 0)) + { + + /* Yes, this is free entry. Is it the first? */ + if (i < j) + { + + /* Yes, first free entry - remember it. */ + dir_entry = dir_entry1; + j = i; + } + } + /* Determine if the directory entries are exhausted. */ + else if (dir_entry1.fx_dir_entry_name[0] == FX_DIR_ENTRY_DONE) + { + + /* Yes, this we are at the end of the directory. Have there + been any other free entries? */ + if (i < j) + { + + /* No, we need to remember this as the free entry. */ + dir_entry = dir_entry1; + j = i; + } + break; + } + /* Check for a volume name. */ + else if (dir_entry1.fx_dir_entry_attributes & FX_VOLUME) + { + + /* Yes, we have found a previously set volume name - use this entry. */ + dir_entry = dir_entry1; + j = i; + break; + } + + /* Move to next directory entry. */ + i++; + } while (i < media_ptr -> fx_media_root_directory_entries); + + /* Determine if a volume entry was not found and there are no more empty slots. */ + if (i == media_ptr -> fx_media_root_directory_entries) + { + + /* Determine if there was a free or previous volume name. */ + if (j == (media_ptr -> fx_media_root_directory_entries + 1)) + { + + /* No, nothing was available in the root directory. */ + + /* Release media protection. */ + FX_UNPROTECT + + /* No, existing volume name or space in the root directly was found, return an error. */ + return(FX_MEDIA_INVALID); + } + } + + /* Now set the volume name and attribute. */ + + /* Read the logical directory sector. */ + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) dir_entry.fx_dir_entry_log_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_BOOT_SECTOR); + + /* Check the status of reading the directory entry. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* Calculate pointer into directory buffer. */ + work_ptr = media_ptr -> fx_media_memory_buffer + + (UINT)dir_entry.fx_dir_entry_byte_offset; + + /* Copy the volume name into the directory entry. */ + for (i = 0; volume_name[i]; i++) + { + + /* Have we reached the end? */ + if (i == 11) + { + + break; + } + + /* Pickup volume name byte. */ + alpha = volume_name[i]; + + /* Determine if alpha needs to be converted to upper case. */ + if ((alpha >= 'a') && (alpha <= 'z')) + { + + /* Convert alpha to upper case. */ + alpha = (CHAR)((INT)alpha - 0x20); + } + + /* Store volume name. */ + work_ptr[i] = (UCHAR)alpha; + } + + /* Pad with space characters. */ + for (; i < 11; i++) + { + work_ptr[i] = 0x20; + } + + /* Set the appropriate attributes. */ + work_ptr[11] = FX_VOLUME | FX_ARCHIVE; + + /* Set the other parts of the volume entry. */ + + /* Clear the hi word of cluster. */ + work_ptr[20] = 0; + work_ptr[21] = 0; + + /* Clear the low word of cluster. */ + work_ptr[26] = 0; + work_ptr[27] = 0; + + /* Clear the file size. */ + work_ptr[28] = 0; + work_ptr[29] = 0; + work_ptr[30] = 0; + work_ptr[31] = 0; + + /* Write the directory sector to the media. */ + status = _fx_utility_logical_sector_write(media_ptr, (ULONG64) dir_entry.fx_dir_entry_log_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the status. */ + return(status); +} + diff --git a/common/src/fx_media_write.c b/common/src/fx_media_write.c new file mode 100644 index 0000000..1693377 --- /dev/null +++ b/common/src/fx_media_write.c @@ -0,0 +1,138 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_media.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_media_write PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes the specified raw logical sector to the */ +/* specified media. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* logical_sector Logical sector to read */ +/* buffer_ptr Pointer to caller's buffer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_logical_sector_write Write logical sector utility */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_media_write(FX_MEDIA *media_ptr, ULONG logical_sector, VOID *buffer_ptr) +{ + +UINT status; + +#ifdef TX_ENABLE_EVENT_TRACE +TX_TRACE_BUFFER_ENTRY *trace_event; +ULONG trace_timestamp; +#endif + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of times this service has been called. */ + media_ptr -> fx_media_writes++; +#endif + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_MEDIA_WRITE, media_ptr, logical_sector, buffer_ptr, 0, FX_TRACE_MEDIA_EVENTS, &trace_event, &trace_timestamp) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Check for write protect at the media level (set by driver). */ + if (media_ptr -> fx_media_driver_write_protect) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return write protect error. */ + return(FX_WRITE_PROTECT); + } + + /* Write the logical sector. */ + status = _fx_utility_logical_sector_write(media_ptr, (ULONG64) logical_sector, buffer_ptr, ((ULONG) 1), FX_DATA_SECTOR); + +#ifdef TX_ENABLE_EVENT_TRACE + + /* Check for successful status. */ + if (status == FX_SUCCESS) + { + + /* Update the trace event with the bytes written. */ + FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_MEDIA_WRITE, 0, 0, 0, media_ptr -> fx_media_bytes_per_sector) + } +#endif + + /* Release media protection. */ + FX_UNPROTECT + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fx_partition_offset_calculate.c b/common/src/fx_partition_offset_calculate.c new file mode 100644 index 0000000..f1f3d69 --- /dev/null +++ b/common/src/fx_partition_offset_calculate.c @@ -0,0 +1,605 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Application Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_utility.h" + + +/* Define internal data structures. */ + +typedef struct FX_MEDIA_PARTITION_STRUCT +{ + ULONG fx_media_part_start; + ULONG fx_media_part_size; +} FX_MEDIA_PARTITION; + +/* Define internal partition constants. */ + +#ifndef FX_MAX_PARTITION_COUNT +#define FX_MAX_PARTITION_COUNT 16 +#endif /* FX_MAX_PARTITION_COUNT */ + +#define FX_PARTITION_TABLE_OFFSET 446 +#define FX_PARTITION_ENTRY_SIZE 16 +#define FX_PARTITION_TYPE_OFFSET 4 +#define FX_PARTITION_LBA_OFFSET 8 +#define FX_PARTITION_SECTORS_OFFSET 12 + +#define FX_PARTITION_TYPE_FREE 0x00 +#define FX_PARTITION_TYPE_EXTENDED 0x05 +#define FX_PARTITION_TYPE_EXTENDED_LBA 0x0F + + +/* Define function prototypes for the partition table parsing application + utility. */ + +UINT _fx_partition_offset_calculate(void *partition_sector, UINT partition, + ULONG *partition_start, ULONG *partition_size); +UINT _fx_utility_partition_get(FX_MEDIA_PARTITION *partition_table, + UINT *count, ULONG sector, UCHAR *sector_buffer); +UINT _fx_partition_offset_calculate_extended(FX_MEDIA *media_ptr, void *partition_sector, UINT partition, + ULONG *partition_start, ULONG *partition_size); + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_partition_offset_calculate PORTABLE C */ +/* 6.1.6 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calculates the sector offset to the specified */ +/* partition. The buffer containing the partition table is also */ +/* supplied to this function. If the buffer supplied is a boot */ +/* record (which could be the case in non-partition systems), this */ +/* function returns an offset of zero, the total sectors, and a */ +/* successful status indicating that the buffer supplied is the boot */ +/* record. Otherwise, if a partition is found, this function returns */ +/* the sector offset to its boot record along with a successful */ +/* status. If the specified partition is not found or the buffer is */ +/* not a partition table or boot record, this function returns an */ +/* error. */ +/* */ +/* Note: Empty partitions have a FX_SUCCESS return code, however their */ +/* starting sector is FX_NULL and the size returned is 0. */ +/* */ +/* INPUT */ +/* */ +/* partition_sector Pointer to buffer containing */ +/* either the partition table */ +/* or the boot sector */ +/* partition Desired partition */ +/* partition_start Return partition start */ +/* partition_size Return partition size */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_partition_get Actual partition parsing */ +/* routine */ +/* */ +/* CALLED BY */ +/* */ +/* Application Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* 04-02-2021 William E. Lamie Modified comment(s), */ +/* ignored signature check for */ +/* no partition situation, */ +/* resulting in version 6.1.6 */ +/* */ +/**************************************************************************/ +UINT _fx_partition_offset_calculate(void *partition_sector, UINT partition, + ULONG *partition_start, ULONG *partition_size) +{ + +FX_MEDIA_PARTITION partition_table[4]; +UINT count; +ULONG64 total_sectors; +UCHAR *partition_sector_ptr; + + + /* Setup working pointer and initialize count. */ + partition_sector_ptr = partition_sector; + count = 0; + + /* Check for a real boot sector instead of a partition table. */ + if ((partition_sector_ptr[0] == 0xe9) || ((partition_sector_ptr[0] == 0xeb) && (partition_sector_ptr[2] == 0x90))) + { + + /* Yes, a real boot sector could be present. */ + + /* See if there are good values for sectors per FAT. */ + if (partition_sector_ptr[0x16] || partition_sector_ptr[0x17] || partition_sector_ptr[0x24] || partition_sector_ptr[0x25] || partition_sector_ptr[0x26] || partition_sector_ptr[0x27]) + { + + /* There are values for sectors per FAT. */ + + /* Determine if there is a total sector count. */ + total_sectors = 0; + + if (partition_sector_ptr[0x13] || partition_sector_ptr[0x14]) + { + + /* Calculate the total sectors, FAT12/16. */ + total_sectors = (((ULONG) partition_sector_ptr[0x14]) << 8) | ((ULONG) partition_sector_ptr[0x13]); + } + else if (partition_sector_ptr[0x20] || partition_sector_ptr[0x21] || partition_sector_ptr[0x22] || partition_sector_ptr[0x23]) + { + + /* Calculate the total sectors, FAT32. */ + total_sectors = (((ULONG) partition_sector_ptr[0x23]) << 24) | + (((ULONG) partition_sector_ptr[0x22]) << 16) | + (((ULONG) partition_sector_ptr[0x21]) << 8) | + ((ULONG) partition_sector_ptr[0x20]); + } + + /* Determine if there is a total sector count. */ + if (total_sectors) + { + + if (partition_start != FX_NULL) + { + /* Return an offset of 0, size of boot record, and a successful status. */ + *partition_start = 0; + } + + /* Determine if the total sectors is required. */ + if (partition_size != FX_NULL) + { + + /* Return the total sectors. */ + *partition_size = (ULONG)(total_sectors & 0xFFFFFFFF); + } + + /* Return success! */ + return(FX_SUCCESS); + } + } + } + + /* Check signature to make sure the buffer is valid. */ + if ((partition_sector_ptr[510] != 0x55) || (partition_sector_ptr[511] != 0xAA)) + { + + /* Invalid, return an error. */ + return(FX_NOT_FOUND); + } + + /* Not bootable, look for specific partition. */ + _fx_utility_partition_get(partition_table, &count, 0, partition_sector_ptr); + + /* Determine if return value is valid. */ + if (partition >= count) + { + + /* No, return an error. */ + return(FX_NOT_FOUND); + } + + /* Return the partition starting sector, if non-NULL. */ + if (partition_start != FX_NULL) + { + *partition_start = partition_table[partition].fx_media_part_start; + } + + /* Return the partition size, if non-NULL. */ + if (partition_size != FX_NULL) + { + *partition_size = partition_table[partition].fx_media_part_size; + } + + /* Return successful completion. */ + return(FX_SUCCESS); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_utility_partition_get PORTABLE C */ +/* 6.1.6 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function parses the partition sector and completes the */ +/* supplied partition entry structure. */ +/* */ +/* INPUT */ +/* */ +/* partition_table Pointer to partition table */ +/* count Number of partitions found */ +/* sector Base sector */ +/* sector_buffer Buffer containing partition */ +/* table */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _fx_partition_offset_calculate Calculate partition offset */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* 04-02-2021 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1.6 */ +/* */ +/**************************************************************************/ +UINT _fx_utility_partition_get(FX_MEDIA_PARTITION *partition_table, + UINT *count, ULONG sector, UCHAR *sector_buffer) +{ + +UINT i; +ULONG base_sector, value; + + /* This parameter has not been supported yet. */ + FX_PARAMETER_NOT_USED(sector); + + /* Initialize base sector. */ + base_sector = 0; + + for(i = 446; i <= 494; i+=16) + { + if (sector_buffer[i + 4] == 0) /* no partition entry here */ + { + + partition_table[*count].fx_media_part_start = 0; + partition_table[*count].fx_media_part_size = 0; + } + else + { + + value = (ULONG) sector_buffer[i + 8]; /* little endian start value */ + value = (((ULONG) sector_buffer[i + 9]) << 8) | value; + value = (((ULONG) sector_buffer[i + 10]) << 16) | value; + value = (((ULONG) sector_buffer[i + 11]) << 24) | value; + partition_table[*count].fx_media_part_start = value + base_sector; + + value = (ULONG) sector_buffer[i + 12]; /* little endian size value */ + value = (((ULONG) sector_buffer[i + 13]) << 8) | value; + value = (((ULONG) sector_buffer[i + 14]) << 16) | value; + value = (((ULONG) sector_buffer[i + 15]) << 24) | value; + partition_table[*count].fx_media_part_size = value; + } + + (*count)++; + } + + /* Return success. */ + return(FX_SUCCESS); +} + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_partition_offset_calculate_extended PORTABLE C */ +/* 6.2.0 */ +/* AUTHOR */ +/* */ +/* Xiuwen Cai, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calculates the sector offset to the specified */ +/* partition. The buffer containing the partition table is also */ +/* supplied to this function. If the buffer supplied is a boot */ +/* record (which could be the case in non-partition systems), this */ +/* function returns an offset of zero, the total sectors, and a */ +/* successful status indicating that the buffer supplied is the boot */ +/* record. Otherwise, if a partition is found, this function returns */ +/* the sector offset to its boot record along with a successful */ +/* status. If the specified partition is not found or the buffer is */ +/* not a partition table or boot record, this function returns an */ +/* error. */ +/* */ +/* Note: Empty partitions have a FX_NOT_FOUND return code. */ +/* Use partition index 0 to 3 for primary partition and index 4 to */ +/* FX_MAX_PARTITION_COUNT for extended partition. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* partition_sector Pointer to buffer containing */ +/* either the partition table */ +/* or the boot sector */ +/* partition Desired partition */ +/* partition_start Return partition start */ +/* partition_size Return partition size */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_16_unsigned_read Read a USHORT from memory */ +/* _fx_utility_32_unsigned_read Read a ULONG from memory */ +/* _fx_utility_64_unsigned_read Read a ULONG64 from memory */ +/* Media driver */ +/* */ +/* CALLED BY */ +/* */ +/* Application Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 10-31-2022 Xiuwen Cai Initial Version 6.2.0 */ +/* */ +/**************************************************************************/ +UINT _fx_partition_offset_calculate_extended(FX_MEDIA *media_ptr, void *partition_sector, UINT partition, + ULONG *partition_start, ULONG *partition_size) +{ + +ULONG64 total_sectors; +UCHAR *partition_sector_ptr; +UCHAR partition_type; +UINT i; +ULONG base_sector; +ULONG base_sector_extended; + + + /* Setup working pointer. */ + partition_sector_ptr = partition_sector; + + /* Check for a real boot sector instead of a partition table. */ + if ((partition_sector_ptr[0] == 0xe9) || ((partition_sector_ptr[0] == 0xeb) && (partition_sector_ptr[2] == 0x90))) + { + + /* Yes, a real boot sector could be present. */ + + /* See if there are good values for sectors per FAT. */ + if (partition_sector_ptr[0x16] || partition_sector_ptr[0x17] || partition_sector_ptr[0x24] || partition_sector_ptr[0x25] || partition_sector_ptr[0x26] || partition_sector_ptr[0x27]) + { + + /* There are values for sectors per FAT. */ + + /* Get the total sectors, FAT12/16. */ + total_sectors = _fx_utility_16_unsigned_read(&partition_sector_ptr[FX_SECTORS]); + + if (total_sectors == 0) + { + + /* Get the total sectors, FAT32. */ + total_sectors = _fx_utility_32_unsigned_read(&partition_sector_ptr[FX_HUGE_SECTORS]); + } + + /* Determine if there is a total sector count. */ + if (total_sectors) + { + + if (partition_start != FX_NULL) + { + /* Return an offset of 0, size of boot record, and a successful status. */ + *partition_start = 0; + } + + /* Determine if the total sectors is required. */ + if (partition_size != FX_NULL) + { + + /* Return the total sectors. */ + *partition_size = (ULONG)(total_sectors & 0xFFFFFFFF); + } + + /* Return success! */ + return(FX_SUCCESS); + } + } + } + + /* Check signature to make sure the buffer is valid. */ + if ((partition_sector_ptr[510] != FX_SIG_BYTE_1) || (partition_sector_ptr[511] != FX_SIG_BYTE_2)) + { + + /* Invalid, return an error. */ + return(FX_NOT_FOUND); + } + + /* Not bootable, look for specific partition. */ + + /* Check if primary partitions are addressed. */ + if (partition < 4) + { + + /* Get partition type. */ + partition_type = partition_sector_ptr[FX_PARTITION_TABLE_OFFSET + partition * FX_PARTITION_ENTRY_SIZE + FX_PARTITION_TYPE_OFFSET]; + + /* Check if there is a vaild partition. */ + if (partition_type != FX_PARTITION_TYPE_FREE) + { + + /* Return the partition starting sector, if non-NULL. */ + if (partition_start != FX_NULL) + { + *partition_start = _fx_utility_32_unsigned_read(&partition_sector_ptr[FX_PARTITION_TABLE_OFFSET + partition * FX_PARTITION_ENTRY_SIZE + FX_PARTITION_LBA_OFFSET]); + } + + /* Return the partition size, if non-NULL. */ + if (partition_size != FX_NULL) + { + *partition_size = _fx_utility_32_unsigned_read(&partition_sector_ptr[FX_PARTITION_TABLE_OFFSET + partition * FX_PARTITION_ENTRY_SIZE + FX_PARTITION_SECTORS_OFFSET]); + } + + /* Return success! */ + return(FX_SUCCESS); + } + else + { + + /* Not partition here. */ + return(FX_NOT_FOUND); + } + } + + /* Check for invalid parameter. */ + if (partition > FX_MAX_PARTITION_COUNT) + { + + /* Return error. */ + return(FX_NOT_FOUND); + } + + base_sector = 0; + + /* Loop to find the extended partition table. */ + for(i = FX_PARTITION_TABLE_OFFSET; i <= FX_PARTITION_TABLE_OFFSET + 3 * FX_PARTITION_ENTRY_SIZE; i += FX_PARTITION_ENTRY_SIZE) + { + + /* Get partition type. */ + partition_type = partition_sector_ptr[i + FX_PARTITION_TYPE_OFFSET]; + if (partition_type == FX_PARTITION_TYPE_EXTENDED || partition_type == FX_PARTITION_TYPE_EXTENDED_LBA) + { + base_sector = _fx_utility_32_unsigned_read(&partition_sector_ptr[i + FX_PARTITION_LBA_OFFSET]); + break; + } + } + + if (base_sector == 0) + { + + /* No extended partition. */ + return(FX_NOT_FOUND); + } + + base_sector_extended = base_sector; + + for (i = 4; i <= partition; i++) + { + + /* Read the partition sector from the device. Build the read sector + command. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_READ; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = partition_sector_ptr; + media_ptr -> fx_media_driver_logical_sector = base_sector; + media_ptr -> fx_media_driver_sectors = 1; + media_ptr -> fx_media_driver_sector_type = FX_UNKNOWN_SECTOR; + media_ptr -> fx_media_hidden_sectors = 0; + + /* Invoke the driver to read the sector. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Determine if the sector was read correctly. */ + if (media_ptr -> fx_media_driver_status != FX_SUCCESS) + { + + /* Return error. */ + return(FX_IO_ERROR); + } + + /* Check signature to make sure the sector is valid. */ + if ((partition_sector_ptr[510] != FX_SIG_BYTE_1) || (partition_sector_ptr[511] != FX_SIG_BYTE_2)) + { + + /* Invalid, return an error. */ + return(FX_NOT_FOUND); + } + + /* Determine if this is the desired partition. */ + if (i == partition) + { + + /* Get partition type. */ + partition_type = partition_sector_ptr[FX_PARTITION_TABLE_OFFSET + FX_PARTITION_TYPE_OFFSET]; + if (partition_type != FX_PARTITION_TYPE_FREE) + { + + /* Return the partition starting sector, if non-NULL. */ + if (partition_start != FX_NULL) + { + *partition_start = _fx_utility_32_unsigned_read(&partition_sector_ptr[FX_PARTITION_TABLE_OFFSET + FX_PARTITION_LBA_OFFSET]) + base_sector; + } + + /* Return the partition size, if non-NULL. */ + if (partition_size != FX_NULL) + { + *partition_size = _fx_utility_32_unsigned_read(&partition_sector_ptr[FX_PARTITION_TABLE_OFFSET + FX_PARTITION_SECTORS_OFFSET]); + } + + /* Return success! */ + return(FX_SUCCESS); + } + else + { + /* Not partition here. */ + return(FX_NOT_FOUND); + } + } + else + { + + /* Get partition type. */ + partition_type = partition_sector_ptr[FX_PARTITION_TABLE_OFFSET + FX_PARTITION_ENTRY_SIZE + FX_PARTITION_TYPE_OFFSET]; + if (partition_type == FX_PARTITION_TYPE_EXTENDED || partition_type == FX_PARTITION_TYPE_EXTENDED_LBA) + { + + /* Update sector number for next partition table. */ + base_sector = _fx_utility_32_unsigned_read(&partition_sector_ptr[FX_PARTITION_TABLE_OFFSET + FX_PARTITION_ENTRY_SIZE + FX_PARTITION_LBA_OFFSET]) + base_sector_extended; + } + else + { + /* No valid partition, get out of the loop. */ + break; + } + } + + } + + /* Return error. */ + return(FX_NOT_FOUND); +} \ No newline at end of file diff --git a/common/src/fx_ram_driver.c b/common/src/fx_ram_driver.c new file mode 100644 index 0000000..5a9e57f --- /dev/null +++ b/common/src/fx_ram_driver.c @@ -0,0 +1,370 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** RAM Disk Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#include "fx_api.h" + + +/* The RAM driver relies on the fx_media_format call to be made prior to + the fx_media_open call. The following call will format the default + 32KB RAM drive, with a sector size of 128 bytes per sector. + + fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + media_memory, // Media buffer pointer + sizeof(media_memory), // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + */ + + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_ram_driver PORTABLE C */ +/* 6.1.5 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point to the generic RAM disk driver */ +/* that is delivered with all versions of FileX. The format of the */ +/* RAM disk is easily modified by calling fx_media_format prior */ +/* to opening the media. */ +/* */ +/* This driver also serves as a template for developing FileX drivers */ +/* for actual devices. Simply replace the read/write sector logic with */ +/* calls to read/write from the appropriate physical device */ +/* */ +/* FileX RAM/FLASH structures look like the following: */ +/* */ +/* Physical Sector Contents */ +/* */ +/* 0 Boot record */ +/* 1 FAT Area Start */ +/* +FAT Sectors Root Directory Start */ +/* +Directory Sectors Data Sector Start */ +/* */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_memory_copy Copy sector memory */ +/* _fx_utility_16_unsigned_read Read 16-bit unsigned */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* 03-02-2021 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1.5 */ +/* */ +/**************************************************************************/ +VOID _fx_ram_driver(FX_MEDIA *media_ptr) +{ + +UCHAR *source_buffer; +UCHAR *destination_buffer; +UINT bytes_per_sector; + + + /* There are several useful/important pieces of information contained in + the media structure, some of which are supplied by FileX and others + are for the driver to setup. The following is a summary of the + necessary FX_MEDIA structure members: + + FX_MEDIA Member Meaning + + fx_media_driver_request FileX request type. Valid requests from + FileX are as follows: + + FX_DRIVER_READ + FX_DRIVER_WRITE + FX_DRIVER_FLUSH + FX_DRIVER_ABORT + FX_DRIVER_INIT + FX_DRIVER_BOOT_READ + FX_DRIVER_RELEASE_SECTORS + FX_DRIVER_BOOT_WRITE + FX_DRIVER_UNINIT + + fx_media_driver_status This value is RETURNED by the driver. + If the operation is successful, this + field should be set to FX_SUCCESS for + before returning. Otherwise, if an + error occurred, this field should be + set to FX_IO_ERROR. + + fx_media_driver_buffer Pointer to buffer to read or write + sector data. This is supplied by + FileX. + + fx_media_driver_logical_sector Logical sector FileX is requesting. + + fx_media_driver_sectors Number of sectors FileX is requesting. + + + The following is a summary of the optional FX_MEDIA structure members: + + FX_MEDIA Member Meaning + + fx_media_driver_info Pointer to any additional information + or memory. This is optional for the + driver use and is setup from the + fx_media_open call. The RAM disk uses + this pointer for the RAM disk memory + itself. + + fx_media_driver_write_protect The DRIVER sets this to FX_TRUE when + media is write protected. This is + typically done in initialization, + but can be done anytime. + + fx_media_driver_free_sector_update The DRIVER sets this to FX_TRUE when + it needs to know when clusters are + released. This is important for FLASH + wear-leveling drivers. + + fx_media_driver_system_write FileX sets this flag to FX_TRUE if the + sector being written is a system sector, + e.g., a boot, FAT, or directory sector. + The driver may choose to use this to + initiate error recovery logic for greater + fault tolerance. + + fx_media_driver_data_sector_read FileX sets this flag to FX_TRUE if the + sector(s) being read are file data sectors, + i.e., NOT system sectors. + + fx_media_driver_sector_type FileX sets this variable to the specific + type of sector being read or written. The + following sector types are identified: + + FX_UNKNOWN_SECTOR + FX_BOOT_SECTOR + FX_FAT_SECTOR + FX_DIRECTORY_SECTOR + FX_DATA_SECTOR + */ + + /* Process the driver request specified in the media control block. */ + switch (media_ptr -> fx_media_driver_request) + { + + case FX_DRIVER_READ: + { + + /* Calculate the RAM disk sector offset. Note the RAM disk memory is pointed to by + the fx_media_driver_info pointer, which is supplied by the application in the + call to fx_media_open. */ + source_buffer = ((UCHAR *)media_ptr -> fx_media_driver_info) + + ((media_ptr -> fx_media_driver_logical_sector + + media_ptr -> fx_media_hidden_sectors) * + media_ptr -> fx_media_bytes_per_sector); + + /* Copy the RAM sector into the destination. */ + _fx_utility_memory_copy(source_buffer, media_ptr -> fx_media_driver_buffer, + media_ptr -> fx_media_driver_sectors * + media_ptr -> fx_media_bytes_per_sector); + + /* Successful driver request. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break; + } + + case FX_DRIVER_WRITE: + { + + /* Calculate the RAM disk sector offset. Note the RAM disk memory is pointed to by + the fx_media_driver_info pointer, which is supplied by the application in the + call to fx_media_open. */ + destination_buffer = ((UCHAR *)media_ptr -> fx_media_driver_info) + + ((media_ptr -> fx_media_driver_logical_sector + + media_ptr -> fx_media_hidden_sectors) * + media_ptr -> fx_media_bytes_per_sector); + + /* Copy the source to the RAM sector. */ + _fx_utility_memory_copy(media_ptr -> fx_media_driver_buffer, destination_buffer, + media_ptr -> fx_media_driver_sectors * + media_ptr -> fx_media_bytes_per_sector); + + /* Successful driver request. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break; + } + + case FX_DRIVER_FLUSH: + { + + /* Return driver success. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break; + } + + case FX_DRIVER_ABORT: + { + + /* Return driver success. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break; + } + + case FX_DRIVER_INIT: + { + + /* FLASH drivers are responsible for setting several fields in the + media structure, as follows: + + media_ptr -> fx_media_driver_free_sector_update + media_ptr -> fx_media_driver_write_protect + + The fx_media_driver_free_sector_update flag is used to instruct + FileX to inform the driver whenever sectors are not being used. + This is especially useful for FLASH managers so they don't have + maintain mapping for sectors no longer in use. + + The fx_media_driver_write_protect flag can be set anytime by the + driver to indicate the media is not writable. Write attempts made + when this flag is set are returned as errors. */ + + /* Perform basic initialization here... since the boot record is going + to be read subsequently and again for volume name requests. */ + + /* Successful driver request. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break; + } + + case FX_DRIVER_UNINIT: + { + + /* There is nothing to do in this case for the RAM driver. For actual + devices some shutdown processing may be necessary. */ + + /* Successful driver request. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break; + } + + case FX_DRIVER_BOOT_READ: + { + + /* Read the boot record and return to the caller. */ + + /* Calculate the RAM disk boot sector offset, which is at the very beginning of + the RAM disk. Note the RAM disk memory is pointed to by the + fx_media_driver_info pointer, which is supplied by the application in the + call to fx_media_open. */ + source_buffer = (UCHAR *)media_ptr -> fx_media_driver_info; + + /* For RAM driver, determine if the boot record is valid. */ + if ((source_buffer[0] != (UCHAR)0xEB) || + ((source_buffer[1] != (UCHAR)0x34) && + (source_buffer[1] != (UCHAR)0x76)) || + (source_buffer[2] != (UCHAR)0x90)) + { + + /* Invalid boot record, return an error! */ + media_ptr -> fx_media_driver_status = FX_MEDIA_INVALID; + return; + } + + /* For RAM disk only, pickup the bytes per sector. */ + bytes_per_sector = _fx_utility_16_unsigned_read(&source_buffer[FX_BYTES_SECTOR]); + + + /* Ensure this is less than the media memory size. */ + if (bytes_per_sector > media_ptr -> fx_media_memory_size) + { + media_ptr -> fx_media_driver_status = FX_BUFFER_ERROR; + break; + } + + /* Copy the RAM boot sector into the destination. */ + _fx_utility_memory_copy(source_buffer, media_ptr -> fx_media_driver_buffer, + bytes_per_sector); + + /* Successful driver request. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break; + } + + case FX_DRIVER_BOOT_WRITE: + { + + /* Write the boot record and return to the caller. */ + + /* Calculate the RAM disk boot sector offset, which is at the very beginning of the + RAM disk. Note the RAM disk memory is pointed to by the fx_media_driver_info + pointer, which is supplied by the application in the call to fx_media_open. */ + destination_buffer = (UCHAR *)media_ptr -> fx_media_driver_info; + + /* Copy the RAM boot sector into the destination. */ + _fx_utility_memory_copy(media_ptr -> fx_media_driver_buffer, destination_buffer, + media_ptr -> fx_media_bytes_per_sector); + + /* Successful driver request. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break; + } + + default: + { + + /* Invalid driver request. */ + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + break; + } + } +} + diff --git a/common/src/fx_system_date_get.c b/common/src/fx_system_date_get.c new file mode 100644 index 0000000..2fe378b --- /dev/null +++ b/common/src/fx_system_date_get.c @@ -0,0 +1,119 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** System */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_system_date_get PORTABLE C */ +/* 6.1.7 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the current contents of the system date in */ +/* the fields specified by the caller. */ +/* */ +/* INPUT */ +/* */ +/* year Pointer to year */ +/* month Pointer to month */ +/* day Pointer to day */ +/* */ +/* OUTPUT */ +/* */ +/* FX_SUCCESS */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* 06-02-2021 William E. Lamie Modified comment(s), */ +/* checked null pointer, */ +/* resulting in version 6.1.7 */ +/* */ +/**************************************************************************/ +UINT _fx_system_date_get(UINT *year, UINT *month, UINT *day) +{ + +UINT date; + + + /* Get a copy of the current date. */ + date = _fx_system_date; + + /* Check to see if the year is required. */ + if (year) + { + + /* Pickup the year. */ + *year = ((date >> FX_YEAR_SHIFT) & FX_YEAR_MASK) + FX_BASE_YEAR; + } + + /* Check to see if the month is required. */ + if (month) + { + + /* Pickup the month. */ + *month = (date >> FX_MONTH_SHIFT) & FX_MONTH_MASK; + } + + /* Check to see if the day is required. */ + if (day) + { + + /* Pickup the day. */ + *day = date & FX_DAY_MASK; + } + + /* If trace is enabled, insert this event into the trace buffer. */ + if (year && month && day) + { + FX_TRACE_IN_LINE_INSERT(FX_TRACE_SYSTEM_DATE_GET, *year, *month, *day, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + } + + /* Return successful status. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_system_date_set.c b/common/src/fx_system_date_set.c new file mode 100644 index 0000000..c5971fb --- /dev/null +++ b/common/src/fx_system_date_set.c @@ -0,0 +1,88 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** System */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_system_date_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the system date to the date specified by the */ +/* caller. */ +/* */ +/* INPUT */ +/* */ +/* year Year (1980-2107) */ +/* month Month (1-12) */ +/* day Day (1-28/29/30/31) */ +/* */ +/* OUTPUT */ +/* */ +/* FX_SUCCESS */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_system_date_set(UINT year, UINT month, UINT day) +{ + + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_SYSTEM_DATE_SET, year, month, day, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Set the system date. */ + _fx_system_date = ((year - FX_BASE_YEAR) << FX_YEAR_SHIFT) | + (month << FX_MONTH_SHIFT) | day; + + /* Return successful status. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_system_initialize.c b/common/src/fx_system_initialize.c new file mode 100644 index 0000000..e7d7d6b --- /dev/null +++ b/common/src/fx_system_initialize.c @@ -0,0 +1,207 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** System */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Locate FileX control component data in this file. */ + +#define FX_SYSTEM_INIT + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_system_initialize PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the various control data structures for */ +/* the FileX System component. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* tx_timer_create Create system timer */ +/* */ +/* CALLED BY */ +/* */ +/* Application Initialization */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), and */ +/* added conditional to */ +/* disable build options, */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +VOID _fx_system_initialize(VOID) +{ + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_SYSTEM_INITIALIZE, 0, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Initialize the head pointer of the opened media list and the + number of opened media. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Initialize the time and date fields with their default values. */ + _fx_system_date = FX_INITIAL_DATE; + _fx_system_time = FX_INITIAL_TIME; + + /* Initialize the sector and FAT cache sizes. */ + _fx_system_media_max_sector_cache = FX_MAX_SECTOR_CACHE; + _fx_system_media_max_fat_cache = FX_MAX_FAT_CACHE; + + /* Create the FileX system timer. This is responsible for updating + the specified date and time at the rate specified by + FX_UPDATE_RATE_IN_TICKS. Note that the timer is not necessary for + regular FileX operation - it is only needed for accurate system + date and time stamps on files. */ + +#ifndef FX_NO_TIMER + tx_timer_create(&_fx_system_timer, "FileX System Timer", _fx_system_timer_entry, FX_TIMER_ID, + FX_UPDATE_RATE_IN_TICKS, FX_UPDATE_RATE_IN_TICKS, TX_AUTO_ACTIVATE); +#endif + +#ifndef FX_DISABLE_BUILD_OPTIONS + /* Setup the build options variables. */ + + /* Setup the first build options variable. */ + if (FX_MAX_LONG_NAME_LEN > 0xFF) + { + _fx_system_build_options_1 = _fx_system_build_options_1 | (((ULONG)0xFF) << 24); + } + else + { + _fx_system_build_options_1 = _fx_system_build_options_1 | (((ULONG)(FX_MAX_LONG_NAME_LEN & 0xFF)) << 24); + } + if (FX_MAX_LAST_NAME_LEN > 0xFF) + { + _fx_system_build_options_1 = _fx_system_build_options_1 | (((ULONG)0xFF) << 16); + } + else + { + _fx_system_build_options_1 = _fx_system_build_options_1 | (((ULONG)(FX_MAX_LAST_NAME_LEN & 0xFF)) << 24); + } + +#ifdef FX_NO_TIMER + _fx_system_build_options_1 = _fx_system_build_options_1 | (((ULONG)1) << 10); +#endif +#ifdef FX_SINGLE_THREAD + _fx_system_build_options_1 = _fx_system_build_options_1 | (((ULONG)1) << 9); +#endif +#ifdef FX_DONT_UPDATE_OPEN_FILES + _fx_system_build_options_1 = _fx_system_build_options_1 | (((ULONG)1) << 8); +#endif +#ifdef FX_MEDIA_DISABLE_SEARCH_CACHE + _fx_system_build_options_1 = _fx_system_build_options_1 | (((ULONG)1) << 7); +#endif +#ifdef FX_MEDIA_STATISTICS_DISABLE + _fx_system_build_options_1 = _fx_system_build_options_1 | (((ULONG)1) << 6); +#endif + +#ifdef FX_SINGLE_OPEN_LEGACY + _fx_system_build_options_1 = _fx_system_build_options_1 | (((ULONG)1) << 4); +#endif +#ifdef FX_RENAME_PATH_INHERIT + _fx_system_build_options_1 = _fx_system_build_options_1 | (((ULONG)1) << 3); +#endif +#ifdef FX_NO_LOCAL_PATH + _fx_system_build_options_1 = _fx_system_build_options_1 | (((ULONG)1) << 2); +#endif +#ifdef FX_FAULT_TOLERANT_DATA + _fx_system_build_options_1 = _fx_system_build_options_1 | (((ULONG)1) << 1); +#endif +#ifdef FX_FAULT_TOLERANT + _fx_system_build_options_1 = _fx_system_build_options_1 | ((ULONG)1); +#endif + + /* Setup the second build options variable. */ + if (FX_MAX_SECTOR_CACHE > ((ULONG)0xFFFF)) + { + _fx_system_build_options_2 = _fx_system_build_options_2 | (((ULONG)0xFFFF) << 16); + } + else + { + _fx_system_build_options_2 = _fx_system_build_options_2 | (((ULONG)FX_MAX_SECTOR_CACHE) << 16); + } + if (FX_FAT_MAP_SIZE > 0xFF) + { + _fx_system_build_options_2 = _fx_system_build_options_2 | (((ULONG)0xFF) << 8); + } + else + { + _fx_system_build_options_2 = _fx_system_build_options_2 | (((ULONG)FX_FAT_MAP_SIZE) << 8); + } + if (FX_MAX_FAT_CACHE > 0xFF) + { + _fx_system_build_options_2 = _fx_system_build_options_2 | ((ULONG)0xFF); + } + else + { + _fx_system_build_options_2 = _fx_system_build_options_2 | ((ULONG)FX_MAX_FAT_CACHE); + } + + /* Setup the third build options variable. */ + if (FX_UPDATE_RATE_IN_SECONDS > 0xFF) + { + _fx_system_build_options_3 = _fx_system_build_options_3 | (((ULONG)0xFF) << 16); + } + else + { + _fx_system_build_options_3 = _fx_system_build_options_3 | (((ULONG)FX_UPDATE_RATE_IN_SECONDS) << 16); + } + if (FX_UPDATE_RATE_IN_TICKS > ((ULONG)0xFFFF)) + { + _fx_system_build_options_3 = _fx_system_build_options_3 | ((ULONG)0xFFFF); + } + else + { + _fx_system_build_options_3 = _fx_system_build_options_3 | ((ULONG)FX_UPDATE_RATE_IN_TICKS); + } +#endif /* FX_DISABLE_BUILD_OPTIONS */ +} + diff --git a/common/src/fx_system_time_get.c b/common/src/fx_system_time_get.c new file mode 100644 index 0000000..1d4a215 --- /dev/null +++ b/common/src/fx_system_time_get.c @@ -0,0 +1,119 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** System */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_system_time_get PORTABLE C */ +/* 6.1.7 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the current contents of the system time in */ +/* the fields specified by the caller. */ +/* */ +/* INPUT */ +/* */ +/* hour Pointer to hour */ +/* minute Pointer to minute */ +/* second Pointer to second */ +/* */ +/* OUTPUT */ +/* */ +/* FX_SUCCESS */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* 06-02-2021 William E. Lamie Modified comment(s), */ +/* checked null pointer, */ +/* resulting in version 6.1.7 */ +/* */ +/**************************************************************************/ +UINT _fx_system_time_get(UINT *hour, UINT *minute, UINT *second) +{ + +UINT time; + + + /* Get a copy of the current system time. */ + time = _fx_system_time; + + /* Check to see if the hour is required. */ + if (hour) + { + + /* Pickup the hour. */ + *hour = (time >> FX_HOUR_SHIFT) & FX_HOUR_MASK; + } + + /* Check to see if the minute is required. */ + if (minute) + { + + /* Pickup the minute. */ + *minute = (time >> FX_MINUTE_SHIFT) & FX_MINUTE_MASK; + } + + /* Check to see if the second is required. */ + if (second) + { + + /* Pickup the second. */ + *second = (time & FX_SECOND_MASK) * 2; + } + + /* If trace is enabled, insert this event into the trace buffer. */ + if (hour && minute && second) + { + FX_TRACE_IN_LINE_INSERT(FX_TRACE_SYSTEM_TIME_GET, *hour, *minute, *second, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + } + + /* Return successful status. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_system_time_set.c b/common/src/fx_system_time_set.c new file mode 100644 index 0000000..eaa4659 --- /dev/null +++ b/common/src/fx_system_time_set.c @@ -0,0 +1,88 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** System */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_system_time_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function set the system time to the value specified by the */ +/* caller. */ +/* */ +/* INPUT */ +/* */ +/* hour Hour (0-23) */ +/* minute Minute (0-59) */ +/* second Second (0-59) */ +/* */ +/* OUTPUT */ +/* */ +/* FX_SUCCESS */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_system_time_set(UINT hour, UINT minute, UINT second) +{ + + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_SYSTEM_TIME_SET, hour, minute, second, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Set the new system time. */ + _fx_system_time = (hour << FX_HOUR_SHIFT) | + (minute << FX_MINUTE_SHIFT) | (second / 2); + + /* Return successful status. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_system_timer_entry.c b/common/src/fx_system_timer_entry.c new file mode 100644 index 0000000..1b2e27e --- /dev/null +++ b/common/src/fx_system_timer_entry.c @@ -0,0 +1,343 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** System */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_system_timer_entry PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is FileX system timer function. It is called at the */ +/* rate specified by FX_UPDATE_RATE_IN_SECONDS and is responsible for */ +/* maintaining both the system date and time. */ +/* */ +/* INPUT */ +/* */ +/* id Not used */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Initialization */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +VOID _fx_system_timer_entry(ULONG id) +{ + +UINT second; +UINT minute; +UINT hour; +UINT day; +UINT month; +UINT year; + + + /* Determine if the ID is valid. */ + if (id == FX_TIMER_ID) + { + + /* Break the current date time into separate fields for easier work! */ + second = (_fx_system_time & FX_SECOND_MASK) * 2; + minute = (_fx_system_time >> FX_MINUTE_SHIFT) & FX_MINUTE_MASK; + hour = (_fx_system_time >> FX_HOUR_SHIFT) & FX_HOUR_MASK; + day = _fx_system_date & FX_DAY_MASK; + month = (_fx_system_date >> FX_MONTH_SHIFT) & FX_MONTH_MASK; + year = ((_fx_system_date >> FX_YEAR_SHIFT) & FX_YEAR_MASK) + FX_BASE_YEAR; + + /* Now apply the "second" update. */ + second = second + FX_UPDATE_RATE_IN_SECONDS; + + /* Determine if we need to adjust the minute field. */ + if (second > FX_MAXIMUM_SECOND) + { + + /* Yes, we need to adjust the minute field. */ + minute = minute + second / 60; + second = second % 60; + + /* Determine if we need to adjust the hour field. */ + if (minute > FX_MAXIMUM_MINUTE) + { + + /* Yes, we need to adjust the hour field. */ + hour = hour + minute / 60; + minute = minute % 60; + + /* Determine if we need to adjust the day field. */ + if (hour > FX_MAXIMUM_HOUR) + { + + /* Yes, we need to adjust the day field. */ + hour = 0; + day++; + + /* Determine if we need to adjust the month field. */ + switch (month) + { + + case 1: /* January */ + { + + /* Check for end of the month. */ + if (day > 31) + { + + /* Move to next month. */ + day = 1; + month++; + } + break; + } + + case 2: /* February */ + { + + /* Check for leap year. We don't need to check for leap + century her (century years divisible by 400) since 2000 + is and this FAT format only supports years to 2107. */ + if ((year % 4) == 0) + { + + /* Leap year in February... check for 29 days + instead of 28. */ + if (day > 29) + { + + /* Adjust the month. */ + day = 1; + month++; + } + } + else + { + + if (day > 28) + { + + /* Adjust the month. */ + day = 1; + month++; + } + } + break; + } + + case 3: /* March */ + { + + /* Check for end of the month. */ + if (day > 31) + { + + /* Move to next month. */ + day = 1; + month++; + } + break; + } + + case 4: /* April */ + { + + /* Check for end of the month. */ + if (day > 30) + { + + /* Move to next month. */ + day = 1; + month++; + } + break; + } + + case 5: /* May */ + { + + /* Check for end of the month. */ + if (day > 31) + { + + /* Move to next month. */ + day = 1; + month++; + } + break; + } + + case 6: /* June */ + { + + /* Check for end of the month. */ + if (day > 30) + { + + /* Move to next month. */ + day = 1; + month++; + } + break; + } + + case 7: /* July */ + { + + /* Check for end of the month. */ + if (day > 31) + { + + /* Move to next month. */ + day = 1; + month++; + } + break; + } + + case 8: /* August */ + { + + /* Check for end of the month. */ + if (day > 31) + { + + /* Move to next month. */ + day = 1; + month++; + } + break; + } + + case 9: /* September */ + { + + /* Check for end of the month. */ + if (day > 30) + { + + /* Move to next month. */ + day = 1; + month++; + } + break; + } + + case 10: /* October */ + { + + /* Check for end of the month. */ + if (day > 31) + { + + /* Move to next month. */ + day = 1; + month++; + } + break; + } + + case 11: /* November */ + { + + /* Check for end of the month. */ + if (day > 30) + { + + /* Move to next month. */ + day = 1; + month++; + } + break; + } + + case 12: /* December */ + { + + /* Check for end of the month. */ + if (day > 31) + { + + /* Move to next month. */ + day = 1; + month = 1; + + /* Also move to next year. */ + year++; + + /* Check for a year that exceeds the representation + in this format. */ + if (year > FX_MAXIMUM_YEAR) + { + return; + } + } + break; + } + + default: /* Invalid month! */ + + return; /* Skip updating date/time! */ + } + } + } + } + + /* Now apply the new setting to the internal representation. */ + + /* Set the system date. */ + _fx_system_date = ((year - FX_BASE_YEAR) << FX_YEAR_SHIFT) | + (month << FX_MONTH_SHIFT) | day; + + /* Set the new system time. */ + _fx_system_time = (hour << FX_HOUR_SHIFT) | + (minute << FX_MINUTE_SHIFT) | (second / 2); + } +} + diff --git a/common/src/fx_trace_event_insert.c b/common/src/fx_trace_event_insert.c new file mode 100644 index 0000000..da9373d --- /dev/null +++ b/common/src/fx_trace_event_insert.c @@ -0,0 +1,146 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Trace */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifndef FX_SOURCE_CODE +#define FX_SOURCE_CODE +#endif + +#include "fx_api.h" + +#ifdef TX_ENABLE_EVENT_TRACE + + +/* Include necessary system files. */ + +#include "tx_trace.h" + + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_trace_event_insert PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts a FileX event into the current trace buffer. */ +/* */ +/* INPUT */ +/* */ +/* event_id User Event ID */ +/* info_field_1 First information field */ +/* info_field_2 First information field */ +/* info_field_3 First information field */ +/* info_field_4 First information field */ +/* current_event Current event pointer for */ +/* post event update */ +/* current_timestamp Timestamp for post event */ +/* update */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Internal FileX Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +VOID _fx_trace_event_insert(ULONG event_id, ULONG info_field_1, ULONG info_field_2, ULONG info_field_3, ULONG info_field_4, ULONG filter, TX_TRACE_BUFFER_ENTRY **current_event, ULONG *current_timestamp) +{ + +TX_INTERRUPT_SAVE_AREA + +TX_TRACE_BUFFER_ENTRY *event; +ULONG timestamp; + + + /* Disable interrupts. */ + TX_DISABLE + + /* Pickup the current event. */ + event = _tx_trace_buffer_current_ptr; + + /* Insert this event into the trace buffer. */ + TX_TRACE_IN_LINE_INSERT(event_id, info_field_1, info_field_2, info_field_3, info_field_4, filter); + + /* Initialize the timestamp to 0. */ + timestamp = 0; + + /* Determine if the event was inserted. */ + if (event) + { + + /* Was the event inserted? */ + if (event -> tx_trace_buffer_entry_event_id == event_id) + { + + /* Yes, the event was inserted in the event trace so pickup the timestamp. */ + timestamp = event -> tx_trace_buffer_entry_time_stamp; + } + else + { + + /* Event was not inserted, simply set the event pointer to NULL. */ + event = FX_NULL; + } + } + + /* Now determine if the caller requested the current event. */ + if (current_event) + { + + /* Yes, return the event pointer of potential subsequent update. */ + *current_event = event; + } + + /* Now determine if the current timestamp was requested. */ + if (current_timestamp) + { + + /* Yes, return the current timestamp. */ + *current_timestamp = timestamp; + } + + /* Restore interrupts. */ + TX_RESTORE +} + +#endif /* TX_ENABLE_EVENT_TRACE */ + diff --git a/common/src/fx_trace_event_update.c b/common/src/fx_trace_event_update.c new file mode 100644 index 0000000..c36fb7b --- /dev/null +++ b/common/src/fx_trace_event_update.c @@ -0,0 +1,134 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Trace */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifndef FX_SOURCE_CODE +#define FX_SOURCE_CODE +#endif + +#include "fx_api.h" + +#ifdef TX_ENABLE_EVENT_TRACE + + +/* Include necessary system files. */ + + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_trace_event_update PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts a FileX event into the current trace buffer. */ +/* */ +/* INPUT */ +/* */ +/* event Event pointer */ +/* timestamp Timestamp of the event */ +/* event_id User Event ID */ +/* info_field_1 First information field */ +/* info_field_2 First information field */ +/* info_field_3 First information field */ +/* info_field_4 First information field */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Internal FileX Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +VOID _fx_trace_event_update(TX_TRACE_BUFFER_ENTRY *event, ULONG timestamp, ULONG event_id, ULONG info_field_1, ULONG info_field_2, ULONG info_field_3, ULONG info_field_4) +{ + +TX_INTERRUPT_SAVE_AREA + + + /* Disable interrupts. */ + TX_DISABLE + + /* Determine if the event exists and is still the event originally inserted into the trace. */ + if ((event) && (event -> tx_trace_buffer_entry_event_id == event_id) && (event -> tx_trace_buffer_entry_time_stamp == timestamp)) + { + + /* Yes, update this trace entry based on the info input parameters. */ + + /* Check for info field 1 update. */ + if (info_field_1) + { + + /* Yes, update info field 1. */ + event -> tx_trace_buffer_entry_information_field_1 = info_field_1; + } + + /* Check for info field 2 update. */ + if (info_field_2) + { + + /* Yes, update info field 2. */ + event -> tx_trace_buffer_entry_information_field_2 = info_field_2; + } + + /* Check for info field 3 update. */ + if (info_field_3) + { + + /* Yes, update info field 3. */ + event -> tx_trace_buffer_entry_information_field_3 = info_field_3; + } + + /* Check for info field 4 update. */ + if (info_field_4) + { + + /* Yes, update info field 4. */ + event -> tx_trace_buffer_entry_information_field_4 = info_field_4; + } + } + /* Restore interrupts. */ + TX_RESTORE +} + + +#endif /* TX_ENABLE_EVENT_TRACE */ + diff --git a/common/src/fx_trace_object_register.c b/common/src/fx_trace_object_register.c new file mode 100644 index 0000000..a860ae9 --- /dev/null +++ b/common/src/fx_trace_object_register.c @@ -0,0 +1,99 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Trace */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifndef TX_SOURCE_CODE +#define TX_SOURCE_CODE +#endif + +#ifndef FX_SOURCE_CODE +#define FX_SOURCE_CODE +#endif + +#include "fx_api.h" + +#ifdef TX_ENABLE_EVENT_TRACE + + +/* Include necessary system files. */ + + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_trace_object_register PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function registers a FileX object in the trace registry. */ +/* */ +/* INPUT */ +/* */ +/* object_type Type of system object */ +/* object_ptr Address of system object */ +/* object_name Name of system object */ +/* parameter_1 Supplemental parameter 1 */ +/* parameter_2 Supplemental parameter 2 */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_trace_object_register Actual register function */ +/* */ +/* CALLED BY */ +/* */ +/* Application Initialization */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +VOID _fx_trace_object_register(UCHAR object_type, VOID *object_ptr, CHAR *object_name, ULONG parameter_1, ULONG parameter_2) +{ + +TX_INTERRUPT_SAVE_AREA + + + /* Disable interrupts. */ + TX_DISABLE + + /* Call actual object register function. */ + _tx_trace_object_register(object_type, object_ptr, object_name, parameter_1, parameter_2); + + /* Restore interrupts. */ + TX_RESTORE +} +#endif + diff --git a/common/src/fx_trace_object_unregister.c b/common/src/fx_trace_object_unregister.c new file mode 100644 index 0000000..17a22c2 --- /dev/null +++ b/common/src/fx_trace_object_unregister.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Trace */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifndef TX_SOURCE_CODE +#define TX_SOURCE_CODE +#endif + +#ifndef FX_SOURCE_CODE +#define FX_SOURCE_CODE +#endif + +#include "fx_api.h" + +#ifdef TX_ENABLE_EVENT_TRACE + + +/* Include necessary system files. */ + +#include "tx_trace.h" + + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_trace_object_unregister PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function unregisters a FileX object in the trace registry. */ +/* */ +/* INPUT */ +/* */ +/* object_ptr Address of system object */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_trace_object_unregister Actual unregister function */ +/* */ +/* CALLED BY */ +/* */ +/* FileX delete functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +VOID _fx_trace_object_unregister(VOID *object_ptr) +{ + +TX_INTERRUPT_SAVE_AREA + + + /* Disable interrupts. */ + TX_DISABLE + + /* Call actual object unregister function. */ + _tx_trace_object_unregister(object_ptr); + + /* Restore interrupts. */ + TX_RESTORE +} +#endif + diff --git a/common/src/fx_unicode_directory_create.c b/common/src/fx_unicode_directory_create.c new file mode 100644 index 0000000..2b29000 --- /dev/null +++ b/common/src/fx_unicode_directory_create.c @@ -0,0 +1,282 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Unicode */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_unicode.h" +#include "fx_directory.h" +#include "fx_utility.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_unicode_directory_create PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates the specified unicode directory. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to media */ +/* source_unicode_name Pointer to source unicode name*/ +/* source_unicode_length Unicode name length */ +/* short_name Designated short name */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_search Search directory */ +/* _fx_unicode_directory_entry_change Change unicode file name */ +/* _fx_unicode_directory_search Search for unicode name */ +/* _fx_directory_create Create a directory */ +/* _fx_fault_tolerant_transaction_start Start fault tolerant */ +/* transaction */ +/* _fx_fault_tolerant_transaction_end End fault tolerant transaction*/ +/* _fx_fault_tolerant_recover Recover FAT chain */ +/* _fx_fault_tolerant_reset_log_file Reset the log file */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_unicode_directory_create(FX_MEDIA *media_ptr, UCHAR *source_unicode_name, ULONG source_unicode_length, CHAR *short_name) +{ + +FX_DIR_ENTRY dir_entry; +UINT i, status; +ULONG temp_length; +UCHAR destination_shortname[13]; + + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Clear the short name string. */ + dir_entry.fx_dir_entry_short_name[0] = FX_NULL; + + /* Set destination shortname to null. */ + destination_shortname[0] = FX_NULL; + + /* Clear the return short name. */ + short_name[0] = FX_NULL; + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_UNICODE_DIRECTORY_CREATE, media_ptr, source_unicode_name, source_unicode_length, short_name, FX_TRACE_DIRECTORY_EVENTS, 0, 0) + + /* Protect media. */ + FX_PROTECT + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Start transaction. */ + _fx_fault_tolerant_transaction_start(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Check for write protect at the media level (set by driver). */ + if (media_ptr -> fx_media_driver_write_protect) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return write protect error. */ + return(FX_WRITE_PROTECT); + } + + /* Setup temporary length. */ + temp_length = source_unicode_length; + + /* Determine if the destination directory is already present. */ + status = _fx_unicode_directory_search(media_ptr, &dir_entry, destination_shortname, sizeof(destination_shortname), source_unicode_name, &temp_length, 0); + + /* Determine if the search was successful. */ + if (status == FX_SUCCESS) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(FX_ALREADY_CREATED); + } + + /* Okay, at this point we need to create a new long directory name that has enough space for the + eventual unicode directory name. */ + + /* Copy the characters from the unicode directory name and make sure they are + within the ASCII range. */ + _fx_unicode_temp_long_file_name[0] = 'z'; + for (i = 1; i < source_unicode_length; i++) + { + + /* Build temporary long file name. */ + _fx_unicode_temp_long_file_name[i] = (UCHAR)((INT)'0' + (i % 9)); + } + _fx_unicode_temp_long_file_name[i] = FX_NULL; + + /* Loop to try different temp long file names... if necessary. */ + do + { + + /* Create a new directory with the temp long file name. */ + status = _fx_directory_create(media_ptr, (CHAR *)_fx_unicode_temp_long_file_name); + + /* Determine if there was an error. */ + if (status == FX_ALREADY_CREATED) + { + + /* Adjust the name slightly and try again! */ + _fx_unicode_temp_long_file_name[0]--; + + /* Determine if it is outside the lower case boundary. */ + if (_fx_unicode_temp_long_file_name[0] < 0x61) + { + break; + } + } + } while (status == FX_ALREADY_CREATED); + + /* Determine if there was an error. */ + if (status) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return error. */ + return(status); + } + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Clear the short name string. */ + dir_entry.fx_dir_entry_short_name[0] = 0; + + /* Search the system for the supplied file name. */ + status = _fx_directory_search(media_ptr, (CHAR *)_fx_unicode_temp_long_file_name, &dir_entry, FX_NULL, FX_NULL); + + /* Determine if the search was successful. */ + if (status != FX_SUCCESS) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* We can now change the temporary long file name with the destination unicode name. */ + status = _fx_unicode_directory_entry_change(media_ptr, &dir_entry, source_unicode_name, source_unicode_length); + + /* Was this successful? */ + if (status == FX_SUCCESS) + { + + /* Yes, copy the short file name to the destination. */ + /* The new short name only have 8 characters, since we didn't include a dot in temp_long_file_name. */ + for (i = 0; i < FX_DIR_NAME_SIZE; i++) + { + + /* Copy a character. */ + short_name[i] = dir_entry.fx_dir_entry_short_name[i]; + + /* Are we done? */ + if (short_name[i] == FX_NULL) + { + break; + } + } + } + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + /* End transaction. */ + status = _fx_fault_tolerant_transaction_end(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release the protection. */ + FX_UNPROTECT + + /* Return completion status. */ + return(status); +} + diff --git a/common/src/fx_unicode_directory_entry_change.c b/common/src/fx_unicode_directory_entry_change.c new file mode 100644 index 0000000..2519b30 --- /dev/null +++ b/common/src/fx_unicode_directory_entry_change.c @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Unicode */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_unicode.h" +#include "fx_utility.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_unicode_directory_entry_change PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function changes the unicode name of a directory entry. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to media */ +/* entry_ptr Directory entry */ +/* unicode_name Destination unicode name */ +/* unicode_name_length Unicode name size */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_FAT_entry_read Read a FAT entry */ +/* _fx_utility_logical_sector_read Read a logical sector */ +/* _fx_utility_logical_sector_write Write a logical sector */ +/* _fx_fault_tolerant_add_dir_log Add directory redo log */ +/* */ +/* CALLED BY */ +/* */ +/* Unicode Utilities */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_unicode_directory_entry_change(FX_MEDIA *media_ptr, FX_DIR_ENTRY *entry_ptr, UCHAR *unicode_name, ULONG unicode_name_length) +{ + +UCHAR *work_ptr, *sector_base_ptr; +UINT status; +UINT i, j, k, u, card, lfn_entries; +UCHAR eof_marker; +ULONG logical_sector, relative_sector; +ULONG byte_offset; +ULONG cluster, next_cluster; + +#ifdef FX_ENABLE_FAULT_TOLERANT +UCHAR *changed_ptr; +UINT changed_size; +ULONG changed_offset; +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of directory entry write requests. */ + media_ptr -> fx_media_directory_entry_writes++; +#endif + + /* Pickup the byte offset of the entry. */ + byte_offset = entry_ptr -> fx_dir_entry_byte_offset; + + /* Pickup the logical sector of the entry. */ + logical_sector = (ULONG)entry_ptr -> fx_dir_entry_log_sector; + + /* Figure out where what cluster we are in. */ + if (logical_sector >= (ULONG)(media_ptr -> fx_media_data_sector_start)) + { + + /* Calculate the cluster that this logical sector is in. */ + cluster = (logical_sector - media_ptr -> fx_media_data_sector_start) / (media_ptr -> fx_media_sectors_per_cluster) + FX_FAT_ENTRY_START; + + /* Calculate the relative cluster. */ + relative_sector = logical_sector - (((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG)cluster - FX_FAT_ENTRY_START) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster))); + } + else + { + + /* Clear the cluster and the relative sector. */ + cluster = 0; + relative_sector = 0; + } + + /* Read the logical directory sector. */ + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) entry_ptr -> fx_dir_entry_log_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR); + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + + /* Setup a pointer into the buffer. */ + sector_base_ptr = (UCHAR *)media_ptr -> fx_media_memory_buffer; + work_ptr = sector_base_ptr + (UINT)entry_ptr -> fx_dir_entry_byte_offset; + + /* Initialize the unicode index. */ + u = 0; + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Initialize data for fault tolerant. */ + changed_ptr = work_ptr; + changed_size = 0; + changed_offset = entry_ptr -> fx_dir_entry_byte_offset; +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Check for a valid long name. */ + if ((0x40 & (*work_ptr))) + { + + /* Get the lower 5 bit containing the cardinality. */ + card = (*work_ptr & (UCHAR)0x1f); + lfn_entries = card; + + /* Loop through the file name. */ + for (j = 0; j < lfn_entries; j++) + { + + /* Clear the eof marker. */ + eof_marker = 0; + + /* Loop through file name fields. */ + for (i = 1, k = (26 * (card - 1)) & 0xFFFFFFFF; i < FX_DIR_ENTRY_SIZE; i += 2) + { + + /* Process relative to specific fields. */ + if ((i == 11) || (i == 26)) + { + continue; + } + + if (i == 13) + { + i = 12; + continue; + } + + /* Determine if the EOF marker is present. */ + if (eof_marker) + { + + work_ptr[i] = eof_marker; + work_ptr[i + 1] = eof_marker; + } + else if (k < (unicode_name_length * 2)) + { + + work_ptr[i] = unicode_name[k]; + work_ptr[i + 1] = unicode_name[k + 1]; + u = u + 2; + } + else if (k == (unicode_name_length * 2)) + { + + work_ptr[i] = 0; + work_ptr[i + 1] = 0; + + /* end of name, pad with 0xff. */ + eof_marker = (UCHAR)0xff; + } + + k = k + 2; + } + + /* Decrement the card. */ + card--; + + /* Setup pointers for the name write. */ + work_ptr += FX_DIR_ENTRY_SIZE; + byte_offset += FX_DIR_ENTRY_SIZE; + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Update changed_size. */ + changed_size += FX_DIR_ENTRY_SIZE; +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Determine if the write is within the current sector. */ + if (byte_offset >= media_ptr -> fx_media_bytes_per_sector) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled) + { + + /* Redirect this request to log file. */ + status = _fx_fault_tolerant_add_dir_log(media_ptr, (ULONG64) logical_sector, changed_offset, changed_ptr, changed_size); + } + else + { + + /* Write the current sector out. */ + status = _fx_utility_logical_sector_write(media_ptr, (ULONG64) logical_sector, + sector_base_ptr, ((ULONG) 1), FX_DIRECTORY_SECTOR); + } + + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } +#else /* FX_ENABLE_FAULT_TOLERANT */ + + /* Write the current sector out. */ + /* Note: Since the sector is in the cache after a sector read, therefore _fx_utility_logical_sector_write + always returns success when FX_ENABLE_FAULT_TOLERANT is not defined. In other words, the checking + on the return value is needed only when FX_ENABLE_FAULT_TOLERANT is defined. */ + _fx_utility_logical_sector_write(media_ptr, (ULONG64) logical_sector, + sector_base_ptr, ((ULONG) 1), FX_DIRECTORY_SECTOR); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Determine if we are in the root directory. */ + if (logical_sector >= (ULONG)(media_ptr -> fx_media_data_sector_start)) + { + + /* Determine the next sector of the directory entry. */ + if (relative_sector < (media_ptr -> fx_media_sectors_per_cluster - 1)) + { + + /* More sectors in this cluster. */ + + /* Simply increment the logical sector. */ + logical_sector++; + + /* Increment the relative sector. */ + relative_sector++; + } + else + { + + /* We need to move to the next cluster. */ + + /* Pickup the next cluster. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &next_cluster); + + /* Check for I/O error. */ + if (status != FX_SUCCESS) + { + + /* Return error code. */ + return(status); + } + + /* Copy next cluster to the current cluster. */ + cluster = next_cluster; + + /* Check the value of the new cluster - it must be a valid cluster number + or something is really wrong! */ + if ((cluster < FX_FAT_ENTRY_START) || (cluster > media_ptr -> fx_media_fat_reserved)) + { + + /* Send error message back to caller. */ + return(FX_FILE_CORRUPT); + } + + /* Setup the relative sector (this is zero for subsequent cluster. */ + relative_sector = 0; + + /* Calculate the next logical sector. */ + logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG)cluster - FX_FAT_ENTRY_START) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)); + } + } + else + { + + /* Increment the logical sector. */ + logical_sector++; + + /* Determine if the logical sector is valid. */ + if (logical_sector >= (ULONG)(media_ptr -> fx_media_data_sector_start)) + { + + /* We have exceeded the root directory. */ + + /* Send error message back to caller. */ + return(FX_FILE_CORRUPT); + } + } + + /* Read the next logical sector. */ + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) logical_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR); + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + + /* Move to the next sector buffer. */ + sector_base_ptr = media_ptr -> fx_media_memory_buffer; + + /* Setup new buffer pointers. */ + byte_offset = 0; + work_ptr = sector_base_ptr; + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Initialize data for fault tolerant. */ + changed_ptr = work_ptr; + changed_size = 0; + changed_offset = 0; +#endif /* FX_ENABLE_FAULT_TOLERANT */ + } + } + } + + /* Check for an error! */ + if (u != (unicode_name_length * 2)) + { + + /* Return an error! */ + return(FX_FILE_CORRUPT); + } + +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled) + { + + /* Redirect this request to log file. */ + status = _fx_fault_tolerant_add_dir_log(media_ptr, (ULONG64) logical_sector, changed_offset, changed_ptr, changed_size); + } + else + { +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Write the directory sector to the media. */ + status = _fx_utility_logical_sector_write(media_ptr, (ULONG64) logical_sector, + sector_base_ptr, ((ULONG) 1), FX_DIRECTORY_SECTOR); +#ifdef FX_ENABLE_FAULT_TOLERANT + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + return(status); +} + diff --git a/common/src/fx_unicode_directory_entry_read.c b/common/src/fx_unicode_directory_entry_read.c new file mode 100644 index 0000000..c4d2ae3 --- /dev/null +++ b/common/src/fx_unicode_directory_entry_read.c @@ -0,0 +1,748 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Unicode */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_unicode.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_unicode_directory_entry_read PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads a unicode directory entry. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to media */ +/* source_dir Pointer to source directory */ +/* entry_ptr Entry index in the directory */ +/* destination_ptr Destination directory */ +/* unicode_name Destination unicode name */ +/* unicode_size Destination unicode name size */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_FAT_entry_read Read a FAT entry */ +/* _fx_utility_logical_sector_read Read a logical sector */ +/* _fx_utility_16_unsigned_read Read a 2-byte value */ +/* _fx_utility_32_unsigned_read Read a 4-byte value */ +/* */ +/* CALLED BY */ +/* */ +/* Unicode Utilities */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_unicode_directory_entry_read(FX_MEDIA *media_ptr, FX_DIR_ENTRY *source_dir, + ULONG *entry_ptr, FX_DIR_ENTRY *destination_ptr, + UCHAR *unicode_name, ULONG *unicode_size) +{ + +UINT i, j, k, card, dotflag, get_short_name; +UINT number_of_lfns; +UINT status; +ULONG cluster, next_cluster = 0; +UINT relative_cluster; +UINT relative_sector; +ULONG logical_sector; +ULONG byte_offset; +ULONG bytes_per_cluster; +UCHAR *read_ptr; +CHAR *short_name_ptr; +ULONG entry = *entry_ptr; +UINT u; + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of directory entry read requests. */ + media_ptr -> fx_media_directory_entry_reads++; +#endif + + /* Calculate the byte offset of this directory entry. */ + byte_offset = entry * FX_DIR_ENTRY_SIZE; + + /* Determine if a sub-directory or FAT32 root directory is specified. */ + if ((source_dir) || (media_ptr -> fx_media_32_bit_FAT)) + { + + /* Yes, a sub-directory is present. */ + + /* Calculate the number of bytes per cluster. */ + bytes_per_cluster = ((ULONG)media_ptr -> fx_media_bytes_per_sector) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster); + + /* Check for invalid value. */ + if (bytes_per_cluster == 0) + { + + /* Invalid media, return error. */ + return(FX_MEDIA_INVALID); + } + + /* Now determine the relative cluster in the sub-directory file. */ + relative_cluster = (UINT)(byte_offset / bytes_per_cluster); + + /* Calculate the byte offset within the cluster. */ + byte_offset = byte_offset % bytes_per_cluster; + + /* Now figure out the relative sector within the cluster. */ + relative_sector = (UINT)(byte_offset / ((ULONG)media_ptr -> fx_media_bytes_per_sector)); + + /* Read the directory sector into the internal memory buffer. */ + + /* Determine if there is a sub-directory. */ + if (source_dir) + { + + /* Determine if this source directory has valid information from the previous call. */ + if ((source_dir -> fx_dir_entry_last_search_cluster) && + (source_dir -> fx_dir_entry_last_search_relative_cluster <= relative_cluster) && + (source_dir -> fx_dir_entry_last_search_log_sector == source_dir -> fx_dir_entry_log_sector) && + (source_dir -> fx_dir_entry_last_search_byte_offset == source_dir -> fx_dir_entry_byte_offset)) + { + + /* Use the previous information to start the search. */ + cluster = source_dir -> fx_dir_entry_last_search_cluster; + + /* Setup the relative cluster index to the saved relative cluster. */ + i = source_dir -> fx_dir_entry_last_search_relative_cluster; + + /* Clear the search cluster. It will be updated prior to successful return. */ + source_dir -> fx_dir_entry_last_search_cluster = 0; + } + else + { + + /* Nothing from the previous directory read, just setup the starting cluster to the + beginning of the sub-directory. */ + cluster = source_dir -> fx_dir_entry_cluster; + + /* Setup the relative cluster index to zero. */ + i = 0; + } + } + else + { + + /* No, setup the starting cluster to the FAT32 root cluster. */ + cluster = media_ptr -> fx_media_root_cluster_32; + + /* Setup the relative cluster index to zero. */ + i = 0; + } + + /* Loop to position to the appropriate cluster. */ + while (i < relative_cluster) + { + + /* Check the value of the new cluster - it must be a valid cluster number + or something is really wrong! */ + if ((cluster < FX_FAT_ENTRY_START) || (cluster > media_ptr -> fx_media_fat_reserved)) + { + + /* Send error message back to caller. */ + return(FX_FILE_CORRUPT); + } + + /* Read the next cluster. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &next_cluster); + + /* There is a potential for loop, but hardly anything can be done */ + + /* Check for I/O error. */ + if (status != FX_SUCCESS) + { + + /* Return error code. */ + return(status); + } + + /* Setup the actual cluster. */ + cluster = next_cluster; + + /* Increment the relative cluster number. */ + i++; + } + + /* At this point, the directory data sector needs to be read. */ + logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG)cluster - FX_FAT_ENTRY_START) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) + + relative_sector; + + /* Read the logical directory sector. */ + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) logical_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR); + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + + /* Return error code. */ + return(status); + } + + /* Calculate the byte offset within this sector. */ + byte_offset = byte_offset % media_ptr -> fx_media_bytes_per_sector; + } + else + { + + /* Read the entry from the root directory. */ + + /* Determine which sector the requested root directory entry is in. */ + logical_sector = (byte_offset / media_ptr -> fx_media_bytes_per_sector) + + (ULONG)media_ptr -> fx_media_root_sector_start; + + /* Read the logical directory sector. */ + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) logical_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR); + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + + /* Return error code. */ + return(status); + } + + /* Set the cluster and relative variables (not used in this case) to avoid any compiler + warnings. */ + relative_cluster = relative_sector = cluster = 0; + + /* Now calculate the byte offset into this sector. */ + byte_offset = byte_offset - + ((logical_sector - (ULONG)media_ptr -> fx_media_root_sector_start) * + media_ptr -> fx_media_bytes_per_sector); + } + + /* Setup a pointer into the buffer. */ + read_ptr = (UCHAR *)media_ptr -> fx_media_memory_buffer + (UINT)byte_offset; + + /* Save the logical sector and byte offset in the returned directory entry. */ + destination_ptr -> fx_dir_entry_log_sector = logical_sector; + destination_ptr -> fx_dir_entry_byte_offset = byte_offset; + + /* Clear the short file name information. */ + destination_ptr -> fx_dir_entry_long_name_shorted = 0; + destination_ptr -> fx_dir_entry_short_name[0] = 0; + + /* Setup short name pointer. */ + short_name_ptr = destination_ptr -> fx_dir_entry_name; + + /* Initialize the unicode index. */ + u = 0; + + /* Check if long file name exists. */ + get_short_name = 0; + if ((*(read_ptr + 11) == (UCHAR)FX_LONG_NAME) && (*read_ptr != (UCHAR)FX_DIR_ENTRY_FREE)) + { + + /* Collate the long name. */ + + + /* Save the number of LFN entries. */ + number_of_lfns = (UINT)(*read_ptr & (UCHAR)0x1f); + + if(number_of_lfns == 0) + { + /* Number of LFN cannot is 1-based. Therefore it cannot be zero. */ + return(FX_FILE_CORRUPT); + } + else + { + /* Pickup the file name length. */ + i = (number_of_lfns - 1) * FX_LONG_NAME_ENTRY_LEN; + } + + /* Check the file name size. */ + if (i >= (FX_MAX_LONG_NAME_LEN - 1)) + { + + /* Name is too big, shorten it. */ + get_short_name = 1; + destination_ptr -> fx_dir_entry_long_name_shorted = (UINT)(*read_ptr & (UCHAR)0x1f); + } + else + { + + /* Size of name is fine, save pointer to short file name. */ + short_name_ptr = destination_ptr -> fx_dir_entry_short_name; + + /* Loop to make sure the long file name is NULL terminated. */ + j = i + FX_LONG_NAME_ENTRY_LEN + 1; + do + { + /* Place a NULL in the long name. */ + destination_ptr -> fx_dir_entry_name[i] = 0; + + /* Position to the next entry. */ + i++; + } while ((i < j) && (i < FX_MAX_LONG_NAME_LEN)); + } + + /* Loop to pickup the rest of the name. */ + do + { + + /* Get the lower 5 bit containing the cardinality. */ + card = (*read_ptr) & 0x1f; + + if(card == 0) + { + + /* Number of LFN starts from one. Therefore it cannot be zero. */ + return(FX_FILE_CORRUPT); + } + else + { + card = card - 1; + } + /* For simplicity no checksum or cardinality checking is done */ + if ((get_short_name == 0) || (u)) + { + + /* Loop to pickup name. */ + for (i = 1, j = 0, k = 0; i < FX_DIR_ENTRY_SIZE; i += 2) + { + + if ((i == 11) || (i == 26)) + { + continue; + } + + /* i = 12, 27 is not generated due to +=2 */ + if (i == 13) + { + i = 12; + continue; /* this time next unicode is byte offset 14*/ + } + + /* Determine if there is an actual unicode character present. */ + if (read_ptr[i + 1]) + { + + /* Extended byte is non-zero, make sure both bytes of the unicode entry are not + all ones, since this is a normal case. */ + if ((read_ptr[i + 1] != (UCHAR)0xFF) || (read_ptr[i] != (UCHAR)0xFF)) + { + + /* Name is an actual unicode name, shorten it. */ + get_short_name = 1; + + /* Save the number of directory entries the LFN has. This will be + used later when updating the 8.3 portion of the LFN. */ + destination_ptr -> fx_dir_entry_long_name_shorted = number_of_lfns; + + /* Setup short name pointer. */ + short_name_ptr = destination_ptr -> fx_dir_entry_name; + } + } + + /* Determine if the character is NULL. */ + if (((read_ptr[i] == FX_NULL) && (read_ptr[i + 1] == FX_NULL)) || + ((read_ptr[i] == (UCHAR)0xFF) && (read_ptr[i + 1] == (UCHAR)0xFF))) + { + continue; + } + + /* Determine if the name is too big. */ + if ((card * 13 + j) >= (FX_MAX_LONG_NAME_LEN - 1)) + { + + /* Name is actually too big, shorten it. */ + get_short_name = 1; + + /* Save the number of directory entries the LFN has. This will be + used later when updating the 8.3 portion of the LFN. */ + destination_ptr -> fx_dir_entry_long_name_shorted = number_of_lfns; + + /* Also reposition the short name pointer. */ + short_name_ptr = destination_ptr -> fx_dir_entry_name; + + break; + } + + /* Each entry contains 13 unicode and first byte ASCII, second byte is extended. */ + if (get_short_name == 0) + { + destination_ptr -> fx_dir_entry_name[13 * card + j] = (CHAR)read_ptr[i]; + } + + /* Save the potential unicode characters. */ + unicode_name[k + 26 * card + j] = read_ptr[i]; + unicode_name[k + 26 * card + j + 1] = read_ptr[i + 1]; + k++; + u++; + + j++; + } + } + + /* Determine if a new sector needs to be read. */ + if (byte_offset + FX_DIR_ENTRY_SIZE >= media_ptr -> fx_media_bytes_per_sector) + { + + /* Determine if a sub-directory or FAT32 root directory is specified. */ + if ((source_dir) || (media_ptr -> fx_media_32_bit_FAT)) + { + + /* Determine the next sector of the directory entry. */ + if (relative_sector < (media_ptr -> fx_media_sectors_per_cluster - 1)) + { + + /* More sectors in this cluster. */ + + /* Simply increment the logical sector. */ + logical_sector++; + + /* Increment the relative sector. */ + relative_sector++; + } + else + { + + /* We need to move to the next cluster. */ + + /* Pickup the next cluster. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &next_cluster); + + /* Check for I/O error. */ + if (status != FX_SUCCESS) + { + + /* Return error code. */ + return(status); + } + + /* Copy next cluster to the current cluster. */ + cluster = next_cluster; + + /* Check the value of the new cluster - it must be a valid cluster number + or something is really wrong! */ + if ((cluster < FX_FAT_ENTRY_START) || (cluster > media_ptr -> fx_media_fat_reserved)) + { + + /* Send error message back to caller. */ + return(FX_FILE_CORRUPT); + } + + /* Now increment the relative cluster. */ + relative_cluster++; + + /* Setup the relative sector (this is zero for subsequent cluster. */ + relative_sector = 0; + + /* Calculate the next logical sector. */ + logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) + + (((ULONG)cluster - FX_FAT_ENTRY_START) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster)); + } + } + else + { + + /* Non-FAT 32 root directory. */ + + /* Advance to the next sector. */ + logical_sector++; + + /* Determine if the logical sector is valid. */ + if (logical_sector >= (ULONG)(media_ptr -> fx_media_root_sector_start + media_ptr -> fx_media_root_sectors)) + { + + /* Trying to read past root directory - send error message back to caller. */ + return(FX_FILE_CORRUPT); + } + } + + /* Read the new sector. */ + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) logical_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR); + + /* Check I/O status. */ + if (status != FX_SUCCESS) + { + return(status); + } + + /* Set the byte offset to 0 for new sector. */ + byte_offset = 0; + } + else + { + + /* Calculate the new byte offset. */ + byte_offset += FX_DIR_ENTRY_SIZE; + } + + /* Calculate the next read pointer. */ + read_ptr = (UCHAR *)media_ptr -> fx_media_memory_buffer + (UINT)byte_offset; + + /* Move to the next entry. */ + entry++; + } while (card > 0); + + /* Set flag indicating long file name is present. */ + destination_ptr -> fx_dir_entry_long_name_present = 1; + } + else + { + /* No long file name is present. */ + get_short_name = 1; + } + + /* Determine if we need to clear the long name flag. */ + if (get_short_name == 1) + { + + /* Clear the long name flag. */ + destination_ptr -> fx_dir_entry_long_name_present = 0; + } + + /* Store the unicode name size. */ + *unicode_size = u; + + /* Pickup the short file name. */ + short_name_ptr[0] = 0; + dotflag = 0; + for (i = 0, j = 0; i < (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE); i++) + { + + /* Check for a NULL. */ + if ((CHAR)read_ptr[i] == 0) + { + break; + } + + /* Check for a dot. This happens for the first two directory entries, no + extra dot is needed. */ + if ((CHAR)read_ptr[i] == '.') + { + dotflag = 2; + } + + /* Check for a space. */ + if ((CHAR)read_ptr[i] == ' ') + { + /* Put a dot if a character comes after space. */ + if (dotflag == 0) + { + dotflag = 1; + } + continue; + } + + /* Check for the main short file name size. */ + if (i == FX_DIR_NAME_SIZE) + { + /* Check to see if we need to insert a dot. */ + if (dotflag == 0) + { + dotflag = 1; + } + } + + /* Check to see if we need to add a dot. */ + if (dotflag == 1) + { + /* Add dot to short file name. */ + short_name_ptr[j++] = '.'; + dotflag = 2; /* no more dot for spaces */ + } + + /* Copy a character. */ + short_name_ptr[j] = (CHAR)read_ptr[i]; + + /* Increment size. */ + j++; + } + + /* Determine if a long file name is present and its associated short file + name is actually free. */ + if ((destination_ptr -> fx_dir_entry_long_name_present) && (((UCHAR)short_name_ptr[0]) == (UCHAR)FX_DIR_ENTRY_FREE)) + { + + /* Yes, the short file name is really free even though long file name entries directly precede it. + In this case, simply place the free directory marker at the front of the long file name. */ + destination_ptr -> fx_dir_entry_name[0] = (CHAR)FX_DIR_ENTRY_FREE; + short_name_ptr[0] = (CHAR)0; + } + + /* Determine if the short name pointer is NULL while the read pointer is + non-NULL. */ + if ((short_name_ptr[0] == 0) && (read_ptr[0] == ' ')) + { + + /* This condition can occur with an all blank volume name. Simply + copy the volume name to the short name in this case. */ + for (j = 0; j < (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE); j++) + { + + /* Copy a byte of the volume name. */ + short_name_ptr[j] = (CHAR)read_ptr[j]; + } + } + + /* Set end of string to null. */ + short_name_ptr[j] = 0; + + /* Load up the destination directory entry. */ + read_ptr += (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE); + + /* Copy the attribute into the destination. */ + destination_ptr -> fx_dir_entry_attributes = *read_ptr++; + + /* Pickup the reserved byte. */ + destination_ptr -> fx_dir_entry_reserved = *read_ptr++; + + /* Check for an undocumented NT file name feature for optimizing the storage + of all lower case file names that otherwise are valid 8.3 file names. The + following reserved bit definitions are present: + + BIT3 - set if 8.3 is all in lower case and no extended filename. + BIT4 - set for file, clear for directory entry if no extended filename. + + This is true for all NT systems. Prior to NT follows MSDOS FAT documentation and + is set to 0x00, all bits cleared. Therefore if BIT3 is set force lowercase. */ + if ((get_short_name) && (destination_ptr -> fx_dir_entry_reserved & 0x08)) + { + + /* Microsoft undocumented NT file name feature... convert short name to lower + case. */ + for (j = 0; j <= (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE) && (short_name_ptr[j] != 0x00); j++) + { + + /* Determine if an upper case character is present. */ + if ((short_name_ptr[j] >= 'A') && (short_name_ptr[j] <= 'Z')) + { + + /* Yes, an upper case character is present. Force it to lower case. */ + short_name_ptr[j] = (CHAR)((INT)short_name_ptr[j] + 32); + } + } + } + + /* Pickup the created time in milliseconds. */ + destination_ptr -> fx_dir_entry_created_time_ms = *read_ptr++; + + /* Pickup the created time. */ + destination_ptr -> fx_dir_entry_created_time = _fx_utility_16_unsigned_read(read_ptr); + read_ptr = read_ptr + 2; /* Always 2 bytes */ + + /* Pickup the created date. */ + destination_ptr -> fx_dir_entry_created_date = _fx_utility_16_unsigned_read(read_ptr); + read_ptr = read_ptr + 2; /* Always 2 bytes */ + + /* Pickup the last accessed date. */ + destination_ptr -> fx_dir_entry_last_accessed_date = _fx_utility_16_unsigned_read(read_ptr); + read_ptr = read_ptr + 2; /* Always 2 bytes */ + + /* read the upper 2 bytes of starting cluster - required only for 32 bit FAT */ + if (media_ptr -> fx_media_32_bit_FAT) + { + + /* FAT32 only. */ + destination_ptr -> fx_dir_entry_cluster = _fx_utility_16_unsigned_read(read_ptr); + destination_ptr -> fx_dir_entry_cluster <<= 16; + } + else + { + /* Not required for non FAT32. */ + destination_ptr -> fx_dir_entry_cluster = 0; + } + + /* Advance the read pointer. */ + read_ptr = read_ptr + 2; /* Always 2 bytes */ + + /* Copy the time into the destination. */ + destination_ptr -> fx_dir_entry_time = _fx_utility_16_unsigned_read(read_ptr); + read_ptr = read_ptr + 2; /* Always 2 bytes */ + + /* Copy the date into the destination. */ + destination_ptr -> fx_dir_entry_date = _fx_utility_16_unsigned_read(read_ptr); + read_ptr = read_ptr + 2; /* Always 2 bytes */ + + /* Copy the starting cluster into the destination. */ + destination_ptr -> fx_dir_entry_cluster += _fx_utility_16_unsigned_read(read_ptr); + read_ptr = read_ptr + 2; /* Always 2 bytes */ + + /* Copy the file size into the destination. */ + destination_ptr -> fx_dir_entry_file_size = _fx_utility_32_unsigned_read(read_ptr); + + /* Clear the destination search specific fields. */ + destination_ptr -> fx_dir_entry_last_search_cluster = 0; + destination_ptr -> fx_dir_entry_last_search_relative_cluster = 0; + destination_ptr -> fx_dir_entry_last_search_log_sector = 0; + destination_ptr -> fx_dir_entry_last_search_byte_offset = 0; + + /* Remember the entry number. */ + destination_ptr -> fx_dir_entry_number = entry; + + /* Return entry number. */ + *entry_ptr = entry; + + /* Determine if we should remember the last cluster and relative cluster. */ + if (source_dir) + { + + /* Yes, remember the last cluster and relative cluster for a subsequent call + to read a directory entry. */ + source_dir -> fx_dir_entry_last_search_cluster = cluster; + source_dir -> fx_dir_entry_last_search_relative_cluster = relative_cluster; + + /* Also remember several other items that are unique to the directory... just to verify that the + search information can be used. */ + source_dir -> fx_dir_entry_last_search_log_sector = source_dir -> fx_dir_entry_log_sector; + source_dir -> fx_dir_entry_last_search_byte_offset = source_dir -> fx_dir_entry_byte_offset; + } + + /* Return success to the caller. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_unicode_directory_rename.c b/common/src/fx_unicode_directory_rename.c new file mode 100644 index 0000000..3906d1b --- /dev/null +++ b/common/src/fx_unicode_directory_rename.c @@ -0,0 +1,344 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Unicode */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_unicode.h" +#include "fx_directory.h" +#include "fx_utility.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_unicode_directory_rename PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates the specified unicode directory. */ +/* This function first attempts to find the specified directory. */ +/* If found, the unicode rename request is valid and the directory */ +/* will be changed to the new unicode name. Otherwise, if the directory*/ +/* is not found, the appropriate error code is returned to the caller. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to media */ +/* old_unicode_name Pointer to old unicode name */ +/* old_unicode_length Old unicode name length */ +/* new_unicode_name Pointer to new unicode name */ +/* new_unicode_length Old unicode name length */ +/* new_short_name Pointer to new short name */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _fx_unicode_short_name_get Get the directory short name */ +/* _fx_unicode_directory_search Search for unicode name */ +/* _fx_unicode_directory_entry_change Change unicode file name */ +/* _fx_directory_rename Rename the directory */ +/* _fx_directory_search Search directory */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_unicode_directory_rename(FX_MEDIA *media_ptr, UCHAR *old_unicode_name, ULONG old_unicode_length, + UCHAR *new_unicode_name, ULONG new_unicode_length, CHAR *new_short_name) +{ + +FX_DIR_ENTRY dir_entry; +UINT i, status; +ULONG temp_length; +CHAR old_dir_short_name[13]; +CHAR new_dir_short_name[13]; +UCHAR alpha, beta; + + + /* Clear the return short name. */ + new_short_name[0] = FX_NULL; + + /* Set shortname to null. */ + old_dir_short_name[0] = FX_NULL; + new_dir_short_name[0] = FX_NULL; + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + + /* Get the old short name. */ + status = _fx_unicode_short_name_get(media_ptr, old_unicode_name, old_unicode_length, &old_dir_short_name[0]); + + /* Determine if the search was successful. */ + if (status != FX_SUCCESS) + { + + /* Return the error code. */ + return(status); + } + + /* Protect media. */ + FX_PROTECT + + /* Setup temporary length. */ + temp_length = new_unicode_length; + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Clear the short name string. */ + dir_entry.fx_dir_entry_short_name[0] = FX_NULL; + + /* Determine if the new directory name is already present. */ + status = _fx_unicode_directory_search(media_ptr, &dir_entry, (UCHAR *)new_dir_short_name, sizeof(new_dir_short_name), new_unicode_name, &temp_length, 0); + + /* Determine if the search was successful. */ + if (status == FX_SUCCESS) + { + + /* Determine if the name length are equal. */ + if (old_unicode_length == new_unicode_length) + { + + /* Determine if the new name simply has a UNICODE case change. If so, simply let the processing + continue. */ + i = 0; + do + { + + /* Pickup an old name and new name character and convert to upper case if necessary. */ + alpha = old_unicode_name[i * 2]; + if ((alpha >= 'a') && (alpha <= 'z') && (old_unicode_name[i * 2 + 1] == 0)) + { + + /* Lower case, convert to upper case! */ + alpha = (UCHAR)((INT)alpha - 0x20); + } + beta = new_unicode_name[i * 2]; + if ((beta >= 'a') && (beta <= 'z') && (new_unicode_name[i * 2 + 1] == 0)) + { + + /* Lower case, convert to upper case! */ + beta = (UCHAR)((INT)beta - 0x20); + } + + /* Now compare the characters. */ + if (alpha != beta) + { + + /* Get out of this loop! */ + break; + } + + /* Pickup the high bytes. */ + alpha = old_unicode_name[i * 2 + 1]; + beta = new_unicode_name[i * 2 + 1]; + + /* Now compare the byte. */ + if ((alpha != beta)) + { + + /* Get out of this loop! */ + break; + } + + /* Move to next character. */ + i++; + } while (i < old_unicode_length); + } + else + { + alpha = 0; + beta = 1; + } + + /* Now determine if the names match. */ + if (alpha != beta) + { + + /* Yes, the directory name already exists in the target location. */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(FX_ALREADY_CREATED); + } + } + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Start transaction. */ + _fx_fault_tolerant_transaction_start(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Okay, at this point we need to create a new long directory name that has enough space for the + eventual unicode directory name. */ + + /* Copy the characters from the unicode directory name and make sure they are + within the ASCII range. */ + _fx_unicode_temp_long_file_name[0] = 'z'; + for (i = 1; i < new_unicode_length; i++) + { + + /* Build temporary long file name. */ + _fx_unicode_temp_long_file_name[i] = (UCHAR)((UINT)'0' + (i % 9)); + } + _fx_unicode_temp_long_file_name[i] = FX_NULL; + + /* Loop to try different temp long file names... if necessary. */ + do + { + + /* Rename the directory name. */ + status = _fx_directory_rename(media_ptr, old_dir_short_name, (CHAR *)_fx_unicode_temp_long_file_name); + + /* Determine if there was an error. */ + if (status == FX_ALREADY_CREATED) + { + + /* Adjust the name slightly and try again! */ + _fx_unicode_temp_long_file_name[0]--; + + /* Determine if it is outside the lower case boundary. */ + if (_fx_unicode_temp_long_file_name[0] < 0x61) + { + break; + } + } + } while (status == FX_ALREADY_CREATED); + + /* Determine if there was an error. */ + if (status) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return error. */ + return(status); + } + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Clear the short name string. */ + dir_entry.fx_dir_entry_short_name[0] = FX_NULL; + + /* Search the system for the supplied file name. */ + status = _fx_directory_search(media_ptr, (CHAR *)_fx_unicode_temp_long_file_name, &dir_entry, FX_NULL, FX_NULL); + + /* Determine if the search was successful. */ + if (status != FX_SUCCESS) + { + +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* We can now change the temporary long file name with the destination unicode name. */ + status = _fx_unicode_directory_entry_change(media_ptr, &dir_entry, new_unicode_name, new_unicode_length); + + /* Determine if the rename was successful. */ + if (status == FX_SUCCESS) + { + + /* Yes, copy the short file name to the destination. */ + /* The new short name only have 8 characters, since we didn't include a dot in temp_long_file_name. */ + for (i = 0; i < FX_DIR_NAME_SIZE; i++) + { + + /* Copy a character. */ + new_short_name[i] = dir_entry.fx_dir_entry_short_name[i]; + + /* Are we done? */ + if (new_short_name[i] == FX_NULL) + { + break; + } + } + } + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + /* End transaction. */ + status = _fx_fault_tolerant_transaction_end(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release the protection. */ + FX_UNPROTECT + + /* Return completion status. */ + return(status); +} + diff --git a/common/src/fx_unicode_directory_search.c b/common/src/fx_unicode_directory_search.c new file mode 100644 index 0000000..b3622e8 --- /dev/null +++ b/common/src/fx_unicode_directory_search.c @@ -0,0 +1,501 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Unicode */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_unicode.h" +#include "fx_utility.h" + +#ifndef FX_NO_LOCAL_PATH +FX_LOCAL_PATH_SETUP +#endif + + +/* Define several Unicode working arrays... This keeps the data structures + off the local stack. */ + +UCHAR _fx_unicode_temp_long_file_name[FX_MAX_LONG_NAME_LEN]; +UCHAR _fx_unicode_search_name[FX_MAX_LONG_NAME_LEN * 2]; + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_unicode_directory_search PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function searches for the specified unicode or short name. */ +/* */ +/* Note: The buffer of short_name and unicode_name must be valid to */ +/* fill search result. When short_name is a zero length string, search */ +/* is based on unicode string (terminated with NULL). If it's found */ +/* the short name is written back to buffer of short_name. In this case*/ +/* unicode_name_buffer_length is ignored and short_name_buffer_length */ +/* must not be zero to specify the buffer length. If buffer is too */ +/* smallfor the result, overflow characters and NULL-terminator are cut*/ +/* off. When short_name is a valid string, search is based on */ +/* short_name (terminated with NULL). If it's found the unicode name is*/ +/* written back to buffer of unicode_name. In this case */ +/* short_name_buffer_length is ignored and unicode_name_buffer_length */ +/* must not be zero to specify the unicode buffer length. If buffer is */ +/* too small for the result, overflow characters are cut off but */ +/* NULL-terminator is kept. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to media */ +/* short_name Pointer to short name */ +/* short_name_buffer_length Buffer length for short name */ +/* unicode_name Pointer to Unicode name */ +/* unicode_name_length Unicode name length */ +/* unicode_name_buffer_length Buffer length for Unicode name*/ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _fx_unicode_directory_entry_read Read a full unicode directory */ +/* entry */ +/* _fx_utility_FAT_entry_read Read a FAT entry */ +/* */ +/* CALLED BY */ +/* */ +/* Unicode Utilities */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_unicode_directory_search(FX_MEDIA *media_ptr, FX_DIR_ENTRY *entry_ptr, + UCHAR *short_name, ULONG short_name_buffer_length, + UCHAR *unicode_name, ULONG *unicode_name_length, ULONG unicode_name_buffer_length) +{ + +ULONG i, j; +UINT status, found; +ULONG cluster, next_cluster = 0; +ULONG directory_size; +FX_DIR_ENTRY search_dir; +FX_DIR_ENTRY *search_dir_ptr; +ULONG unicode_search_length; +ULONG local_unicode_name_length; +CHAR unicode_to_short_name[13]; +CHAR *short_name_ptr; + + + /* Setup temp unicode name length. */ + local_unicode_name_length = *unicode_name_length; + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of directory search requests. */ + media_ptr -> fx_media_directory_searches++; +#endif + + /* Set the initial search directory to the current working + directory - if there is one. */ + + /* First check for a local path pointer stored in the thread control block. This + is only available in ThreadX Version 4 and above. */ +#ifndef FX_NO_LOCAL_PATH + if (_tx_thread_current_ptr -> tx_thread_filex_ptr) + { + + /* Determine if the local directory is not the root directory. */ + if (((FX_PATH *)_tx_thread_current_ptr -> tx_thread_filex_ptr) -> fx_path_directory.fx_dir_entry_name[0]) + { + + /* Start at the current working directory of the media. */ + search_dir = ((FX_PATH *)_tx_thread_current_ptr -> tx_thread_filex_ptr) -> fx_path_directory; + + /* Set the internal pointer to the search directory as well. */ + search_dir_ptr = &search_dir; + } + else + { + + /* We are searching in the root directory. */ + search_dir_ptr = FX_NULL; + } + } + else +#endif + if (media_ptr -> fx_media_default_path.fx_path_directory.fx_dir_entry_name[0]) + { + + /* Start at the current working directory of the media. */ + search_dir = media_ptr -> fx_media_default_path.fx_path_directory; + + /* Set the internal pointer to the search directory as well. */ + search_dir_ptr = &search_dir; + } + else + { + + /* The current default directory is the root so just set the + search directory pointer to NULL. */ + search_dir_ptr = FX_NULL; + } + + /* Calculate the directory size. */ + if (search_dir_ptr) + { + + /* Ensure that the search directory's last search cluster is cleared. */ + search_dir_ptr -> fx_dir_entry_last_search_cluster = 0; + + /* Calculate the directory size by counting the allocated + clusters for it. */ + i = 0; + cluster = search_dir_ptr -> fx_dir_entry_cluster; + while ((cluster >= FX_FAT_ENTRY_START) && (cluster < media_ptr -> fx_media_fat_reserved)) + { + + /* Increment the cluster count. */ + i++; + + /* Read the next FAT entry. */ + status = _fx_utility_FAT_entry_read(media_ptr, cluster, &next_cluster); + + /* Check the return status. */ + if (status != FX_SUCCESS) + { + + /* Return the bad status. */ + return(status); + } + + /* Check for error situation. */ + if ((cluster == next_cluster) || (i > media_ptr -> fx_media_total_clusters)) + { + + /* Return the bad status. */ + return(FX_FAT_READ_ERROR); + } + + cluster = next_cluster; + } + + /* Now we can calculate the directory size. */ + directory_size = (((ULONG)media_ptr -> fx_media_bytes_per_sector) * + ((ULONG)media_ptr -> fx_media_sectors_per_cluster) * i) / + (ULONG)FX_DIR_ENTRY_SIZE; + + /* Also save this in the directory entry so we don't have to + calculate it later. */ + search_dir_ptr -> fx_dir_entry_file_size = directory_size; + } + else + { + + /* Directory size is the number of entries in the root directory. */ + directory_size = (ULONG)media_ptr -> fx_media_root_directory_entries; + } + + /* Determine if we are searching for a short file name or a unicode file name. */ + if (short_name[0] == 0) + { + + /* If the unicode name fit into short name length, covert the Unicode to ASCII if possible. */ + if (local_unicode_name_length <= 13) + { + for (j = 0; j < local_unicode_name_length; j++) + { + if ((unicode_name[j * 2] <= 0x7F) && (unicode_name[j * 2 + 1] == 0)) + { + + unicode_to_short_name[j] = (CHAR)unicode_name[j * 2]; + if ((unicode_to_short_name[j] >= 'a') && (unicode_to_short_name[j] <= 'z')) + { + + /* Lower case, convert to upper case! */ + unicode_to_short_name[j] = (CHAR)((INT)unicode_to_short_name[j] - 0x20); + } + } + else + { + unicode_to_short_name[0] = 0; + break; + } + } + } + else + { + unicode_to_short_name[0] = 0; + } + } + else + { + unicode_to_short_name[0] = 0; + } + + /* Loop through entries in the directory. Yes, this is a + linear search! */ + i = 0; + do + { + + /* Read an entry from the directory. */ + status = _fx_unicode_directory_entry_read(media_ptr, search_dir_ptr, &i, entry_ptr, &_fx_unicode_search_name[0], &unicode_search_length); + i++; + + /* Check for error status. */ + if (status != FX_SUCCESS) + { + return(status); + } + + /* Determine if this is an empty entry. */ + if (((UCHAR)entry_ptr -> fx_dir_entry_name[0] == (UCHAR)FX_DIR_ENTRY_FREE) && (entry_ptr -> fx_dir_entry_short_name[0] == 0)) + { + continue; + } + + /* Determine if this is the last directory entry. */ + if ((UCHAR)entry_ptr -> fx_dir_entry_name[0] == (UCHAR)FX_DIR_ENTRY_DONE) + { + break; + } + + /* Determine if there is a short name to match. */ + if (unicode_to_short_name[0]) + { + + /* Get the short name pointer. */ + if (entry_ptr -> fx_dir_entry_short_name[0]) + { + short_name_ptr = entry_ptr -> fx_dir_entry_short_name; + } + else + { + short_name_ptr = entry_ptr -> fx_dir_entry_name; + } + + for (j = 0; j < local_unicode_name_length; j++) + { + + /* Compare characters. */ + if (short_name_ptr[j] != unicode_to_short_name[j]) + { + break; + } + } + if (j == local_unicode_name_length) + { + + /* Only the 1st 13 bytes or the buffer length is copied, whichever is smaller. */ + if (short_name_buffer_length > 13) + { + short_name_buffer_length = 13; + } + + /* The names match, copy the short name into the destination. */ + for (j = 0; j < short_name_buffer_length; j++) + { + short_name[j] = (UCHAR)short_name_ptr[j]; + } + + /* Return success to caller. */ + return(FX_SUCCESS); + } + } + + /* Determine if this is not a unicode name. */ + if (unicode_search_length == 0) + { + continue; + } + + /* Determine if we are searching for a short file name or a unicode file name. */ + if (short_name[0]) + { + + /* We have a short name and need a unicode name. Compare the short name against the short name in + the directory entry for a match. */ + found = FX_TRUE; + for (j = 0; j < 12; j++) + { + + /* Compare characters... */ + if (entry_ptr -> fx_dir_entry_short_name[0]) + { + + /* Yes, the return name is in the short name field... compare against it! */ + if (short_name[j] != (UCHAR)entry_ptr -> fx_dir_entry_short_name[j]) + { + + found = FX_FALSE; + break; + } + } + else + { + + /* No, the return name is in the name field... compare against it! */ + if (short_name[j] != (UCHAR)entry_ptr -> fx_dir_entry_name[j]) + { + + found = FX_FALSE; + break; + } + } + + /* Are we done? */ + if (short_name[j] == (UCHAR)FX_NULL) + { + break; + } + } + + /* One final compare to see if we have a match. */ + if ((found == FX_FALSE) || ((j == 12) && (short_name[12] != 0))) + { + continue; + } + + /* A match was found so copy the unicode name and length and return. */ + /* Copy the length. */ + *unicode_name_length = unicode_search_length; + + /* Check if the name fit in the buffer. */ + if (unicode_name_buffer_length < (unicode_search_length + 1) * 2) + { + unicode_search_length = (unicode_name_buffer_length - 2) / 2; + } + + /* Copy the unicode name. */ + for (j = 0; j < unicode_search_length * 2; j++) + { + + /* Copy one unicode character to the destination... */ + unicode_name[j] = _fx_unicode_search_name[j]; + } + + /* Make sure there is a NULL in the destination. */ + unicode_name[j] = (UCHAR)0; + unicode_name[j + 1] = (UCHAR)0; + + + + /* Return successful completion. */ + return(FX_SUCCESS); + } + else + { + + /* Determine if this is the correct unicode name. */ + if (unicode_search_length != local_unicode_name_length) + { + continue; + } + + /* Compare the unicode search name with the requested unicode name. */ + for (j = 0; j < (local_unicode_name_length * 2); j = j + 2) + { + + /* Compare bytes of each unicode name. */ + if (unicode_name[j] != _fx_unicode_search_name[j]) + { + + /* Not match, Check if the character is in ASCII range. */ + if ((_fx_unicode_search_name[j + 1] == 0) && (unicode_name[j + 1] == 0)) + { + + /* Check if it is case mismatch. */ + if ((unicode_name[j]) >= 'a' && (unicode_name[j] <= 'z')) + { + if ((unicode_name[j] - 0x20) == _fx_unicode_search_name[j]) + { + continue; + } + } + if ((_fx_unicode_search_name[j]) >= 'a' && (_fx_unicode_search_name[j] <= 'z')) + { + if ((_fx_unicode_search_name[j] - 0x20) == unicode_name[j]) + { + continue; + } + } + } + + break; + } + + /* Compare the next byte. */ + if (unicode_name[j + 1] != _fx_unicode_search_name[j + 1]) + { + break; + } + } + + /* Determine if the names do not match. */ + if (j != (local_unicode_name_length * 2)) + { + continue; + } + + /* Otherwise, the names match, copy the short name into the destination. */ + /* Only the 1st 13 bytes or the buffer length is copied, whichever is smaller. */ + if (short_name_buffer_length > 13) + { + short_name_buffer_length = 13; + } + for (j = 0; j < short_name_buffer_length; j++) + { + + /* Copy a character. */ + if (entry_ptr -> fx_dir_entry_short_name[0]) + { + short_name[j] = (UCHAR)entry_ptr -> fx_dir_entry_short_name[j]; + } + else + { + short_name[j] = (UCHAR)entry_ptr -> fx_dir_entry_name[j]; + } + } + + /* Return success to caller. */ + return(FX_SUCCESS); + } + } while (i < directory_size); + + /* Return not found. */ + return(FX_NOT_FOUND); +} + diff --git a/common/src/fx_unicode_file_create.c b/common/src/fx_unicode_file_create.c new file mode 100644 index 0000000..a52d80c --- /dev/null +++ b/common/src/fx_unicode_file_create.c @@ -0,0 +1,283 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Unicode */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_unicode.h" +#include "fx_directory.h" +#include "fx_file.h" +#include "fx_utility.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_unicode_file_create PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates the specified unicode name. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to media */ +/* source_unicode_name Pointer to source unicode name*/ +/* source_unicode_length Unicode name length */ +/* short_name Designated short name */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_search Search directory */ +/* _fx_unicode_directory_entry_change Change unicode file name */ +/* _fx_unicode_directory_search Search for unicode name */ +/* _fx_file_create Create a file */ +/* _fx_fault_tolerant_transaction_start Start fault tolerant */ +/* transaction */ +/* _fx_fault_tolerant_transaction_end End fault tolerant transaction*/ +/* _fx_fault_tolerant_recover Recover FAT chain */ +/* _fx_fault_tolerant_reset_log_file Reset the log file */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_unicode_file_create(FX_MEDIA *media_ptr, UCHAR *source_unicode_name, ULONG source_unicode_length, CHAR *short_name) +{ + +FX_DIR_ENTRY dir_entry; +UINT i, status; +ULONG temp_length; +UCHAR destination_shortname[13]; + + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Clear the short name string. */ + dir_entry.fx_dir_entry_short_name[0] = FX_NULL; + + /* Set destination shortname to null. */ + destination_shortname[0] = FX_NULL; + + /* Clear the return short name. */ + short_name[0] = FX_NULL; + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_UNICODE_FILE_CREATE, media_ptr, source_unicode_name, source_unicode_length, short_name, FX_TRACE_FILE_EVENTS, 0, 0) + + /* Protect media. */ + FX_PROTECT + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Start transaction. */ + _fx_fault_tolerant_transaction_start(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Check for write protect at the media level (set by driver). */ + if (media_ptr -> fx_media_driver_write_protect) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return write protect error. */ + return(FX_WRITE_PROTECT); + } + + /* Setup temporary length. */ + temp_length = source_unicode_length; + + /* Determine if the destination file is already present. */ + status = _fx_unicode_directory_search(media_ptr, &dir_entry, destination_shortname, sizeof(destination_shortname), source_unicode_name, &temp_length, 0); + + /* Determine if the search was successful. */ + if (status == FX_SUCCESS) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(FX_ALREADY_CREATED); + } + + /* Okay, at this point we need to create a new long file name that has enough space for the + eventual unicode file name. */ + + /* Copy the characters from the unicode file name and make sure they are + within the ASCII range. */ + _fx_unicode_temp_long_file_name[0] = 'z'; + for (i = 1; i < source_unicode_length; i++) + { + + /* Build temporary long file name. */ + _fx_unicode_temp_long_file_name[i] = (UCHAR)((UINT)'0' + (i % 9)); + } + _fx_unicode_temp_long_file_name[i] = FX_NULL; + + /* Loop to try different temp long file names... if necessary. */ + do + { + + /* Create a new file with the temp long file name. */ + status = _fx_file_create(media_ptr, (CHAR *)_fx_unicode_temp_long_file_name); + + /* Determine if there was an error. */ + if (status == FX_ALREADY_CREATED) + { + + /* Adjust the name slightly and try again! */ + _fx_unicode_temp_long_file_name[0]--; + + /* Determine if it is outside the lower case boundary. */ + if (_fx_unicode_temp_long_file_name[0] < 0x61) + { + break; + } + } + } while (status == FX_ALREADY_CREATED); + + /* Determine if there was an error. */ + if (status) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return error. */ + return(status); + } + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Clear the short name string. */ + dir_entry.fx_dir_entry_short_name[0] = 0; + + /* Search the system for the supplied file name. */ + status = _fx_directory_search(media_ptr, (CHAR *)_fx_unicode_temp_long_file_name, &dir_entry, FX_NULL, FX_NULL); + + /* Determine if the search was successful. */ + if (status != FX_SUCCESS) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* We can now change the temporary long file name with the destination unicode name. */ + status = _fx_unicode_directory_entry_change(media_ptr, &dir_entry, source_unicode_name, source_unicode_length); + + /* Was this successful? */ + if (status == FX_SUCCESS) + { + + /* Yes, copy the short file name to the destination. */ + /* The new short name only have 8 characters, since we didn't include a dot in temp_long_file_name. */ + for (i = 0; i < FX_DIR_NAME_SIZE; i++) + { + + /* Copy a character. */ + short_name[i] = dir_entry.fx_dir_entry_short_name[i]; + + /* Are we done? */ + if (short_name[i] == FX_NULL) + { + break; + } + } + } + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + /* End transaction. */ + status = _fx_fault_tolerant_transaction_end(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release the protection. */ + FX_UNPROTECT + + /* Return completion status. */ + return(status); +} + diff --git a/common/src/fx_unicode_file_rename.c b/common/src/fx_unicode_file_rename.c new file mode 100644 index 0000000..3ea5b8c --- /dev/null +++ b/common/src/fx_unicode_file_rename.c @@ -0,0 +1,344 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Unicode */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_unicode.h" +#include "fx_directory.h" +#include "fx_file.h" +#include "fx_utility.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_unicode_file_rename PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function first attempts to find the specified file. If found, */ +/* the rename request is valid and the directory entry will be changed */ +/* to the new file name. Otherwise, if the file is not found, the */ +/* appropriate error code is returned to the caller. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to media */ +/* old_unicode_name Pointer to old unicode name */ +/* old_unicode_length Old unicode name length */ +/* new_unicode_name Pointer to new unicode name */ +/* new_unicode_length New unicode name length */ +/* new_short_name Designated new short name */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _fx_unicode_short_name_get Get short name of unicode name*/ +/* _fx_directory_search Search directory */ +/* _fx_unicode_directory_entry_change Change unicode file name */ +/* _fx_unicode_directory_search Search for unicode name */ +/* _fx_file_rename Rename for ASCII file name */ +/* _fx_fault_tolerant_transaction_start Start fault tolerant */ +/* transaction */ +/* _fx_fault_tolerant_transaction_end End fault tolerant transaction*/ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_unicode_file_rename(FX_MEDIA *media_ptr, UCHAR *old_unicode_name, ULONG old_unicode_length, + UCHAR *new_unicode_name, ULONG new_unicode_length, CHAR *new_short_name) +{ +ULONG i; +UINT status; +CHAR alpha, beta; +CHAR old_shortname[13]; +CHAR new_shortname[13]; +FX_DIR_ENTRY dir_entry; + + + /* Clear the return short name. */ + new_short_name[0] = FX_NULL; + + /* Set shortname to null. */ + old_shortname[0] = FX_NULL; + new_shortname[0] = FX_NULL; + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + + /* Get shortname of old unicode name. */ + status = _fx_unicode_short_name_get(media_ptr, old_unicode_name, old_unicode_length, old_shortname); + + /* Determine if the result was successful. */ + if (status != FX_SUCCESS) + { + + /* Return the error code. */ + return(status); + } + + /* Protect media. */ + FX_PROTECT + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Clear the short name string. */ + dir_entry.fx_dir_entry_short_name[0] = FX_NULL; + + /* Search the target directory for the same file name. */ + status = _fx_unicode_directory_search(media_ptr, &dir_entry, (UCHAR *)new_shortname, sizeof(new_shortname), + new_unicode_name, &new_unicode_length, 0); + + /* Determine if the name already exists. */ + if (status == FX_SUCCESS) + { + + if (old_unicode_length == new_unicode_length) + { + + /* Determine if the new name simply has an ASCII case change. If so, simply let the processing + continue. */ + i = 0; + do + { + + /* Pickup an old name and new name character and convert to upper case if necessary. */ + alpha = (CHAR)old_unicode_name[i << 1]; + if ((alpha >= 'a') && (alpha <= 'z') && (old_unicode_name[(i << 1) + 1] == 0)) + { + + /* Lower case, convert to upper case! */ + alpha = (CHAR)((INT)alpha - 0x20); + } + beta = (CHAR)new_unicode_name[i << 1]; + if ((beta >= 'a') && (beta <= 'z') && (new_unicode_name[(i << 1) + 1] == 0)) + { + + /* Lower case, convert to upper case! */ + beta = (CHAR)((INT)beta - 0x20); + } + + /* Now compare the characters. */ + if ((alpha != beta)) + { + + /* Get out of this loop! */ + break; + } + + /* Pickup the high bytes. */ + alpha = (CHAR)old_unicode_name[(i << 1) + 1]; + beta = (CHAR)new_unicode_name[(i << 1) + 1]; + + /* Now compare the byte. */ + if ((alpha != beta)) + { + + /* Get out of this loop! */ + break; + } + + /* Move to next character. */ + i++; + } while (i < old_unicode_length); + } + else + { + + /* Names not match. */ + alpha = 0; + beta = 1; + } + + /* Now determine if the names match. */ + if (alpha != beta) + { + + /* No, the names do not match so simply return an error + to the caller. */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the not a file error code. */ + return(FX_ALREADY_CREATED); + } + } + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Start transaction. */ + _fx_fault_tolerant_transaction_start(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Okay, at this point we need to rename to a new long file name that has enough space for the + eventual unicode file name. */ + + /* Copy the characters from the unicode file name and make sure they are + within the ASCII range. */ + _fx_unicode_temp_long_file_name[0] = 'z'; + for (i = 1; i < new_unicode_length; i++) + { + + /* Build temporary long file name. */ + _fx_unicode_temp_long_file_name[i] = (UCHAR)((UINT)'0' + (i % 9)); + } + _fx_unicode_temp_long_file_name[i] = FX_NULL; + + /* Loop to try different temp long file names... if necessary. */ + do + { + + /* Create a new file with the temp long file name. */ + status = _fx_file_rename(media_ptr, old_shortname, (CHAR *)_fx_unicode_temp_long_file_name); + + /* Determine if there was an error. */ + if (status == FX_ALREADY_CREATED) + { + + /* Adjust the name slightly and try again! */ + _fx_unicode_temp_long_file_name[0]--; + + /* Determine if it is outside the lower case boundary. */ + if (_fx_unicode_temp_long_file_name[0] < 0x61) + { + break; + } + } + } while (status == FX_ALREADY_CREATED); + + /* Determine if there was an error. */ + if (status) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return error. */ + return(status); + } + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Clear the short name string. */ + dir_entry.fx_dir_entry_short_name[0] = 0; + + /* Search the system for the supplied file name. */ + status = _fx_directory_search(media_ptr, (CHAR *)_fx_unicode_temp_long_file_name, &dir_entry, FX_NULL, FX_NULL); + + /* Determine if the search was successful. */ + if (status != FX_SUCCESS) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error status. */ + return(status); + } + + /* We can now change the temporary long file name with the destination unicode name. */ + status = _fx_unicode_directory_entry_change(media_ptr, &dir_entry, new_unicode_name, new_unicode_length); + + /* Was this successful? */ + if (status == FX_SUCCESS) + { + + /* Yes, copy the short file name to the destination. */ + /* The new short name only have 8 characters, since we didn't include a dot in temp_long_file_name. */ + for (i = 0; i < FX_DIR_NAME_SIZE; i++) + { + + /* Copy a character. */ + new_short_name[i] = dir_entry.fx_dir_entry_short_name[i]; + + /* Are we done? */ + if (new_short_name[i] == FX_NULL) + { + break; + } + } + } + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Check for a bad status. */ + if (status != FX_SUCCESS) + { + + FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the bad status. */ + return(status); + } + + /* End transaction. */ + status = _fx_fault_tolerant_transaction_end(media_ptr); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Release the protection. */ + FX_UNPROTECT + + /* Return completion status. */ + return(status); +} + diff --git a/common/src/fx_unicode_length_get.c b/common/src/fx_unicode_length_get.c new file mode 100644 index 0000000..9cfd902 --- /dev/null +++ b/common/src/fx_unicode_length_get.c @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Unicode */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_unicode.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_unicode_length_get PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the length of the supplied unicode name. */ +/* */ +/* Note, this API is deprecated as _fx_unicode_length_get_extended */ +/* should be used. The maximum buffer size of unicode_name is 256. */ +/* */ +/* INPUT */ +/* */ +/* unicode_name Pointer to unicode name */ +/* */ +/* OUTPUT */ +/* */ +/* length */ +/* */ +/* CALLS */ +/* */ +/* _fx_unicode_length_get_extended Actual unicode length get */ +/* service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +ULONG _fx_unicode_length_get(UCHAR *unicode_name) +{ + + /* Call the extended version with 256 bytes maximum buffer length. */ + return(_fx_unicode_length_get_extended(unicode_name, 256)); +} + diff --git a/common/src/fx_unicode_length_get_extended.c b/common/src/fx_unicode_length_get_extended.c new file mode 100644 index 0000000..3838f02 --- /dev/null +++ b/common/src/fx_unicode_length_get_extended.c @@ -0,0 +1,103 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Unicode */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_unicode.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_unicode_length_get_extended PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the length of the supplied unicode name. */ +/* */ +/* INPUT */ +/* */ +/* unicode_name Pointer to unicode name */ +/* buffer_length Buffer length for unicode name*/ +/* */ +/* OUTPUT */ +/* */ +/* length */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +ULONG _fx_unicode_length_get_extended(UCHAR *unicode_name, UINT buffer_length) +{ + +ULONG i; +ULONG length; + + + i = 0; + length = 0; + while (i < buffer_length) + { + + /* See if we are at the end of the unicode string... This assumes that there is a NULL at the end of the string. */ + if ((unicode_name[i] == 0) && (unicode_name[i + 1] == 0)) + { + break; + } + + /* Increment index. */ + i = i + 2; + + /* Increment the length. */ + length++; + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_UNICODE_LENGTH_GET, unicode_name, length, 0, 0, FX_TRACE_FILE_EVENTS, 0, 0) + + /* Return length. */ + return(length); +} + diff --git a/common/src/fx_unicode_name_get.c b/common/src/fx_unicode_name_get.c new file mode 100644 index 0000000..8f20ac8 --- /dev/null +++ b/common/src/fx_unicode_name_get.c @@ -0,0 +1,87 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Unicode */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_unicode.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_unicode_name_get PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function finds the unicode name associated with the supplied */ +/* short 8.3 name. */ +/* */ +/* Note, this API is deprecated as fx_unicode_name_get_extended should */ +/* be used. The maximum written size to destination_unicode_name could */ +/* be FX_MAX_LONG_NAME_LEN * 2. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to media */ +/* source_short_name Pointer to short file name */ +/* destination_unicode_name Pointer to destination name */ +/* destination_unicode_length Destination for name length */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _fx_unicode_name_get_extended Actual unicode name get */ +/* service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_unicode_name_get(FX_MEDIA *media_ptr, CHAR *source_short_name, + UCHAR *destination_unicode_name, ULONG *destination_unicode_length) +{ + + /* Call the extended version with 13 bytes short name length. */ + return(_fx_unicode_name_get_extended(media_ptr, source_short_name, destination_unicode_name, destination_unicode_length, FX_MAX_LONG_NAME_LEN * 2)); +} + diff --git a/common/src/fx_unicode_name_get_extended.c b/common/src/fx_unicode_name_get_extended.c new file mode 100644 index 0000000..38b62f8 --- /dev/null +++ b/common/src/fx_unicode_name_get_extended.c @@ -0,0 +1,136 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Unicode */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_unicode.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_unicode_name_get_extended PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function finds the unicode name associated with the supplied */ +/* short 8.3 name. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to media */ +/* source_short_name Pointer to short file name */ +/* destination_unicode_name Pointer to destination name */ +/* destination_unicode_length Destination for name length */ +/* unicode_name_buffer_length Buffer length of unicode name */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _fx_unicode_directory_search Search for unicode name */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_unicode_name_get_extended(FX_MEDIA *media_ptr, CHAR *source_short_name, + UCHAR *destination_unicode_name, ULONG *destination_unicode_length, ULONG unicode_name_buffer_length) +{ + +UINT status; +FX_DIR_ENTRY dir_entry; + +#ifdef TX_ENABLE_EVENT_TRACE +TX_TRACE_BUFFER_ENTRY *trace_event; +ULONG trace_timestamp; +#endif + + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Clear the short name string. */ + dir_entry.fx_dir_entry_short_name[0] = 0; + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + + /* Set the destination unicode length to zero to indicate there is nothing in terms of a match. */ + *destination_unicode_length = 0; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_UNICODE_NAME_GET, media_ptr, source_short_name, destination_unicode_name, 0, FX_TRACE_FILE_EVENTS, &trace_event, &trace_timestamp) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Search the system for the supplied short file name and return the unicode name if there is a match. */ + status = _fx_unicode_directory_search(media_ptr, &dir_entry, (UCHAR *)source_short_name, 0, destination_unicode_name, destination_unicode_length, unicode_name_buffer_length); + + /* Determine if the search was successful. */ + if (status != FX_SUCCESS) + { + + /* Release media protection. */ + FX_UNPROTECT + + /* Return the error code. */ + return(status); + } + + /* Release media protection. */ + FX_UNPROTECT + + /* Update the trace event with the unicode length. */ + FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_UNICODE_NAME_GET, 0, 0, 0, *destination_unicode_length) + + /* Return successful completion status. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_unicode_short_name_get.c b/common/src/fx_unicode_short_name_get.c new file mode 100644 index 0000000..d4dd144 --- /dev/null +++ b/common/src/fx_unicode_short_name_get.c @@ -0,0 +1,87 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Unicode */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_unicode.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_unicode_short_name_get PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function finds the short name associated with the supplied */ +/* unicode name. */ +/* */ +/* Note, this API is deprecated as fx_unicode_short_name_get_extended */ +/* should be used. The maximum written size to destination_short_name */ +/* could be 13. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to media */ +/* source_unicode_name Pointer to unicode name */ +/* source_unicode_length Length of unicode name */ +/* destination_short_name Pointer to destination name */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _fx_unicode_short_name_get_extended Actual unicode short name get */ +/* service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_unicode_short_name_get(FX_MEDIA *media_ptr, UCHAR *source_unicode_name, ULONG source_unicode_length, + CHAR *destination_short_name) +{ + + /* Call the extended version with 13 bytes short name length. */ + return(_fx_unicode_short_name_get_extended(media_ptr, source_unicode_name, source_unicode_length, destination_short_name, 13)); +} + diff --git a/common/src/fx_unicode_short_name_get_extended.c b/common/src/fx_unicode_short_name_get_extended.c new file mode 100644 index 0000000..c3069a5 --- /dev/null +++ b/common/src/fx_unicode_short_name_get_extended.c @@ -0,0 +1,121 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Unicode */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_unicode.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_unicode_short_name_get_extended PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function finds the short name associated with the supplied */ +/* unicode name. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to media */ +/* source_unicode_name Pointer to unicode name */ +/* source_unicode_length Length of unicode name */ +/* destination_short_name Pointer to destination name */ +/* short_name_buffer_length Buffer length of short name */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _fx_unicode_directory_search Search for unicode name */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_unicode_short_name_get_extended(FX_MEDIA *media_ptr, UCHAR *source_unicode_name, ULONG source_unicode_length, + CHAR *destination_short_name, ULONG short_name_buffer_length) +{ + +UINT status; +ULONG temp_unicode_length; +FX_DIR_ENTRY dir_entry; + + + /* Setup pointer to media name buffer. */ + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + + /* Clear the short name string. */ + dir_entry.fx_dir_entry_short_name[0] = 0; + + /* Check the media to make sure it is open. */ + if (media_ptr -> fx_media_id != FX_MEDIA_ID) + { + + /* Return the media not opened error. */ + return(FX_MEDIA_NOT_OPEN); + } + + + /* Null terminate the short return name. */ + destination_short_name[0] = (UCHAR)0; + + /* Setup temporary unicode name length for search call. */ + temp_unicode_length = source_unicode_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_UNICODE_SHORT_NAME_GET, media_ptr, source_unicode_name, source_unicode_length, destination_short_name, FX_TRACE_FILE_EVENTS, 0, 0) + + /* Protect against other threads accessing the media. */ + FX_PROTECT + + /* Search the system for the supplied short name and return the unicode name if there is a match. */ + status = _fx_unicode_directory_search(media_ptr, &dir_entry, (UCHAR *)destination_short_name, short_name_buffer_length, source_unicode_name, &temp_unicode_length, 0); + + /* Release media protection. */ + FX_UNPROTECT + + /* Return successful completion status. */ + return(status); +} + diff --git a/common/src/fx_utility_16_unsigned_read.c b/common/src/fx_utility_16_unsigned_read.c new file mode 100644 index 0000000..917641f --- /dev/null +++ b/common/src/fx_utility_16_unsigned_read.c @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_utility_16_unsigned_read PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads (with endian awareness) 16-bit unsigned data */ +/* from the specified source and returns the value to the caller. */ +/* */ +/* INPUT */ +/* */ +/* source_ptr Source memory pointer */ +/* */ +/* OUTPUT */ +/* */ +/* UINT value */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_utility_16_unsigned_read(UCHAR *source_ptr) +{ + +UINT value; + + /* Pickup the UINT from the destination with endian-awareness. */ + value = ((((UINT)*(source_ptr + 1)) & 0xFF) << 8) | + ((UINT)*(source_ptr) & 0xFF); + + /* Return value to caller. */ + return(value); +} + diff --git a/common/src/fx_utility_16_unsigned_write.c b/common/src/fx_utility_16_unsigned_write.c new file mode 100644 index 0000000..14bbe53 --- /dev/null +++ b/common/src/fx_utility_16_unsigned_write.c @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_utility_16_unsigned_write PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes (with endian awareness) 16-bit unsigned data */ +/* to the specified destination. */ +/* */ +/* INPUT */ +/* */ +/* dest_ptr Destination memory pointer */ +/* value Value to write to memory */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +VOID _fx_utility_16_unsigned_write(UCHAR *dest_ptr, UINT value) +{ + + /* Store the UINT into the destination with endian-awareness. */ + *(dest_ptr) = (UCHAR)(value & 0xFF); + *(dest_ptr + 1) = (UCHAR)((value >> 8) & 0xFF); +} + diff --git a/common/src/fx_utility_32_unsigned_read.c b/common/src/fx_utility_32_unsigned_read.c new file mode 100644 index 0000000..058d056 --- /dev/null +++ b/common/src/fx_utility_32_unsigned_read.c @@ -0,0 +1,87 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_utility_32_unsigned_read PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads (with endian awareness) a 32-bit unsigned data */ +/* from the specified source and returns the value to the caller. */ +/* */ +/* INPUT */ +/* */ +/* source_ptr Source memory pointer */ +/* */ +/* OUTPUT */ +/* */ +/* ULONG value */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +ULONG _fx_utility_32_unsigned_read(UCHAR *source_ptr) +{ + +ULONG value; + + /* Pickup the UINT from the destination with endian-awareness. */ + value = ((((ULONG) *(source_ptr+3)) & 0xFF) << 24) | + ((((ULONG) *(source_ptr+2)) & 0xFF) << 16) | + ((((ULONG) *(source_ptr+1)) & 0xFF) << 8) | + (((ULONG) *(source_ptr)) & 0xFF); + + /* Return value to caller. */ + return(value); +} + diff --git a/common/src/fx_utility_32_unsigned_write.c b/common/src/fx_utility_32_unsigned_write.c new file mode 100644 index 0000000..70baa19 --- /dev/null +++ b/common/src/fx_utility_32_unsigned_write.c @@ -0,0 +1,83 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_utility_32_unsigned_write PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes (with endian awareness) a 32-bit unsigned data */ +/* type to the specified destination. */ +/* */ +/* INPUT */ +/* */ +/* dest_ptr Destination memory pointer */ +/* value Value to write to memory */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +VOID _fx_utility_32_unsigned_write(UCHAR *dest_ptr, ULONG value) +{ + + /* Store the UINT into the destination with endian-awareness. */ + *(dest_ptr) = (UCHAR)(value & 0xFF); + *(dest_ptr + 1) = (UCHAR)((value >> 8) & 0xFF); + *(dest_ptr + 2) = (UCHAR)((value >> 16) & 0xFF); + *(dest_ptr + 3) = (UCHAR)((value >> 24) & 0xFF); +} + diff --git a/common/src/fx_utility_64_unsigned_read.c b/common/src/fx_utility_64_unsigned_read.c new file mode 100644 index 0000000..5749f58 --- /dev/null +++ b/common/src/fx_utility_64_unsigned_read.c @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_utility_64_unsigned_read PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads (with endian awareness) a 64-bit unsigned data */ +/* from the specified source and returns the value to the caller. */ +/* */ +/* INPUT */ +/* */ +/* source_ptr Source memory pointer */ +/* */ +/* OUTPUT */ +/* */ +/* ULONG64 value */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +ULONG64 _fx_utility_64_unsigned_read(UCHAR *source_ptr) +{ + +ULONG64 value; +ULONG upper_portion; +ULONG lower_portion; + + + lower_portion = _fx_utility_32_unsigned_read(source_ptr); + upper_portion = _fx_utility_32_unsigned_read(source_ptr + sizeof(ULONG)); + + value = ((ULONG64)upper_portion) << 32; + value |= (ULONG64)lower_portion; + + /* Return 64-bit value to caller. */ + return(value); +} + diff --git a/common/src/fx_utility_64_unsigned_write.c b/common/src/fx_utility_64_unsigned_write.c new file mode 100644 index 0000000..1719cc2 --- /dev/null +++ b/common/src/fx_utility_64_unsigned_write.c @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_utility_64_unsigned_write PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes (with endian awareness) a 64-bit unsigned data */ +/* type to the specified destination. */ +/* */ +/* INPUT */ +/* */ +/* dest_ptr Destination memory pointer */ +/* value Value to write to memory */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +VOID _fx_utility_64_unsigned_write(UCHAR *dest_ptr, ULONG64 value) +{ + + /* Store the ULONG64 into the destination with endian-awareness. */ + _fx_utility_32_unsigned_write(dest_ptr, (ULONG)value); + _fx_utility_32_unsigned_write(dest_ptr + sizeof(ULONG), (ULONG)(value >> 32)); +} + diff --git a/common/src/fx_utility_FAT_entry_read.c b/common/src/fx_utility_FAT_entry_read.c new file mode 100644 index 0000000..daf70c7 --- /dev/null +++ b/common/src/fx_utility_FAT_entry_read.c @@ -0,0 +1,433 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_utility.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_utility_FAT_entry_read PORTABLE C */ +/* 6.2.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads the supplied FAT entry from the first FAT of */ +/* the media. 12-bit, 16-bit, and 32-bit FAT reading is supported. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* cluster Cluster entry number */ +/* entry_ptr Pointer to destination for */ +/* the FAT entry */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_16_unsigned_read Read a UINT from FAT buffer */ +/* _fx_utility_32_unsigned_read Read a ULONG form FAT buffer */ +/* _fx_utility_FAT_flush Flush FAT entry cache */ +/* _fx_utility_logical_sector_read Read FAT sector into memory */ +/* _fx_fault_tolerant_read_FAT Read FAT entry from log file */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), and */ +/* added conditional to */ +/* disable fat entry refresh, */ +/* resulting in version 6.1 */ +/* 10-31-2022 Tiejun Zhou Modified comment(s), and */ +/* fixed compiler warning, */ +/* resulting in version 6.2.0 */ +/* */ +/**************************************************************************/ +UINT _fx_utility_FAT_entry_read(FX_MEDIA *media_ptr, ULONG cluster, ULONG *entry_ptr) +{ + +ULONG FAT_sector; +ULONG byte_offset, entry32; +UCHAR *FAT_ptr; +UINT entry, index; +UINT status; +FX_FAT_CACHE_ENTRY *cache_entry_ptr; +#ifndef FX_DISABLE_FAT_ENTRY_REFRESH +FX_FAT_CACHE_ENTRY temp_cache_entry; +#endif /* FX_DISABLE_FAT_ENTRY_REFRESH */ + + +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled && + (media_ptr -> fx_media_fault_tolerant_state & FX_FAULT_TOLERANT_STATE_STARTED)) + { + + /* Redirect this request to log file. */ + status = _fx_fault_tolerant_read_FAT(media_ptr, cluster, entry_ptr, FX_FAULT_TOLERANT_FAT_LOG_TYPE); + + /* Return on success. */ + if (status != FX_READ_CONTINUE) + { + return(status); + } + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + +#ifndef FX_MEDIA_STATISTICS_DISABLE +/* Increment the number of FAT entry reads and cache hits. */ +media_ptr -> fx_media_fat_entry_reads++; +media_ptr -> fx_media_fat_entry_cache_read_hits++; +#endif + +/* Extended port-specific processing macro, which is by default defined to white space. */ +FX_UTILITY_FAT_ENTRY_READ_EXTENSION + +/* Calculate the area of the cache for this FAT entry. */ + index = (cluster & FX_FAT_CACHE_HASH_MASK) * FX_FAT_CACHE_DEPTH; + + /* Build a pointer to the cache entry. */ + cache_entry_ptr = &media_ptr -> fx_media_fat_cache[index]; + +#ifndef FX_DISABLE_FAT_ENTRY_REFRESH + /* Determine if the FAT entry is in the cache - assuming the depth of the FAT cache is + 4 entries. */ + if ((cache_entry_ptr -> fx_fat_cache_entry_cluster) == cluster) + { + + /* Yes, return the cached value. */ + *entry_ptr = cache_entry_ptr -> fx_fat_cache_entry_value; + + /* Don't move anything since we found the entry. */ + + /* Return a successful status. */ + return(FX_SUCCESS); + } + else if (((cache_entry_ptr + 1) -> fx_fat_cache_entry_cluster) == cluster) + { + + /* Yes, return the cached value. */ + *entry_ptr = (cache_entry_ptr + 1) -> fx_fat_cache_entry_value; + + /* Just swap the first and second entry. */ + temp_cache_entry = *(cache_entry_ptr); + *(cache_entry_ptr) = *(cache_entry_ptr + 1); + *(cache_entry_ptr + 1) = temp_cache_entry; + + /* Return a successful status. */ + return(FX_SUCCESS); + } + else if (((cache_entry_ptr + 2) -> fx_fat_cache_entry_cluster) == cluster) + { + + /* Yes, return the cached value. */ + *entry_ptr = (cache_entry_ptr + 2) -> fx_fat_cache_entry_value; + + /* Move the third entry to the top and the first two entries down. */ + temp_cache_entry = *(cache_entry_ptr); + *(cache_entry_ptr) = *(cache_entry_ptr + 2); + *(cache_entry_ptr + 2) = *(cache_entry_ptr + 1); + *(cache_entry_ptr + 1) = temp_cache_entry; + + /* Return a successful status. */ + return(FX_SUCCESS); + } + else if (((cache_entry_ptr + 3) -> fx_fat_cache_entry_cluster) == cluster) + { + + /* Yes, return the cached value. */ + *entry_ptr = (cache_entry_ptr + 3) -> fx_fat_cache_entry_value; + + /* Move the last entry to the top and the first three entries down. */ + temp_cache_entry = *(cache_entry_ptr); + *(cache_entry_ptr) = *(cache_entry_ptr + 3); + *(cache_entry_ptr + 3) = *(cache_entry_ptr + 2); + *(cache_entry_ptr + 2) = *(cache_entry_ptr + 1); + *(cache_entry_ptr + 1) = temp_cache_entry; + + /* Return a successful status. */ + return(FX_SUCCESS); + } +#else +for(UINT i = 0; i < 4; i++) + { + if (((cache_entry_ptr + i) -> fx_fat_cache_entry_cluster) == cluster) + { + *entry_ptr = (cache_entry_ptr + i) -> fx_fat_cache_entry_value; + + /* Return a successful status. */ + return(FX_SUCCESS); + } + } +#endif /* FX_DISABLE_FAT_ENTRY_REFRESH */ + + /* Determine if the oldest entry was modified, i.e. whether or not it is + dirty. */ + if (media_ptr -> fx_media_fat_cache[index + 3].fx_fat_cache_entry_dirty) + { + + /* Yes, the entry is dirty and needs to be flushed out. */ + status = _fx_utility_FAT_flush(media_ptr); + + /* Check for completion status. */ + if (status != FX_SUCCESS) + { + + /* Return error status. */ + return(status); + } + } + + /* If we get here, the entry was not found in the FAT entry cache. We need to + actually read the FAT entry. */ + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Decrement the number of cache hits. */ + media_ptr -> fx_media_fat_entry_cache_read_hits--; + + /* Increment the number of cache misses. */ + media_ptr -> fx_media_fat_entry_cache_read_misses++; +#endif + + /* Determine which type of FAT is present. */ + if (media_ptr -> fx_media_12_bit_FAT) + { + + /* Calculate the byte offset to the cluster entry. */ + byte_offset = (((ULONG)cluster << 1) + cluster) >> 1; + + /* Calculate the FAT sector the requested FAT entry resides in. */ + FAT_sector = (byte_offset / media_ptr -> fx_media_bytes_per_sector) + + (ULONG)media_ptr -> fx_media_reserved_sectors; + + /* Read the sector in. */ + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) FAT_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR); + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + /* Return the error status. */ + return(status); + } + + /* Now calculate the byte offset into this FAT sector. */ + byte_offset = byte_offset - + ((FAT_sector - (ULONG)media_ptr -> fx_media_reserved_sectors) * + media_ptr -> fx_media_bytes_per_sector); + + /* Setup a pointer into the buffer. */ + FAT_ptr = (UCHAR *)media_ptr -> fx_media_memory_buffer + (UINT)byte_offset; + + /* Determine if the cluster entry is odd or even. */ + if (cluster & 1) + { + + /* Odd cluster number. */ + + /* Pickup the lower nibble of the FAT entry. */ + entry = (((UINT)*FAT_ptr) & 0xF0) >> 4; + + /* Move to the next byte of the FAT entry. */ + FAT_ptr++; + + /* Determine if we are now past the end of the FAT buffer in memory. */ + if (byte_offset == (ULONG)(media_ptr -> fx_media_bytes_per_sector - 1)) + { + + /* Yes, we need to read the next sector. */ + FAT_sector++; + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) FAT_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR); + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + + /* Setup a pointer into the buffer. */ + FAT_ptr = (UCHAR *)media_ptr -> fx_media_memory_buffer; + } + + /* Pickup the upper 8 bits of the FAT entry. */ + entry = entry | (((UINT)*FAT_ptr) << 4); + } + else + { + + /* Even cluster number. */ + + /* Pickup the lower byte of the FAT entry. */ + entry = (UINT)(((UINT)*FAT_ptr) & 0xFF); + + /* Move to the next nibble of the FAT entry. */ + FAT_ptr++; + + /* Determine if we are now past the end of the FAT buffer in memory. */ + if (byte_offset == (ULONG)(media_ptr -> fx_media_bytes_per_sector - 1)) + { + + /* Yes, we need to read the next sector. */ + FAT_sector++; + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) FAT_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR); + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + return(status); + } + + /* Setup a pointer into the buffer. */ + FAT_ptr = (UCHAR *)media_ptr -> fx_media_memory_buffer; + } + + /* Pickup the upper 4 bits of the FAT entry. */ + entry = entry | ((((UINT)*FAT_ptr) & 0x0F) << 8); + } + + /* Determine if we need to do sign extension on the 12-bit eof value. */ + if (entry >= FX_MAX_12BIT_CLUST) + { + + /* Yes, we need to sign extend. */ + entry = entry | FX_SIGN_EXTEND; + } + + *entry_ptr = entry; + } + + /* Check for a 16-bit FAT. */ + else if (!media_ptr -> fx_media_32_bit_FAT) + { + + /* 16-bit FAT is present. */ + + /* Calculate the byte offset to the cluster entry. */ + byte_offset = (((ULONG)cluster) * 2); + + /* Calculate the FAT sector the requested FAT entry resides in. */ + FAT_sector = (byte_offset / media_ptr -> fx_media_bytes_per_sector) + + (ULONG)media_ptr -> fx_media_reserved_sectors; + + /* Read the FAT sector. */ + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) FAT_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR); + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + + /* Return the error code. */ + return(status); + } + + /* Now calculate the byte offset into this FAT sector. */ + byte_offset = byte_offset - + ((FAT_sector - (ULONG)media_ptr -> fx_media_reserved_sectors) * + media_ptr -> fx_media_bytes_per_sector); + + /* Setup a pointer into the buffer. */ + FAT_ptr = (UCHAR *)media_ptr -> fx_media_memory_buffer + (UINT)byte_offset; + + /* Pickup the FAT entry. */ + entry = _fx_utility_16_unsigned_read(FAT_ptr); + + *entry_ptr = entry; + } + else + { + + /* Otherwise, a 32 bit FAT present. */ + byte_offset = (((ULONG)cluster) * 4); + + /* Calculate the FAT sector the requested FAT entry resides in. */ + FAT_sector = (byte_offset / media_ptr -> fx_media_bytes_per_sector) + + (ULONG)media_ptr -> fx_media_reserved_sectors; + + /* Calculate the byte offset to the FAT entry. */ + byte_offset = (byte_offset % media_ptr -> fx_media_bytes_per_sector); + + /* Read the appropriate FAT sector. */ + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) FAT_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR); + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + + /* Return the error code. */ + return(status); + } + + /* Setup a pointer into the buffer. */ + FAT_ptr = (UCHAR *)media_ptr -> fx_media_memory_buffer + (ULONG)byte_offset; + + /* Pickup the FAT entry. */ + entry32 = _fx_utility_32_unsigned_read(FAT_ptr); + + + /* Clear upper nibble. */ + entry32 = entry32 & 0x0FFFFFFF; + + *entry_ptr = entry32; + } + + /* Move all the cache entries down so the oldest is at the bottom. */ + *(cache_entry_ptr + 3) = *(cache_entry_ptr + 2); + *(cache_entry_ptr + 2) = *(cache_entry_ptr + 1); + *(cache_entry_ptr + 1) = *(cache_entry_ptr); + + /* Setup the new FAT entry in the cache. */ + cache_entry_ptr -> fx_fat_cache_entry_cluster = cluster; + cache_entry_ptr -> fx_fat_cache_entry_value = *entry_ptr; + + /* Return success to the caller. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_utility_FAT_entry_write.c b/common/src/fx_utility_FAT_entry_write.c new file mode 100644 index 0000000..4f76ad1 --- /dev/null +++ b/common/src/fx_utility_FAT_entry_write.c @@ -0,0 +1,257 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_utility.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_utility_FAT_entry_write PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes to the supplied FAT entry to all FATs in */ +/* the media. 12-bit, 16-bit, and 32-bit FAT writing is supported. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* cluster Cluster entry number */ +/* next_cluster Next cluster integer pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_FAT_flush FLUSH dirty entries in the */ +/* FAT cache */ +/* _fx_fault_tolerant_add_fat_log Add FAT redo log */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_utility_FAT_entry_write(FX_MEDIA *media_ptr, ULONG cluster, ULONG next_cluster) +{ + +UINT status, index, i; +FX_FAT_CACHE_ENTRY *cache_entry_ptr; +#ifdef FX_ENABLE_FAULT_TOLERANT +ULONG FAT_sector; + + /* While fault_tolerant is enabled, only FAT entries in the same sector are allowed to be cached. */ + /* We must flush FAT sectors in the order of FAT chains. */ + if (media_ptr -> fx_media_fault_tolerant_enabled && + (media_ptr -> fx_media_fault_tolerant_state & FX_FAULT_TOLERANT_STATE_STARTED)) + { + + if (!(media_ptr -> fx_media_fault_tolerant_state & FX_FAULT_TOLERANT_STATE_SET_FAT_CHAIN)) + { + + /* Redirect this request to log file. */ + return(_fx_fault_tolerant_add_FAT_log(media_ptr, cluster, next_cluster)); + } + + /* Get sector number of the incoming FAT entry. */ + FAT_sector = _fx_utility_FAT_sector_get(media_ptr, cluster); + + /* Is FAT sector changed? */ + if (FAT_sector != media_ptr -> fx_media_fault_tolerant_cached_FAT_sector) + { + + /* Are there any cached FAT entries? */ + if (media_ptr -> fx_media_fault_tolerant_cached_FAT_sector) + { + + /* Current FAT entry is located in different sector. Force flush. */ + /* Flush the cached individual FAT entries */ + _fx_utility_FAT_flush(media_ptr); + + } + + /* Record sector number of current FAT entry. */ + media_ptr -> fx_media_fault_tolerant_cached_FAT_sector = FAT_sector; + } + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + +#ifndef FX_MEDIA_STATISTICS_DISABLE +/* Increment the number of FAT entry writes and cache hits. */ +media_ptr -> fx_media_fat_entry_writes++; +media_ptr -> fx_media_fat_entry_cache_write_hits++; +#endif + +/* Extended port-specific processing macro, which is by default defined to white space. */ +FX_UTILITY_FAT_ENTRY_WRITE_EXTENSION + +/* Calculate the area of the cache for this FAT entry. */ + index = (cluster & FX_FAT_CACHE_HASH_MASK) * FX_FAT_CACHE_DEPTH; + + /* Build a pointer to the cache entry. */ + cache_entry_ptr = &media_ptr -> fx_media_fat_cache[index]; + + /* First search for the entry in the FAT entry cache. */ + for (i = 0; i < FX_FAT_CACHE_DEPTH; i++) + { + + /* See if the entry matches the write request. */ + if (((cache_entry_ptr + i) -> fx_fat_cache_entry_cluster) == cluster) + { + + /* Yes, we have a matching entry. Save the new information in the FAT + cache and mark this entry as dirty. */ + (cache_entry_ptr + i) -> fx_fat_cache_entry_value = next_cluster; + (cache_entry_ptr + i) -> fx_fat_cache_entry_dirty = 1; + + /* Determine if the driver has requested notification when data sectors in the media + become free. This can be useful to FLASH manager software. */ + if ((media_ptr -> fx_media_driver_free_sector_update) && (next_cluster == FX_FREE_CLUSTER)) + { + + /* Yes, the driver does wish to know that these specific sector(s) are + not in use. */ + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of driver release sectors requests. */ + media_ptr -> fx_media_driver_release_sectors_requests++; +#endif + + /* This cluster is being released so inform the driver that the + corresponding sectors are now available. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_RELEASE_SECTORS; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_logical_sector = (media_ptr -> fx_media_data_sector_start + + ((cluster - FX_FAT_ENTRY_START) * media_ptr -> fx_media_sectors_per_cluster)); + media_ptr -> fx_media_driver_sectors = media_ptr -> fx_media_sectors_per_cluster; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_RELEASE_SECTORS, media_ptr, media_ptr -> fx_media_driver_logical_sector, media_ptr -> fx_media_driver_sectors, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Call the driver. */ + (media_ptr -> fx_media_driver_entry)(media_ptr); + } + + /* Done, return successful status. */ + return(FX_SUCCESS); + } + } + + /* If we reach this point, we know that the FAT write request is not in + the cache. */ + +#ifndef FX_MEDIA_STATISTICS_DISABLE + /* Decrement the number of cache hits. */ + media_ptr -> fx_media_fat_entry_cache_write_hits--; + + /* Increment the number of cache misses. */ + media_ptr -> fx_media_fat_entry_cache_write_misses++; +#endif + + /* Determine if the oldest entry is dirty and needs to be flushed. */ + if (media_ptr -> fx_media_fat_cache[index + 3].fx_fat_cache_entry_dirty == 1) + { + + /* Flush the dirty entry so it can be used to hold the current + FAT entry write request. */ + status = _fx_utility_FAT_flush(media_ptr); + + /* Determine if the write was successful. */ + if (status != FX_SUCCESS) + { + + /* No, return error status to caller. */ + return(status); + } + + } + + /* Move all the cache entries down so the oldest is at the bottom. */ + *(cache_entry_ptr + 3) = *(cache_entry_ptr + 2); + *(cache_entry_ptr + 2) = *(cache_entry_ptr + 1); + *(cache_entry_ptr + 1) = *(cache_entry_ptr); + + /* Save the current FAT entry write request and mark as dirty. */ + cache_entry_ptr -> fx_fat_cache_entry_dirty = 1; + cache_entry_ptr -> fx_fat_cache_entry_cluster = cluster; + cache_entry_ptr -> fx_fat_cache_entry_value = next_cluster; + + /* Determine if the driver has requested notification when data sectors in the media + become free. This can be useful to FLASH manager software. */ + if ((media_ptr -> fx_media_driver_free_sector_update) && (next_cluster == FX_FREE_CLUSTER)) + { + + /* Yes, the driver does wish to know that these specific sector(s) are + not in use. */ + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of driver release sectors requests. */ + media_ptr -> fx_media_driver_release_sectors_requests++; +#endif + + /* This cluster is being released so inform the driver that the + corresponding sectors are now available. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_RELEASE_SECTORS; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_logical_sector = (media_ptr -> fx_media_data_sector_start + + ((cluster - FX_FAT_ENTRY_START) * media_ptr -> fx_media_sectors_per_cluster)); + media_ptr -> fx_media_driver_sectors = media_ptr -> fx_media_sectors_per_cluster; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_RELEASE_SECTORS, media_ptr, media_ptr -> fx_media_driver_logical_sector, media_ptr -> fx_media_driver_sectors, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Call the driver. */ + (media_ptr -> fx_media_driver_entry)(media_ptr); + } + + /* Return success to caller. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_utility_FAT_flush.c b/common/src/fx_utility_FAT_flush.c new file mode 100644 index 0000000..52fb7aa --- /dev/null +++ b/common/src/fx_utility_FAT_flush.c @@ -0,0 +1,548 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_utility_FAT_flush PORTABLE C */ +/* 6.1.2 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function flushes the contents of the FAT cache to the media. */ +/* 12-bit, 16-bit and 32-bit FAT writing is supported. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_16_unsigned_write Write a UINT into buffer */ +/* _fx_utility_32_unsigned_read Read a ULONG from buffer */ +/* _fx_utility_32_unsigned_write Write a ULONG into buffer */ +/* _fx_utility_logical_sector_read Read FAT sector into memory */ +/* _fx_utility_logical_sector_write Write FAT sector back to disk */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* 11-09-2020 William E. Lamie Modified comment(s), */ +/* updated logic for */ +/* FAT secondary update map, */ +/* resulting in version 6.1.2 */ +/* */ +/**************************************************************************/ +UINT _fx_utility_FAT_flush(FX_MEDIA *media_ptr) +{ + +ULONG FAT_sector; +ULONG byte_offset; +UCHAR *FAT_ptr; +UINT temp, i; +UINT status, index, ind; +ULONG cluster, next_cluster; +UCHAR sectors_per_bit; +INT multi_sector_entry; +ULONG sector; + +#ifndef FX_MEDIA_STATISTICS_DISABLE + /* Increment the number of cache flush requests. */ + media_ptr -> fx_media_fat_cache_flushes++; +#endif + + /* Loop through the media's FAT cache and flush out dirty entries. */ + for (index = 0; index < FX_MAX_FAT_CACHE; index++) + { + + /* Determine if the entry is dirty. */ + if ((media_ptr -> fx_media_fat_cache[index].fx_fat_cache_entry_dirty) == 0) + { + + /* No, just advance to the next entry. */ + continue; + } + + /* Otherwise, the entry is indeed dirty and must be flushed out. Process + relative to the type of FAT that is being used. */ + + /* Pickup the contents of the FAT cache entry. */ + cluster = media_ptr -> fx_media_fat_cache[index].fx_fat_cache_entry_cluster; + + /* Determine which type of FAT is present. */ + if (media_ptr -> fx_media_12_bit_FAT) + { + + /* Calculate the byte offset to the cluster entry. */ + byte_offset = (((ULONG)cluster << 1) + cluster) >> 1; + + /* Calculate the FAT sector the requested FAT entry resides in. */ + FAT_sector = (byte_offset / media_ptr -> fx_media_bytes_per_sector) + + (ULONG)media_ptr -> fx_media_reserved_sectors; + + /* Initialize as not written. */ + multi_sector_entry = -1; + + for (;;) + { + + /* Pickup the FAT sector. */ + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) FAT_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR); + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + + /* Determine if a mulit-sector FAT update is present. */ + if (multi_sector_entry != -1) + { + + /* Yes, store the remaining portion of the new FAT entry in the + next FAT sector. */ + + /* Setup a pointer into the buffer. */ + FAT_ptr = (UCHAR *)media_ptr -> fx_media_memory_buffer; + + /* Pickup the cluster and next cluster. */ + cluster = (media_ptr -> fx_media_fat_cache[multi_sector_entry].fx_fat_cache_entry_cluster); + next_cluster = media_ptr -> fx_media_fat_cache[multi_sector_entry].fx_fat_cache_entry_value; + + /* Determine if the cluster entry is odd or even. */ + if (cluster & 1) + { + + /* Store the upper 8 bits of the FAT entry. */ + *FAT_ptr = (UCHAR)((next_cluster >> 4) & 0xFF); + } + else + { + + /* Store the upper 4 bits of the FAT entry. */ + temp = ((UINT)*FAT_ptr) & 0xF0; + *FAT_ptr = (UCHAR)(temp | ((next_cluster >> 8) & 0xF)); + } + + /* Clear the multi-sector flag. */ + multi_sector_entry = -1; + } + + /* Loop through the remainder of the cache to check for multiple entries + within the same FAT sector being written out. */ + for (i = index; i < FX_MAX_FAT_CACHE; i++) + { + + /* Is the cache entry dirty? */ + if ((media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_dirty) == 0) + { + + /* Not dirty, does not need to be flushed. */ + continue; + } + + /* Isolate the cluster. */ + cluster = (media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_cluster); + + /* Calculate the byte offset to the cluster entry. */ + byte_offset = (((ULONG)cluster << 1) + cluster) >> 1; + + /* Pickup the sector. */ + sector = (byte_offset / media_ptr -> fx_media_bytes_per_sector) + + (ULONG)media_ptr -> fx_media_reserved_sectors; + + /* Is it the current FAT sector? */ + if (sector != FAT_sector) + { + + /* Different FAT sector - not in this pass of the loop. */ + continue; + } + + /* Pickup new value for this FAT entry. */ + next_cluster = media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_value; + + /* Now calculate the byte offset into this FAT sector. */ + byte_offset = byte_offset - + ((FAT_sector - (ULONG)media_ptr -> fx_media_reserved_sectors) * + media_ptr -> fx_media_bytes_per_sector); + + /* Determine if we are now past the end of the FAT buffer in memory. */ + if (byte_offset == (ULONG)(media_ptr -> fx_media_bytes_per_sector - 1)) + { + + /* Yes, we need to read the next sector */ + multi_sector_entry = (INT)i; + } + + /* Setup a pointer into the buffer. */ + FAT_ptr = (UCHAR *)media_ptr -> fx_media_memory_buffer + (UINT)byte_offset; + + /* Clear the dirty flag. */ + media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_dirty = 0; + + /* Determine if the cluster entry is odd or even. */ + if (cluster & 1) + { + + /* Odd cluster number. */ + + /* Pickup the upper nibble of the FAT entry. */ + + /* First, set the lower nibble of the FAT entry. */ + temp = (((UINT)*FAT_ptr) & 0x0F); + *FAT_ptr = (UCHAR)(temp | ((next_cluster << 4) & 0xF0)); + + /* Determine if this is a mulit-sector entry. */ + if ((multi_sector_entry) == (INT)i) + { + + /* Yes, requires multiple sector - will write rest of the part later. */ + continue; + } + + /* Move to the next byte of the FAT entry. */ + FAT_ptr++; + + /* Store the upper 8 bits of the FAT entry. */ + *FAT_ptr = (UCHAR)((next_cluster >> 4) & 0xFF); + } + else + { + + /* Even cluster number. */ + + /* Store the lower byte of the FAT entry. */ + *FAT_ptr = (UCHAR)(next_cluster & 0xFF); + + /* Determine if this is a mulit-sector entry. */ + if ((multi_sector_entry) == (INT)i) + { + + /* Yes, requires multiple sector - will write rest of the part later. */ + continue; + } + + /* Move to the next nibble of the FAT entry. */ + FAT_ptr++; + + /* Store the upper 4 bits of the FAT entry. */ + temp = ((UINT)*FAT_ptr) & 0xF0; + *FAT_ptr = (UCHAR)(temp | ((next_cluster >> 8) & 0xF)); + } + } + + /* First, write out the current sector. */ + status = _fx_utility_logical_sector_write(media_ptr, (ULONG64) FAT_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR); + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + + /* Mark the FAT sector update bit map to indicate this sector has been written. */ + if (media_ptr -> fx_media_sectors_per_FAT % (FX_FAT_MAP_SIZE << 3) == 0) + { + sectors_per_bit = (UCHAR)((UINT)media_ptr -> fx_media_sectors_per_FAT / (FX_FAT_MAP_SIZE << 3)); + } + else + { + sectors_per_bit = (UCHAR)((UINT)media_ptr -> fx_media_sectors_per_FAT / (FX_FAT_MAP_SIZE << 3) + 1); + } + + /* Check for invalid value. */ + if (sectors_per_bit == 0) + { + + /* Invalid media, return error. */ + return(FX_MEDIA_INVALID); + } + + ind = ((FAT_sector - media_ptr -> fx_media_reserved_sectors) / sectors_per_bit) >> 3; + media_ptr -> fx_media_fat_secondary_update_map[ind] = + (UCHAR)((INT)media_ptr -> fx_media_fat_secondary_update_map[ind] + | (1 <<(((FAT_sector - media_ptr -> fx_media_reserved_sectors) / sectors_per_bit) & 7))); + + /* Determine if the multi-sector flag is set. */ + if (multi_sector_entry != -1) + { + + /* Yes, position to the next sector and read it in. */ + FAT_sector++; + } + else + { + + /* No, we are finished with this loop. */ + break; + } + } + } + else if (!media_ptr -> fx_media_32_bit_FAT) + { + + /* 16-bit FAT is present. */ + + /* Calculate the byte offset to the cluster entry. */ + byte_offset = (((ULONG)cluster) << 1); + + /* Calculate the FAT sector the requested FAT entry resides in. */ + FAT_sector = (byte_offset / media_ptr -> fx_media_bytes_per_sector) + + (ULONG)media_ptr -> fx_media_reserved_sectors; + + /* Read the FAT sector. */ + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) FAT_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR); + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + + /* Loop through the remainder of the cache to check for multiple entries + within the same FAT sector being written out. */ + for (i = index; i < FX_MAX_FAT_CACHE; i++) + { + + /* Determine if the entry is dirty. */ + if (media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_dirty == 0) + { + + /* Not dirty, does not need to be flushed. */ + continue; + } + + /* Isolate the cluster. */ + cluster = (media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_cluster); + + /* Calculate the byte offset to the cluster entry. */ + byte_offset = (((ULONG)cluster) * 2); + + /* Pickup the sector. */ + sector = (byte_offset / media_ptr -> fx_media_bytes_per_sector) + + (ULONG)media_ptr -> fx_media_reserved_sectors; + + /* Is it the current FAT sector? */ + if (sector != FAT_sector) + { + + /* Different FAT sector - not in this pass of the loop. */ + continue; + } + + /* Now calculate the byte offset into this FAT sector. */ + byte_offset = byte_offset - + ((FAT_sector - (ULONG)media_ptr -> fx_media_reserved_sectors) * + media_ptr -> fx_media_bytes_per_sector); + + /* Setup a pointer into the buffer. */ + FAT_ptr = (UCHAR *)media_ptr -> fx_media_memory_buffer + (UINT)byte_offset; + + /* Pickup new value for this FAT entry. */ + next_cluster = media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_value; + + /* Store the FAT entry. */ + _fx_utility_16_unsigned_write(FAT_ptr, (UINT)next_cluster); + + /* Clear the dirty flag. */ + media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_dirty = 0; + } + + /* Write the last written FAT sector out. */ + status = _fx_utility_logical_sector_write(media_ptr, (ULONG64) FAT_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR); + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + /* Return the error status. */ + return(status); + } + + /* Mark the FAT sector update bit map to indicate this sector has been + written. */ + if (media_ptr -> fx_media_sectors_per_FAT % (FX_FAT_MAP_SIZE << 3) == 0) + { + sectors_per_bit = (UCHAR)(media_ptr -> fx_media_sectors_per_FAT / (FX_FAT_MAP_SIZE << 3)); + } + else + { + sectors_per_bit = (UCHAR)((media_ptr -> fx_media_sectors_per_FAT / (FX_FAT_MAP_SIZE << 3)) + 1); + } + ind = ((FAT_sector - media_ptr -> fx_media_reserved_sectors) / sectors_per_bit) >> 3; + media_ptr -> fx_media_fat_secondary_update_map[ind] = + (UCHAR)((INT)media_ptr -> fx_media_fat_secondary_update_map[ind] + | (1 <<(((FAT_sector - media_ptr -> fx_media_reserved_sectors) / sectors_per_bit) & 7))); + } + else + { + + /* 32-bit FAT is present. */ + + /* Calculate the byte offset to the cluster entry. */ + byte_offset = (((ULONG)cluster) * 4); + + /* Calculate the FAT sector the requested FAT entry resides in. */ + FAT_sector = (byte_offset / media_ptr -> fx_media_bytes_per_sector) + + (ULONG)media_ptr -> fx_media_reserved_sectors; + + /* Read the FAT sector. */ + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) FAT_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR); + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + + /* Loop through the remainder of the cache to check for multiple entries + within the same FAT sector being written out. */ + for (i = index; i < FX_MAX_FAT_CACHE; i++) + { + + /* Determine if the entry is dirty. */ + if (media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_dirty == 0) + { + + /* Not dirty, does not need to be flushed. */ + continue; + } + + /* Isolate the cluster. */ + cluster = (media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_cluster); + + /* Calculate the byte offset to the cluster entry. */ + byte_offset = (((ULONG)cluster) * 4); + + /* Pickup the sector. */ + sector = (byte_offset / media_ptr -> fx_media_bytes_per_sector) + + (ULONG)media_ptr -> fx_media_reserved_sectors; + + /* Is it the current FAT sector? */ + if (sector != FAT_sector) + { + + /* Different FAT sector - not in this pass of the loop. */ + continue; + } + + /* Now calculate the byte offset into this FAT sector. */ + byte_offset = byte_offset - + ((FAT_sector - (ULONG)media_ptr -> fx_media_reserved_sectors) * + media_ptr -> fx_media_bytes_per_sector); + + /* Setup a pointer into the buffer. */ + FAT_ptr = (UCHAR *)media_ptr -> fx_media_memory_buffer + (UINT)byte_offset; + + /* Pickup new value for this FAT entry. */ + next_cluster = media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_value; + + /* Store the FAT entry. */ + _fx_utility_32_unsigned_write(FAT_ptr, next_cluster); + + /* Clear the dirty flag. */ + media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_dirty = 0; + } + + /* Write the last written FAT sector out. */ + status = _fx_utility_logical_sector_write(media_ptr, (ULONG64) FAT_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR); + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + + + /* Mark the FAT sector update bit map to indicate this sector has been + written. */ + if (media_ptr -> fx_media_sectors_per_FAT % (FX_FAT_MAP_SIZE << 3) == 0) + { + sectors_per_bit = (UCHAR)(media_ptr -> fx_media_sectors_per_FAT / (FX_FAT_MAP_SIZE << 3)); + } + else + { + sectors_per_bit = (UCHAR)((media_ptr -> fx_media_sectors_per_FAT / (FX_FAT_MAP_SIZE << 3)) + 1); + } + ind = ((FAT_sector - media_ptr -> fx_media_reserved_sectors) / sectors_per_bit) >> 3; + media_ptr -> fx_media_fat_secondary_update_map[ind] = + (UCHAR)((INT)media_ptr -> fx_media_fat_secondary_update_map[ind] + | (1 <<(((FAT_sector - media_ptr -> fx_media_reserved_sectors) / sectors_per_bit) & 7))); + } + } + +#ifdef FX_ENABLE_FAULT_TOLERANT + + /* While fault_tolerant is enabled, this function will be called before the return of all APIs. */ + if (media_ptr -> fx_media_fault_tolerant_enabled) + { + + /* Delete the record of FAT sector since all FAT entries are flushed. */ + media_ptr -> fx_media_fault_tolerant_cached_FAT_sector = 0; + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Return successful status. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_utility_FAT_map_flush.c b/common/src/fx_utility_FAT_map_flush.c new file mode 100644 index 0000000..9ab3702 --- /dev/null +++ b/common/src/fx_utility_FAT_map_flush.c @@ -0,0 +1,172 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_utility_FAT_map_flush PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function updates mirrors changes in the primary FAT to each of */ +/* secondary FATs in the media. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_logical_sector_read Read FAT sector into memory */ +/* _fx_utility_logical_sector_write Write FAT sector back to disk */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_utility_FAT_map_flush(FX_MEDIA *media_ptr) +{ + +ULONG FAT_sector, last_sector; +UINT i, status, FATs; +UCHAR sectors_per_bit; + + + /* Determine how many FAT sectors each bit in the bit map represents. Depending on + the number of sectors in the primary FAT, each bit in this map may represent one + or more primary FAT sectors. Because of this, it is possible some FAT sectors that + were not changed may get flushed out to the secondary FAT. However, this method + provides very nice performance benefits during normal operation and is much more + reasonable than performing a total copy of the primary FAT to each secondary FAT + on media flush and media close. */ + if (media_ptr -> fx_media_sectors_per_FAT % (FX_FAT_MAP_SIZE << 3) == 0) + { + sectors_per_bit = (UCHAR)(media_ptr -> fx_media_sectors_per_FAT / (FX_FAT_MAP_SIZE << 3)); + } + else + { + sectors_per_bit = (UCHAR)(media_ptr -> fx_media_sectors_per_FAT / (FX_FAT_MAP_SIZE << 3) + 1); + } + + /* Loop through the FAT update map to mirror primary FAT sectors to secondary FAT(s). */ + for (i = 0; i < FX_FAT_MAP_SIZE << 3; i++) + { + + /* Determine if there are FAT changes specified by this entry. */ + if ((media_ptr -> fx_media_fat_secondary_update_map[i >> 3] & (1 << (i & 7))) == 0) + { + + /* No, look at the next bit map entry. */ + continue; + } + + /* Setup the parameters for performing the update. */ + FAT_sector = i * sectors_per_bit + media_ptr -> fx_media_reserved_sectors; + last_sector = FAT_sector + sectors_per_bit; + + /* Make sure the last update sector is within range. */ + if (last_sector > (media_ptr -> fx_media_sectors_per_FAT + media_ptr -> fx_media_reserved_sectors)) + { + last_sector = media_ptr -> fx_media_sectors_per_FAT + media_ptr -> fx_media_reserved_sectors; + } + + /* Loop to mirror primary FAT sectors to secondary FAT(s). */ + for (; FAT_sector < last_sector; FAT_sector++) + { + + /* Read the FAT sector. */ + status = _fx_utility_logical_sector_read(media_ptr, (ULONG64) FAT_sector, + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR); + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + /* Return the error status. */ + return(status); + } + + /* Pickup how many secondary FATs there are. */ + FATs = media_ptr -> fx_media_number_of_FATs - 1; + + /* Loop to update additional FAT entries. */ + while (FATs) + { + + /* Mirror main FAT sector write into the additional FATs. */ + status = _fx_utility_logical_sector_write(media_ptr, + ((ULONG64) FAT_sector) + ((ULONG64)FATs * (ULONG64)(media_ptr -> fx_media_sectors_per_FAT)), + media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR); + + /* Determine if an error occurred. */ + if (status != FX_SUCCESS) + { + + /* Return the error status. */ + return(status); + } + + /* Decrement the number of FATs. */ + FATs--; + } + } + } + + /* Clear the bit map that indicates primary FAT updates. */ + for (i = 0; i < FX_FAT_MAP_SIZE; i++) + { + + /* Clear each entry in the bit map. */ + media_ptr -> fx_media_fat_secondary_update_map[i] = 0; + } + + /* Return a successful completion. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_utility_FAT_sector_get.c b/common/src/fx_utility_FAT_sector_get.c new file mode 100644 index 0000000..e403fcc --- /dev/null +++ b/common/src/fx_utility_FAT_sector_get.c @@ -0,0 +1,111 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_utility_FAT_sector_get PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the sector of supplied FAT entry. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* cluster Cluster entry number */ +/* */ +/* OUTPUT */ +/* */ +/* return sector of FAT entry */ +/* */ +/* CALLS */ +/* */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +ULONG _fx_utility_FAT_sector_get(FX_MEDIA *media_ptr, ULONG cluster) +{ + +ULONG FAT_sector; +ULONG byte_offset; + + /* Determine which type of FAT is present. */ + if (media_ptr -> fx_media_12_bit_FAT) + { + + /* 12-bit FAT is present. */ + + /* Calculate the byte offset to the cluster entry. */ + byte_offset = (((ULONG)cluster << 1) + cluster) >> 1; + + } + else if (!media_ptr -> fx_media_32_bit_FAT) + { + + /* 16-bit FAT is present. */ + + /* Calculate the byte offset to the cluster entry. */ + byte_offset = (((ULONG)cluster) << 1); + } + else + { + + /* 32-bit FAT is present. */ + + /* Calculate the byte offset to the cluster entry. */ + byte_offset = (((ULONG)cluster) * 4); + } + + /* Calculate the FAT sector the requested FAT entry resides in. */ + FAT_sector = (byte_offset / media_ptr -> fx_media_bytes_per_sector) + + (ULONG)media_ptr -> fx_media_reserved_sectors; + + /* Return successful status. */ + return(FAT_sector); +} + diff --git a/common/src/fx_utility_absolute_path_get.c b/common/src/fx_utility_absolute_path_get.c new file mode 100644 index 0000000..5de13f1 --- /dev/null +++ b/common/src/fx_utility_absolute_path_get.c @@ -0,0 +1,32 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_utility.h" + + + diff --git a/common/src/fx_utility_logical_sector_cache_entry_read.c b/common/src/fx_utility_logical_sector_cache_entry_read.c new file mode 100644 index 0000000..e1580f8 --- /dev/null +++ b/common/src/fx_utility_logical_sector_cache_entry_read.c @@ -0,0 +1,359 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_utility_logical_sector_cache_entry_read PORTABLE C */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function handles logical sector cache read requests for the */ +/* logical sector read function. If the function finds the requested */ +/* sector in the cache, it setup the appropriate pointers and */ +/* returns a FX_NULL. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* logical_sector Logical sector number */ +/* previous_cache_entry Pointer to previous entry in */ +/* non-hashed cache */ +/* */ +/* OUTPUT */ +/* */ +/* FX_CACHED_SECTOR * Cache entry to setup */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _fx_utility_logical_sector_read Logical sector read function */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), and */ +/* added conditional to */ +/* disable cache, */ +/* resulting in version 6.1 */ +/* 01-31-2022 William E. Lamie Modified comment(s), fixed */ +/* errors without cache, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +FX_CACHED_SECTOR *_fx_utility_logical_sector_cache_entry_read(FX_MEDIA *media_ptr, ULONG64 logical_sector, + FX_CACHED_SECTOR **previous_cache_entry) +{ + +#ifndef FX_DISABLE_CACHE +FX_CACHED_SECTOR *cache_entry; +FX_CACHED_SECTOR temp_storage; +ULONG cache_size; +ULONG index; + + + /* Determine if the logical sector cache access should use the hash function. */ + if (media_ptr -> fx_media_sector_cache_hashed) + { + + /* Calculate the area of the cache for this logical sector. */ + + /* First compute the hashed value of this index by simply using the lower bits of + the sector number. */ + index = (ULONG)(logical_sector & media_ptr -> fx_media_sector_cache_hash_mask); + + /* Set the bit indicating there is one or more valid sectors at this cache index. */ + media_ptr -> fx_media_sector_cache_hashed_sector_valid |= ((ULONG)1) << (index % 32); + + /* Compute the actual array index by multiplying by the cache depth. */ + index = index * FX_SECTOR_CACHE_DEPTH; + + /* Build a pointer to the cache entry. */ + cache_entry = &(media_ptr -> fx_media_sector_cache[index]); + + /* Determine if the logical sector is in the cache - assuming the depth of the + sector cache is 4 entries. */ + if ((cache_entry -> fx_cached_sector_valid) && (cache_entry -> fx_cached_sector == logical_sector)) + { + + /* Yes, we found a match. Simply setup the pointer to this + buffer and return. */ + media_ptr -> fx_media_memory_buffer = cache_entry -> fx_cached_sector_memory_buffer; + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of logical sectors cache read hits. */ + media_ptr -> fx_media_logical_sector_cache_read_hits++; +#endif + /* Success, return to caller immediately! */ + return(FX_NULL); + } + else if (((cache_entry + 1) -> fx_cached_sector_valid) && ((cache_entry + 1) -> fx_cached_sector == logical_sector)) + { + + /* Yes, we found a match. Simply setup the pointer to this + buffer and return. */ + media_ptr -> fx_media_memory_buffer = (cache_entry + 1) -> fx_cached_sector_memory_buffer; + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of logical sectors cache read hits. */ + media_ptr -> fx_media_logical_sector_cache_read_hits++; +#endif + + /* Swap the first and second cache entries to keep the most recently used + at the top. */ + temp_storage.fx_cached_sector_memory_buffer = (cache_entry) -> fx_cached_sector_memory_buffer; + temp_storage.fx_cached_sector = (cache_entry) -> fx_cached_sector; + temp_storage.fx_cached_sector_buffer_dirty = (cache_entry) -> fx_cached_sector_buffer_dirty; + temp_storage.fx_cached_sector_valid = (cache_entry) -> fx_cached_sector_valid; + temp_storage.fx_cached_sector_type = (cache_entry) -> fx_cached_sector_type; + + (cache_entry) -> fx_cached_sector_memory_buffer = (cache_entry + 1) -> fx_cached_sector_memory_buffer; + (cache_entry) -> fx_cached_sector = (cache_entry + 1) -> fx_cached_sector; + (cache_entry) -> fx_cached_sector_buffer_dirty = (cache_entry + 1) -> fx_cached_sector_buffer_dirty; + (cache_entry) -> fx_cached_sector_valid = (cache_entry + 1) -> fx_cached_sector_valid; + (cache_entry) -> fx_cached_sector_type = (cache_entry + 1) -> fx_cached_sector_type; + + (cache_entry + 1) -> fx_cached_sector_memory_buffer = temp_storage.fx_cached_sector_memory_buffer; + (cache_entry + 1) -> fx_cached_sector = temp_storage.fx_cached_sector; + (cache_entry + 1) -> fx_cached_sector_buffer_dirty = temp_storage.fx_cached_sector_buffer_dirty; + (cache_entry + 1) -> fx_cached_sector_valid = temp_storage.fx_cached_sector_valid; + (cache_entry + 1) -> fx_cached_sector_type = temp_storage.fx_cached_sector_type; + + /* Success, return to caller immediately! */ + return(FX_NULL); + } + else if (((cache_entry + 2) -> fx_cached_sector_valid) && ((cache_entry + 2) -> fx_cached_sector == logical_sector)) + { + + /* Yes, we found a match. Simply setup the pointer to this + buffer and return. */ + media_ptr -> fx_media_memory_buffer = (cache_entry + 2) -> fx_cached_sector_memory_buffer; + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of logical sectors cache read hits. */ + media_ptr -> fx_media_logical_sector_cache_read_hits++; +#endif + + /* Move the third entry to the top and the first two entries down. */ + temp_storage.fx_cached_sector_memory_buffer = (cache_entry) -> fx_cached_sector_memory_buffer; + temp_storage.fx_cached_sector = (cache_entry) -> fx_cached_sector; + temp_storage.fx_cached_sector_buffer_dirty = (cache_entry) -> fx_cached_sector_buffer_dirty; + temp_storage.fx_cached_sector_valid = (cache_entry) -> fx_cached_sector_valid; + temp_storage.fx_cached_sector_type = (cache_entry) -> fx_cached_sector_type; + + (cache_entry) -> fx_cached_sector_memory_buffer = (cache_entry + 2) -> fx_cached_sector_memory_buffer; + (cache_entry) -> fx_cached_sector = (cache_entry + 2) -> fx_cached_sector; + (cache_entry) -> fx_cached_sector_buffer_dirty = (cache_entry + 2) -> fx_cached_sector_buffer_dirty; + (cache_entry) -> fx_cached_sector_valid = (cache_entry + 2) -> fx_cached_sector_valid; + (cache_entry) -> fx_cached_sector_type = (cache_entry + 2) -> fx_cached_sector_type; + + (cache_entry + 2) -> fx_cached_sector_memory_buffer = (cache_entry + 1) -> fx_cached_sector_memory_buffer; + (cache_entry + 2) -> fx_cached_sector = (cache_entry + 1) -> fx_cached_sector; + (cache_entry + 2) -> fx_cached_sector_buffer_dirty = (cache_entry + 1) -> fx_cached_sector_buffer_dirty; + (cache_entry + 2) -> fx_cached_sector_valid = (cache_entry + 1) -> fx_cached_sector_valid; + (cache_entry + 2) -> fx_cached_sector_type = (cache_entry + 1) -> fx_cached_sector_type; + + (cache_entry + 1) -> fx_cached_sector_memory_buffer = temp_storage.fx_cached_sector_memory_buffer; + (cache_entry + 1) -> fx_cached_sector = temp_storage.fx_cached_sector; + (cache_entry + 1) -> fx_cached_sector_buffer_dirty = temp_storage.fx_cached_sector_buffer_dirty; + (cache_entry + 1) -> fx_cached_sector_valid = temp_storage.fx_cached_sector_valid; + (cache_entry + 1) -> fx_cached_sector_type = temp_storage.fx_cached_sector_type; + + /* Success, return to caller immediately! */ + return(FX_NULL); + } + else if (((cache_entry + 3) -> fx_cached_sector_valid) && ((cache_entry + 3) -> fx_cached_sector == logical_sector)) + { + + /* Yes, we found a match. Simply setup the pointer to this + buffer and return. */ + media_ptr -> fx_media_memory_buffer = (cache_entry + 3) -> fx_cached_sector_memory_buffer; + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of logical sectors cache read hits. */ + media_ptr -> fx_media_logical_sector_cache_read_hits++; +#endif + + /* Move the last entry to the top and the first three entries down. */ + temp_storage.fx_cached_sector_memory_buffer = (cache_entry) -> fx_cached_sector_memory_buffer; + temp_storage.fx_cached_sector = (cache_entry) -> fx_cached_sector; + temp_storage.fx_cached_sector_buffer_dirty = (cache_entry) -> fx_cached_sector_buffer_dirty; + temp_storage.fx_cached_sector_valid = (cache_entry) -> fx_cached_sector_valid; + temp_storage.fx_cached_sector_type = (cache_entry) -> fx_cached_sector_type; + + (cache_entry) -> fx_cached_sector_memory_buffer = (cache_entry + 3) -> fx_cached_sector_memory_buffer; + (cache_entry) -> fx_cached_sector = (cache_entry + 3) -> fx_cached_sector; + (cache_entry) -> fx_cached_sector_buffer_dirty = (cache_entry + 3) -> fx_cached_sector_buffer_dirty; + (cache_entry) -> fx_cached_sector_valid = (cache_entry + 3) -> fx_cached_sector_valid; + (cache_entry) -> fx_cached_sector_type = (cache_entry + 3) -> fx_cached_sector_type; + + (cache_entry + 3) -> fx_cached_sector_memory_buffer = (cache_entry + 2) -> fx_cached_sector_memory_buffer; + (cache_entry + 3) -> fx_cached_sector = (cache_entry + 2) -> fx_cached_sector; + (cache_entry + 3) -> fx_cached_sector_buffer_dirty = (cache_entry + 2) -> fx_cached_sector_buffer_dirty; + (cache_entry + 3) -> fx_cached_sector_valid = (cache_entry + 2) -> fx_cached_sector_valid; + (cache_entry + 3) -> fx_cached_sector_type = (cache_entry + 2) -> fx_cached_sector_type; + + (cache_entry + 2) -> fx_cached_sector_memory_buffer = (cache_entry + 1) -> fx_cached_sector_memory_buffer; + (cache_entry + 2) -> fx_cached_sector = (cache_entry + 1) -> fx_cached_sector; + (cache_entry + 2) -> fx_cached_sector_buffer_dirty = (cache_entry + 1) -> fx_cached_sector_buffer_dirty; + (cache_entry + 2) -> fx_cached_sector_valid = (cache_entry + 1) -> fx_cached_sector_valid; + (cache_entry + 2) -> fx_cached_sector_type = (cache_entry + 1) -> fx_cached_sector_type; + + (cache_entry + 1) -> fx_cached_sector_memory_buffer = temp_storage.fx_cached_sector_memory_buffer; + (cache_entry + 1) -> fx_cached_sector = temp_storage.fx_cached_sector; + (cache_entry + 1) -> fx_cached_sector_buffer_dirty = temp_storage.fx_cached_sector_buffer_dirty; + (cache_entry + 1) -> fx_cached_sector_valid = temp_storage.fx_cached_sector_valid; + (cache_entry + 1) -> fx_cached_sector_type = temp_storage.fx_cached_sector_type; + + /* Success, return to caller immediately! */ + return(FX_NULL); + } + + /* At this point we have a cache miss. We need to move all of the sectors down one slot, swapping + the 4th entry with the first. */ + temp_storage.fx_cached_sector_memory_buffer = (cache_entry + 3) -> fx_cached_sector_memory_buffer; + temp_storage.fx_cached_sector = (cache_entry + 3) -> fx_cached_sector; + temp_storage.fx_cached_sector_buffer_dirty = (cache_entry + 3) -> fx_cached_sector_buffer_dirty; + temp_storage.fx_cached_sector_valid = (cache_entry + 3) -> fx_cached_sector_valid; + temp_storage.fx_cached_sector_type = (cache_entry + 3) -> fx_cached_sector_type; + + (cache_entry + 3) -> fx_cached_sector_memory_buffer = (cache_entry + 2) -> fx_cached_sector_memory_buffer; + (cache_entry + 3) -> fx_cached_sector = (cache_entry + 2) -> fx_cached_sector; + (cache_entry + 3) -> fx_cached_sector_buffer_dirty = (cache_entry + 2) -> fx_cached_sector_buffer_dirty; + (cache_entry + 3) -> fx_cached_sector_valid = (cache_entry + 2) -> fx_cached_sector_valid; + (cache_entry + 3) -> fx_cached_sector_type = (cache_entry + 2) -> fx_cached_sector_type; + + (cache_entry + 2) -> fx_cached_sector_memory_buffer = (cache_entry + 1) -> fx_cached_sector_memory_buffer; + (cache_entry + 2) -> fx_cached_sector = (cache_entry + 1) -> fx_cached_sector; + (cache_entry + 2) -> fx_cached_sector_buffer_dirty = (cache_entry + 1) -> fx_cached_sector_buffer_dirty; + (cache_entry + 2) -> fx_cached_sector_valid = (cache_entry + 1) -> fx_cached_sector_valid; + (cache_entry + 2) -> fx_cached_sector_type = (cache_entry + 1) -> fx_cached_sector_type; + + (cache_entry + 1) -> fx_cached_sector_memory_buffer = (cache_entry) -> fx_cached_sector_memory_buffer; + (cache_entry + 1) -> fx_cached_sector = (cache_entry) -> fx_cached_sector; + (cache_entry + 1) -> fx_cached_sector_buffer_dirty = (cache_entry) -> fx_cached_sector_buffer_dirty; + (cache_entry + 1) -> fx_cached_sector_valid = (cache_entry) -> fx_cached_sector_valid; + (cache_entry + 1) -> fx_cached_sector_type = (cache_entry) -> fx_cached_sector_type; + + (cache_entry) -> fx_cached_sector_memory_buffer = temp_storage.fx_cached_sector_memory_buffer; + (cache_entry) -> fx_cached_sector = temp_storage.fx_cached_sector; + (cache_entry) -> fx_cached_sector_buffer_dirty = temp_storage.fx_cached_sector_buffer_dirty; + (cache_entry) -> fx_cached_sector_valid = temp_storage.fx_cached_sector_valid; + (cache_entry) -> fx_cached_sector_type = temp_storage.fx_cached_sector_type; + + /* Set the previous pointer to NULL to avoid the linked list update below. */ + *previous_cache_entry = FX_NULL; + } + else + { + + /* Search for an entry in the cache that matches this request. */ + cache_size = media_ptr -> fx_media_sector_cache_size; + cache_entry = media_ptr -> fx_media_sector_cache_list_ptr; + *previous_cache_entry = FX_NULL; + + /* Look at the cache entries until a match is found or the end of + the cache is reached. */ + while (cache_size--) + { + + /* Determine if the requested sector has been found. */ + if ((cache_entry -> fx_cached_sector_valid) && (cache_entry -> fx_cached_sector == logical_sector)) + { + + /* Yes, we found a match. Simply setup the pointer to this + buffer and return. */ + media_ptr -> fx_media_memory_buffer = cache_entry -> fx_cached_sector_memory_buffer; + + /* Determine if we need to update the last used list. */ + if (*previous_cache_entry) + { + + /* Yes, the current entry is not at the front of the list + so we need to change the order. */ + + /* Link the previous entry to this entry's next pointer. */ + (*previous_cache_entry) -> fx_cached_sector_next_used = + cache_entry -> fx_cached_sector_next_used; + + /* Place this entry at the head of the list. */ + cache_entry -> fx_cached_sector_next_used = + media_ptr -> fx_media_sector_cache_list_ptr; + media_ptr -> fx_media_sector_cache_list_ptr = cache_entry; + } + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of logical sectors cache read hits. */ + media_ptr -> fx_media_logical_sector_cache_read_hits++; +#endif + + /* Success, return to caller immediately! */ + return(FX_NULL); + } + + /* Otherwise, we have not found the cached entry yet. */ + + /* If there are more entries, move to the next one. */ + if (cache_entry -> fx_cached_sector_next_used) + { + + *previous_cache_entry = cache_entry; + cache_entry = cache_entry -> fx_cached_sector_next_used; + } + } + } + + /* The requested sector is not in cache, return the last cache entry. */ + return(cache_entry); +#else + FX_PARAMETER_NOT_USED(media_ptr); + FX_PARAMETER_NOT_USED(logical_sector); + FX_PARAMETER_NOT_USED(previous_cache_entry); + return(FX_NULL); +#endif /* FX_DISABLE_CACHE */ +} + diff --git a/common/src/fx_utility_logical_sector_flush.c b/common/src/fx_utility_logical_sector_flush.c new file mode 100644 index 0000000..da1d47f --- /dev/null +++ b/common/src/fx_utility_logical_sector_flush.c @@ -0,0 +1,522 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_utility_logical_sector_flush PORTABLE C */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function handles logical sector flush requests for all FileX */ +/* components. It will process all dirty logical sectors in the */ +/* logical sector cache within the range specified. This function */ +/* optionally invalidates sectors. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* starting_sector Starting sector number */ +/* sectors Number of sectors */ +/* invalidate Invalidate flag */ +/* (FX_TRUE -> invalidate) */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* I/O Driver */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), and */ +/* added conditional to */ +/* disable cache, */ +/* resulting in version 6.1 */ +/* 01-31-2022 William E. Lamie Modified comment(s), fixed */ +/* errors without cache, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +UINT _fx_utility_logical_sector_flush(FX_MEDIA *media_ptr, ULONG64 starting_sector, ULONG64 sectors, UINT invalidate) +{ + +#ifndef FX_DISABLE_CACHE +FX_CACHED_SECTOR *cache_entry; +UINT cache_size; +UINT i, bit_set, use_starting_sector; +ULONG index; +ULONG remaining_valid; +ULONG remaining_dirty; +ULONG64 ending_sector; +ULONG valid_bit_map; + + +/* Extended port-specific processing macro, which is by default defined to white space. */ +FX_UTILITY_LOGICAL_SECTOR_FLUSH_EXTENSION + +/* Calculate the ending sector. */ + ending_sector = starting_sector + sectors - 1; + + /* Pickup the number of dirty sectors currently in the cache. */ + remaining_dirty = media_ptr -> fx_media_sector_cache_dirty_count; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_MEDIA_FLUSH, media_ptr, media_ptr -> fx_media_sector_cache_dirty_count, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Determine what type of cache configuration we have. */ + if (media_ptr -> fx_media_sector_cache_hashed == FX_FALSE) + { + + /* Linear cache present, simply walk through the search list until + an unused cache entry is present. */ + + /* Flush and invalidate the internal logical sector cache. */ + cache_size = media_ptr -> fx_media_sector_cache_size; + cache_entry = media_ptr -> fx_media_sector_cache_list_ptr; + + /* Look at the cache entries that have been written to. */ + while ((cache_size--) && (cache_entry -> fx_cached_sector)) + { + + /* Determine if invalidation is not required and there are no + more dirty sectors. */ + if ((remaining_dirty == 0) && (invalidate == FX_FALSE)) + { + + /* Yes, nothing left to do. */ + break; + } + + /* Determine if there are any more sectors to process. */ + if (sectors == 0) + { + + /* No more sectors required to process. */ + break; + } + + /* Determine if this cached sector is within the specified range and is valid. */ + if ((cache_entry -> fx_cached_sector_valid) && + (cache_entry -> fx_cached_sector >= starting_sector) && + (cache_entry -> fx_cached_sector <= ending_sector)) + { + + /* Yes, the cache entry is valid and within the specified range. Determine if + the requested sector has been written to. */ + if (cache_entry -> fx_cached_sector_buffer_dirty) + { + + /* Yes, write the cached sector out to the media. */ + + /* Check for write protect at the media level (set by driver). */ + if (media_ptr -> fx_media_driver_write_protect == FX_FALSE) + { + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of driver write sector(s) requests. */ + media_ptr -> fx_media_driver_write_requests++; +#endif + + /* Build write request to the driver. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_WRITE; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = cache_entry -> fx_cached_sector_memory_buffer; +#ifdef FX_DRIVER_USE_64BIT_LBA + media_ptr -> fx_media_driver_logical_sector = cache_entry -> fx_cached_sector; +#else + media_ptr -> fx_media_driver_logical_sector = (ULONG)cache_entry -> fx_cached_sector; +#endif + media_ptr -> fx_media_driver_sectors = 1; + media_ptr -> fx_media_driver_sector_type = cache_entry -> fx_cached_sector_type; + + /* Sectors other than FX_DATA_SECTOR will never be dirty when FX_FAULT_TOLERANT is defined. */ +#ifndef FX_FAULT_TOLERANT + /* Determine if the system write flag needs to be set. */ + if (cache_entry -> fx_cached_sector_type != FX_DATA_SECTOR) + { + + /* Yes, a system sector write is present so set the flag. The driver + can use this flag to make extra safeguards in writing the sector + out, yielding more fault tolerance. */ + media_ptr -> fx_media_driver_system_write = FX_TRUE; + } +#endif /* FX_FAULT_TOLERANT */ + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, cache_entry -> fx_cached_sector, 1, cache_entry -> fx_cached_sector_memory_buffer, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Invoke the driver to write the sector. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Clear the system write flag. */ + media_ptr -> fx_media_driver_system_write = FX_FALSE; + + /* Check for successful completion. */ + if (media_ptr -> fx_media_driver_status) + { + + /* Error writing a cached sector out. Return the + error status. */ + return(media_ptr -> fx_media_driver_status); + } + + /* Clear the buffer dirty flag since it has been flushed + out. */ + cache_entry -> fx_cached_sector_buffer_dirty = FX_FALSE; + + /* Decrement the number of dirty sectors currently in the cache. */ + media_ptr -> fx_media_sector_cache_dirty_count--; + remaining_dirty--; + } + } + + /* Determine if the invalidate option is specified. */ + if (invalidate) + { + + /* Invalidate the cache entry. */ + cache_entry -> fx_cached_sector_valid = FX_FALSE; + + /* Place all ones in the sector number. */ + cache_entry -> fx_cached_sector = (~(ULONG64)0); + + /* Determine if this sector is still dirty, this could be the case if + write protection was turned on. */ + if (cache_entry -> fx_cached_sector_buffer_dirty) + { + + /* Yes, clear the dirty flag. */ + cache_entry -> fx_cached_sector_buffer_dirty = FX_FALSE; + + /* Decrement the number of dirty sectors currently in the cache. */ + media_ptr -> fx_media_sector_cache_dirty_count--; + remaining_dirty--; + } + } + + /* Decrement the number of sectors in the range that have been processed. */ + sectors--; + } + + /* Move to the next entry in the sector cache. */ + cache_entry = cache_entry -> fx_cached_sector_next_used; + } + } + else + { + + /* Hashed cache is present. Pickup the cache size. */ + cache_size = media_ptr -> fx_media_sector_cache_size; + + /* Initialize the loop control parameters. */ + bit_set = 0; + valid_bit_map = media_ptr -> fx_media_sector_cache_hashed_sector_valid; + + /* Determine how to process the hashed cache based on the number of sectors + to process. If the sequential sector range is less than the bit map size, + simply use the starting sector to derive the index into the cache. */ + if (sectors < 32) + { + use_starting_sector = FX_TRUE; + } + else + { + use_starting_sector = FX_FALSE; + } + + /* Determine if there is anything valid in the cache. */ + while (valid_bit_map) + { + + /* Determine if invalidation is not required and there are no + more dirty sectors. */ + if ((remaining_dirty == 0) && (invalidate == FX_FALSE)) + { + + /* Yes, nothing left to do. */ + break; + } + + /* Determine if there are any more sectors to process. */ + if ((sectors == 0) || (starting_sector > ending_sector)) + { + + /* No more sectors required to process. */ + break; + } + + /* Determine how to compute the hash index. */ + if (use_starting_sector) + { + + /* Calculate the hash value of this sector using the lower bits. */ + index = (ULONG)(starting_sector & media_ptr -> fx_media_sector_cache_hash_mask); + + /* Calculate the bit set indicating there is one or more valid sectors at this cache index. */ + bit_set = (index % 32); + + /* Compute the actual array index by multiplying by the cache depth. */ + index = (bit_set * FX_SECTOR_CACHE_DEPTH); + } + else + { + + /* Walk the bit map to find the next valid entry. */ + + /* Find the next set bit. */ + while ((valid_bit_map & 1) == 0) + { + + /* Otherwise, shift down the bit in the bit map. */ + valid_bit_map = valid_bit_map >> 1; + + /* Increment the set bit marker. */ + bit_set++; + } + + /* Compute the first actual index into the hashed cache. */ + index = (bit_set * FX_SECTOR_CACHE_DEPTH); + } + + /* At this point, bit_set represents the next group of hashed sectors that could + have valid cache entries and index represents the index into the sector cache + of that sector group. */ + + /* Clear the remaining valid sectors for this entry in the bit map. */ + remaining_valid = 0; + + /* Loop to check the corresponding hash entries. */ + do + { + + /* Setup pointer to the cache entry. */ + cache_entry = &(media_ptr -> fx_media_sector_cache[index]); + + /* Loop to examine the full depth of the hashed cache. */ + for (i = 0; i < 4; i++) + { + + /* Determine if this cached sector is within the specified range and is valid. */ + if ((cache_entry -> fx_cached_sector_valid) && + (cache_entry -> fx_cached_sector >= starting_sector) && + (cache_entry -> fx_cached_sector <= ending_sector)) + { + + /* Determine if the requested sector has been written to. */ + if (cache_entry -> fx_cached_sector_buffer_dirty) + { + + + /* Yes, write the cached sector out to the media. */ + + /* Check for write protect at the media level (set by driver). */ + if (media_ptr -> fx_media_driver_write_protect == FX_FALSE) + { + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of driver write sector(s) requests. */ + media_ptr -> fx_media_driver_write_requests++; +#endif + + /* Build Write request to the driver. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_WRITE; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = cache_entry -> fx_cached_sector_memory_buffer; +#ifdef FX_DRIVER_USE_64BIT_LBA + media_ptr -> fx_media_driver_logical_sector = cache_entry -> fx_cached_sector; +#else + media_ptr -> fx_media_driver_logical_sector = (ULONG)cache_entry -> fx_cached_sector; +#endif + media_ptr -> fx_media_driver_sectors = 1; + media_ptr -> fx_media_driver_sector_type = cache_entry -> fx_cached_sector_type; + + /* Sectors other than FX_DATA_SECTOR will never be dirty when FX_FAULT_TOLERANT is defined. */ +#ifndef FX_FAULT_TOLERANT + /* Determine if the system write flag needs to be set. */ + if (cache_entry -> fx_cached_sector_type != FX_DATA_SECTOR) + { + + /* Yes, a system sector write is present so set the flag. The driver + can use this flag to make extra safeguards in writing the sector + out, yielding more fault tolerance. */ + media_ptr -> fx_media_driver_system_write = FX_TRUE; + } +#endif /* FX_FAULT_TOLERANT */ + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, cache_entry -> fx_cached_sector, 1, cache_entry -> fx_cached_sector_memory_buffer, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Invoke the driver to write the sector. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Clear the system write flag. */ + media_ptr -> fx_media_driver_system_write = FX_FALSE; + + /* Check for successful completion. */ + if (media_ptr -> fx_media_driver_status) + { + + /* Error writing a cached sector out. Return the + error status. */ + return(media_ptr -> fx_media_driver_status); + } + + /* Clear the buffer dirty flag since it has been flushed + out. */ + cache_entry -> fx_cached_sector_buffer_dirty = FX_FALSE; + + /* Decrement the number of dirty sectors currently in the cache. */ + media_ptr -> fx_media_sector_cache_dirty_count--; + remaining_dirty--; + } + } + + /* Determine if the invalidate option is specified. */ + if (invalidate) + { + + /* Invalidate the cache entry. */ + cache_entry -> fx_cached_sector_valid = FX_FALSE; + + /* Place all ones in the sector number. */ + cache_entry -> fx_cached_sector = (~(ULONG64)0); + + /* Determine if this sector is still dirty, this could be the case if + write protection was turned on. */ + if (cache_entry -> fx_cached_sector_buffer_dirty) + { + + /* Yes, clear the dirty flag. */ + cache_entry -> fx_cached_sector_buffer_dirty = FX_FALSE; + + /* Decrement the number of dirty sectors currently in the cache. */ + media_ptr -> fx_media_sector_cache_dirty_count--; + remaining_dirty--; + } + } + + /* Decrement the number of sectors in the range that have been processed. */ + sectors--; + } + else + { + + /* Determine if the sector is valid. */ + if (cache_entry -> fx_cached_sector_valid) + { + + /* Increment the number of still remaining but out of range sectors. */ + remaining_valid++; + } + } + + /* Determine if invalidation is not required and there are no + more dirty sectors. */ + if ((remaining_dirty == 0) && (invalidate == FX_FALSE)) + { + + /* Yes, nothing left to do. */ + break; + } + + /* Determine if there are any more sectors to process. */ + if ((sectors == 0) && (invalidate == FX_FALSE)) + { + + /* No more sectors required to process. */ + break; + } + + /* Move to the next cache entry. */ + cache_entry++; + } + + /* Move the index to the next position since the bit map can only represent 32 + cache entries. */ + index = index + (32 * FX_SECTOR_CACHE_DEPTH); + } while (index < cache_size); + + /* Determine if invalidation was required and there are no more valid sectors + associated with this bit position. */ + if ((invalidate) && (remaining_valid == 0)) + { + + /* Clear this bit position. */ + media_ptr -> fx_media_sector_cache_hashed_sector_valid &= ~(((ULONG)1) << bit_set); + } + + /* Determine if the starting sector is being used for examination of the hash. */ + if (use_starting_sector) + { + + /* Move to the next sector. */ + starting_sector++; + } + else + { + + /* Move to next bit in the map. */ + valid_bit_map = valid_bit_map >> 1; + + /* Increment the set bit marker. */ + bit_set++; + } + } + } +#else + FX_PARAMETER_NOT_USED(media_ptr); + FX_PARAMETER_NOT_USED(starting_sector); + FX_PARAMETER_NOT_USED(sectors); + FX_PARAMETER_NOT_USED(invalidate); +#endif /* FX_DISABLE_CACHE */ + + /* If we get here, return successful status to the caller. */ + return(FX_SUCCESS); +} + diff --git a/common/src/fx_utility_logical_sector_read.c b/common/src/fx_utility_logical_sector_read.c new file mode 100644 index 0000000..54140c5 --- /dev/null +++ b/common/src/fx_utility_logical_sector_read.c @@ -0,0 +1,647 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_utility.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_utility_logical_sector_read PORTABLE C */ +/* 6.2.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function handles logical sector read requests for all FileX */ +/* components. If the logical sector is currently in the logical */ +/* sector cache, the function simply sets the appropriate pointer and */ +/* returns a successful status to the caller. Otherwise, physical I/O */ +/* is requested through the corresponding I/O driver. */ +/* */ +/* Note: Conversion of the logical sector is done inside the driver. */ +/* This results in a performance boost for FLASH or RAM media */ +/* devices. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* logical_sector Logical sector number */ +/* buffer_ptr Pointer of receiving buffer */ +/* sectors Number of sectors to read */ +/* sector_type Type of sector(s) to read */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_logical_sector_cache_entry_read */ +/* Read logical sector cache */ +/* _fx_utility_logical_sector_flush Flush and invalidate sectors */ +/* that overlap with non-cache */ +/* sector I/O. */ +/* _fx_utility_memory_copy Copy cache sector */ +/* _fx_fault_tolerant_read_directory_sector */ +/* Read directory sector */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* verified memcpy usage, and */ +/* added conditional to */ +/* disable cache, */ +/* resulting in version 6.1 */ +/* 04-02-2021 Bhupendra Naphade Modified comment(s), */ +/* updated check for logical */ +/* sector value, */ +/* resulting in version 6.1.6 */ +/* 10-31-2022 Tiejun Zhou Modified comment(s), */ +/* fixed memory buffer when */ +/* cache is disabled, */ +/* resulting in version 6.2.0 */ +/* */ +/**************************************************************************/ +UINT _fx_utility_logical_sector_read(FX_MEDIA *media_ptr, ULONG64 logical_sector, + VOID *buffer_ptr, ULONG sectors, UCHAR sector_type) +{ +#ifndef FX_DISABLE_CACHE +FX_CACHED_SECTOR *cache_entry; +FX_CACHED_SECTOR *previous_cache_entry; +ULONG64 end_sector; +#endif /* FX_DISABLE_CACHE */ + +#ifdef FX_ENABLE_FAULT_TOLERANT +UINT status; +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Determine if the request is for FAT sector. */ + if (sector_type == FX_FAT_SECTOR) + { + + /* Increment the number of FAT sector reads. */ + media_ptr -> fx_media_fat_sector_reads++; + } + + /* Increment the number of logical sectors read. */ + media_ptr -> fx_media_logical_sector_reads++; +#endif + + /* Extended port-specific processing macro, which is by default defined to white space. */ + FX_UTILITY_LOGICAL_SECTOR_READ_EXTENSION + +#ifndef FX_DISABLE_CACHE + /* Determine if the request is for the internal media buffer area. */ + if ((((UCHAR *)buffer_ptr) >= media_ptr -> fx_media_memory_buffer) && + (((UCHAR *)buffer_ptr) <= media_ptr -> fx_media_sector_cache_end)) + { + + /* Internal cache buffer is requested. */ + + /* Examine the logical sector cache. */ + cache_entry = _fx_utility_logical_sector_cache_entry_read(media_ptr, logical_sector, &previous_cache_entry); + + /* Was the sector found? */ + if (cache_entry == FX_NULL) + { + + /* Yes, the sector was found. Return success! */ + return(FX_SUCCESS); + } + + /* At this point, we need to read in a sector from the media. */ + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of logical sectors cache read misses. */ + media_ptr -> fx_media_logical_sector_cache_read_misses++; +#endif + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_LOG_SECTOR_CACHE_MISS, media_ptr, logical_sector, media_ptr -> fx_media_logical_sector_cache_read_misses, media_ptr -> fx_media_sector_cache_size, FX_TRACE_INTERNAL_EVENTS, 0, 0) +#else + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_LOG_SECTOR_CACHE_MISS, media_ptr, logical_sector, 0, media_ptr -> fx_media_sector_cache_size, FX_TRACE_INTERNAL_EVENTS, 0, 0) +#endif + + /* First, check and see if the last used entry has been + modified. */ + if ((cache_entry -> fx_cached_sector_valid) && + (cache_entry -> fx_cached_sector_buffer_dirty)) + { + + /* Yes, we need to flush this buffer out to the physical media + before we read in the new buffer. */ + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of driver write sector(s) requests. */ + media_ptr -> fx_media_driver_write_requests++; +#endif + + /* Build write request to the driver. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_WRITE; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = cache_entry -> fx_cached_sector_memory_buffer; +#ifdef FX_DRIVER_USE_64BIT_LBA + media_ptr -> fx_media_driver_logical_sector = cache_entry -> fx_cached_sector; +#else + media_ptr -> fx_media_driver_logical_sector = (ULONG)cache_entry -> fx_cached_sector; +#endif + media_ptr -> fx_media_driver_sectors = 1; + media_ptr -> fx_media_driver_sector_type = cache_entry -> fx_cached_sector_type; + + /* Determine if the sector is a data sector or a system sector. */ + if (cache_entry -> fx_cached_sector_type != FX_DATA_SECTOR) + { + + /* System sector is present. */ + media_ptr -> fx_media_driver_system_write = FX_TRUE; + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, cache_entry -> fx_cached_sector, 1, cache_entry -> fx_cached_sector_memory_buffer, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Invoke the driver to write the sector. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Clear the system write flag. */ + media_ptr -> fx_media_driver_system_write = FX_FALSE; + + /* Check for successful completion. */ + if (media_ptr -> fx_media_driver_status) + { + + /* Error writing a cached sector out. Return the + error status. */ + return(media_ptr -> fx_media_driver_status); + } + + /* Clear the buffer dirty flag since it has been flushed + out. */ + cache_entry -> fx_cached_sector_buffer_dirty = FX_FALSE; + + /* Decrement the number of outstanding dirty cache entries. */ + media_ptr -> fx_media_sector_cache_dirty_count--; + } + + /* At this point, we can go out and setup this cached sector + entry. */ + + /* Compare against logical sector to make sure it is valid. */ + if (logical_sector >= media_ptr -> fx_media_total_sectors) + { + return(FX_SECTOR_INVALID); + } + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of driver read sector(s) requests. */ + media_ptr -> fx_media_driver_read_requests++; +#endif + + /* Build Read request to the driver. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_READ; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = cache_entry -> fx_cached_sector_memory_buffer; +#ifdef FX_DRIVER_USE_64BIT_LBA + media_ptr -> fx_media_driver_logical_sector = logical_sector; +#else + media_ptr -> fx_media_driver_logical_sector = (ULONG)logical_sector; +#endif + media_ptr -> fx_media_driver_sectors = 1; + media_ptr -> fx_media_driver_sector_type = sector_type; + + /* Determine if the sector is a data sector or a system sector. */ + if (sector_type == FX_DATA_SECTOR) + { + + /* Data sector is present. */ + media_ptr -> fx_media_driver_data_sector_read = FX_TRUE; + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_READ, media_ptr, logical_sector, 1, cache_entry -> fx_cached_sector_memory_buffer, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Invoke the driver to read the sector. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Clear data sector is present flag. */ + media_ptr -> fx_media_driver_data_sector_read = FX_FALSE; + + /* Determine if the read was successful. */ + if (media_ptr -> fx_media_driver_status == FX_SUCCESS) + { + + /* Remember the sector number. */ + cache_entry -> fx_cached_sector = logical_sector; + + /* Make the cache entry valid. */ + cache_entry -> fx_cached_sector_valid = FX_TRUE; + + /* Remember the sector type. */ + cache_entry -> fx_cached_sector_type = sector_type; + + /* Place this entry that the head of the cached sector + list. */ + + /* Determine if we need to update the last used list. */ + if (previous_cache_entry) + { + + /* Yes, the current entry is not at the front of the list + so we need to change the order. */ + + /* Link the previous entry to this entry's next pointer. */ + previous_cache_entry -> fx_cached_sector_next_used = + cache_entry -> fx_cached_sector_next_used; + + /* Place this entry at the head of the list. */ + cache_entry -> fx_cached_sector_next_used = + media_ptr -> fx_media_sector_cache_list_ptr; + media_ptr -> fx_media_sector_cache_list_ptr = cache_entry; + } + +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled && + (media_ptr -> fx_media_fault_tolerant_state & FX_FAULT_TOLERANT_STATE_STARTED) && + (sector_type == FX_DIRECTORY_SECTOR)) + { + + /* Read sector from log file. */ + status = _fx_fault_tolerant_read_directory_sector(media_ptr, logical_sector, cache_entry -> fx_cached_sector_memory_buffer, 1); + + /* Check for successful completion. */ + if (status) + { + + /* Return the error status. */ + return(status); + } + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + } + else + { + + /* Invalidate the cache entry on read errors. */ + cache_entry -> fx_cached_sector_valid = FX_FALSE; + + /* Put all ones in the sector value. */ + cache_entry -> fx_cached_sector = (~(ULONG64)0); + } + + /* Simply setup the pointer to this buffer and return. */ + media_ptr -> fx_media_memory_buffer = cache_entry -> fx_cached_sector_memory_buffer; + + /* Return the driver status. */ + return(media_ptr -> fx_media_driver_status); + } +#else + if ((logical_sector == media_ptr -> fx_media_memory_buffer_sector) && (sectors == 1) && (buffer_ptr == media_ptr -> fx_media_memory_buffer)) + { +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled && + (media_ptr -> fx_media_fault_tolerant_state & FX_FAULT_TOLERANT_STATE_STARTED) && + (sector_type == FX_DIRECTORY_SECTOR)) + { + + /* Read sector from log file. */ + status = _fx_fault_tolerant_read_directory_sector(media_ptr, logical_sector, buffer_ptr, 1); + + /* Check for successful completion. */ + if (status) + { + + /* Return the error status. */ + return(status); + } + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + return(FX_SUCCESS); + } +#endif + else + { + + /* Direct I/O to application buffer area. */ + + /* Compare against logical sector to make sure it is valid. */ + if ((logical_sector + sectors - 1) > (ULONG)media_ptr -> fx_media_total_sectors) + { + return(FX_SECTOR_INVALID); + } + +#ifndef FX_DISABLE_CACHE + /* Attempt to fill the beginning of the buffer from cached sectors. */ + while (sectors) + { + + /* Determine if the sector is in the cache. */ + if (_fx_utility_logical_sector_cache_entry_read(media_ptr, logical_sector, &previous_cache_entry)) + { + + /* Not in the cache - get out of the loop! */ + break; + } + + /* Yes, sector is in the cache. Copy the data from the cache to the destination buffer. */ + _fx_utility_memory_copy(media_ptr -> fx_media_memory_buffer, buffer_ptr, media_ptr -> fx_media_bytes_per_sector); /* Use case of memcpy is verified. */ + + /* Advance the destination buffer. */ + buffer_ptr = ((UCHAR *)buffer_ptr) + media_ptr -> fx_media_bytes_per_sector; + + /* Advance the sector and decrement the number of sectors left. */ + logical_sector++; + sectors--; + } + + /* Calculate the end sector. */ + end_sector = logical_sector + sectors - 1; + + /* Attempt to fill the end of the buffer from the opposite direction. */ + while (sectors) + { + + /* Determine if the sector is in the cache. */ + if (_fx_utility_logical_sector_cache_entry_read(media_ptr, end_sector, &previous_cache_entry)) + { + + /* Not in the cache - get out of the loop! */ + break; + } + + /* Yes, sector is in the cache. Copy the data from the cache to the destination buffer. */ + _fx_utility_memory_copy(media_ptr -> fx_media_memory_buffer, /* Use case of memcpy is verified. */ + ((UCHAR *)buffer_ptr) + ((sectors - 1) * media_ptr -> fx_media_bytes_per_sector), + media_ptr -> fx_media_bytes_per_sector); + + /* Move sector to previous sector and decrement the number of sectors left. */ + end_sector--; + sectors--; + } + + /* Determine if there are still sectors left to read. */ + if (sectors == 0) + { + + /* No more sectors to read - return success! */ + return(FX_SUCCESS); + } + + /* Flush and invalidate any entries in the cache that are in this direct I/O read request range. */ + _fx_utility_logical_sector_flush(media_ptr, logical_sector, (ULONG64) sectors, FX_TRUE); +#endif /* FX_DISABLE_CACHE */ + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of driver read sector(s) requests. */ + media_ptr -> fx_media_driver_read_requests++; +#endif + + /* Build read request to the driver. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_READ; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = buffer_ptr; +#ifdef FX_DRIVER_USE_64BIT_LBA + media_ptr -> fx_media_driver_logical_sector = logical_sector; +#else + media_ptr -> fx_media_driver_logical_sector = (ULONG)logical_sector; +#endif + media_ptr -> fx_media_driver_sectors = sectors; + media_ptr -> fx_media_driver_sector_type = sector_type; + + /* Determine if the sector is a data sector or a system sector. */ + if (sector_type == FX_DATA_SECTOR) + { + + /* Data sector is present. */ + media_ptr -> fx_media_driver_data_sector_read = FX_TRUE; + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_READ, media_ptr, logical_sector, sectors, buffer_ptr, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Invoke the driver to read the sector. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Clear data sector is present flag. */ + media_ptr -> fx_media_driver_data_sector_read = FX_FALSE; + +#ifdef FX_DISABLE_CACHE + if ((media_ptr -> fx_media_driver_status == FX_SUCCESS) && (sectors == 1) && (buffer_ptr == media_ptr -> fx_media_memory_buffer)) + { + media_ptr -> fx_media_memory_buffer_sector = logical_sector; +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled && + (media_ptr -> fx_media_fault_tolerant_state & FX_FAULT_TOLERANT_STATE_STARTED) && + (sector_type == FX_DIRECTORY_SECTOR)) + { + + /* Read sector from log file. */ + status = _fx_fault_tolerant_read_directory_sector(media_ptr, logical_sector, buffer_ptr, 1); + + /* Check for successful completion. */ + if (status) + { + + /* Return the error status. */ + return(status); + } + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + return(FX_SUCCESS); + } +#endif /* FX_DISABLE_CACHE */ + +#ifndef FX_DISABLE_DIRECT_DATA_READ_CACHE_FILL + + /* Determine if the read was successful and if number of sectors just read will + reasonably fit into the cache. */ + if ((media_ptr -> fx_media_driver_status == FX_SUCCESS) && (sectors < (media_ptr -> fx_media_sector_cache_size / 4))) + { + + /* Yes, read of direct sectors was successful. */ + + /* Copy the sectors directly read into the cache so they are available on + subsequent read requests. */ + while (sectors) + { + + /* Attempt to read the cache entry. */ + cache_entry = _fx_utility_logical_sector_cache_entry_read(media_ptr, logical_sector, &previous_cache_entry); + + /* Extended port-specific processing macro, which is by default defined to white space. */ + FX_UTILITY_LOGICAL_SECTOR_READ_EXTENSION_1 + + /* At this point, a cache entry should always be present since we invalidated + the cache over this sector range previously. In any case, check for the error + condition. */ + if (cache_entry == FX_NULL) + { + + /* This case should never happen, however, if it does simply give up on updating the + cache with the sectors from the direct read. */ + return(FX_SUCCESS); + } + + /* Determine if the cache entry is dirty and needs to be written out before it is used. */ + if ((cache_entry -> fx_cached_sector_valid) && + (cache_entry -> fx_cached_sector_buffer_dirty)) + { + + /* Yes, we need to flush this buffer out to the physical media + before we read in the new buffer. */ + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of driver write sector(s) requests. */ + media_ptr -> fx_media_driver_write_requests++; +#endif + + /* Build write request to the driver. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_WRITE; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = cache_entry -> fx_cached_sector_memory_buffer; +#ifdef FX_DRIVER_USE_64BIT_LBA + media_ptr -> fx_media_driver_logical_sector = cache_entry -> fx_cached_sector; +#else + media_ptr -> fx_media_driver_logical_sector = (ULONG)cache_entry -> fx_cached_sector; +#endif + media_ptr -> fx_media_driver_sectors = 1; + media_ptr -> fx_media_driver_sector_type = cache_entry -> fx_cached_sector_type; + + /* Only data sectors may be dirty when FX_FAULT_TOLERANT is defined */ +#ifndef FX_FAULT_TOLERANT + /* Determine if the sector is a data sector or a system sector. */ + if (cache_entry -> fx_cached_sector_type != FX_DATA_SECTOR) + { + + /* System sector is present. */ + media_ptr -> fx_media_driver_system_write = FX_TRUE; + } +#endif /* FX_FAULT_TOLERANT */ + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, cache_entry -> fx_cached_sector, 1, cache_entry -> fx_cached_sector_memory_buffer, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Invoke the driver to write the sector. */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Clear the system write flag. */ + media_ptr -> fx_media_driver_system_write = FX_FALSE; + + /* Check for successful completion. */ + if (media_ptr -> fx_media_driver_status) + { + + /* Error writing a cached sector out. Return the + error status. */ + return(media_ptr -> fx_media_driver_status); + } + + /* Clear the buffer dirty flag since it has been flushed + out. */ + cache_entry -> fx_cached_sector_buffer_dirty = FX_FALSE; + + /* Decrement the number of outstanding dirty cache entries. */ + media_ptr -> fx_media_sector_cache_dirty_count--; + } + + /* Now setup the cache entry with information from the new sector. */ + + /* Remember the sector number. */ + cache_entry -> fx_cached_sector = logical_sector; + + /* Make the cache entry valid. */ + cache_entry -> fx_cached_sector_valid = FX_TRUE; + + /* Remember the sector type. */ + cache_entry -> fx_cached_sector_type = sector_type; + + /* Place this entry that the head of the cached sector + list. */ + + /* Determine if we need to update the last used list. */ + if (previous_cache_entry) + { + + /* Yes, the current entry is not at the front of the list + so we need to change the order. */ + + /* Link the previous entry to this entry's next pointer. */ + previous_cache_entry -> fx_cached_sector_next_used = + cache_entry -> fx_cached_sector_next_used; + + /* Place this entry at the head of the list. */ + cache_entry -> fx_cached_sector_next_used = + media_ptr -> fx_media_sector_cache_list_ptr; + media_ptr -> fx_media_sector_cache_list_ptr = cache_entry; + } + + /* Copy the data from the destination buffer to the cache entry. */ + _fx_utility_memory_copy(buffer_ptr, /* Use case of memcpy is verified. */ + cache_entry -> fx_cached_sector_memory_buffer, + media_ptr -> fx_media_bytes_per_sector); + + /* Advance the destination buffer. */ + buffer_ptr = ((UCHAR *)buffer_ptr) + media_ptr -> fx_media_bytes_per_sector; + + /* Advance the source sector and decrement the sector count. */ + logical_sector++; + sectors--; + } + } +#endif + + /* Return the driver status. */ + return(media_ptr -> fx_media_driver_status); + } +} + diff --git a/common/src/fx_utility_logical_sector_write.c b/common/src/fx_utility_logical_sector_write.c new file mode 100644 index 0000000..156fbab --- /dev/null +++ b/common/src/fx_utility_logical_sector_write.c @@ -0,0 +1,444 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_utility.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_utility_logical_sector_write PORTABLE C */ +/* 6.1.6 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function handles logical sector write requests for all FileX */ +/* components. If the logical sector is currently in the media's */ +/* buffer supplied by the caller, the function simply marks the buffer */ +/* as written to. Otherwise, physical I/O is requested through the */ +/* corresponding I/O driver. */ +/* */ +/* Note: Conversion of the logical sector is done inside the driver. */ +/* This results in a performance boost for FLASH or RAM media */ +/* devices. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* logical_sector Logical sector number */ +/* buffer_ptr Pointer of sector buffer */ +/* sectors Number of sectors to write */ +/* sector_type Type of sector(s) to write */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_utility_logical_sector_flush Flush and invalidate sectors */ +/* that overlap with non-cache */ +/* sector I/O. */ +/* I/O Driver */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), and */ +/* added conditional to */ +/* disable cache, */ +/* resulting in version 6.1 */ +/* 04-02-2021 Bhupendra Naphade Modified comment(s), */ +/* updated check for logical */ +/* sector value, */ +/* resulting in version 6.1.6 */ +/* */ +/**************************************************************************/ +UINT _fx_utility_logical_sector_write(FX_MEDIA *media_ptr, ULONG64 logical_sector, + VOID *buffer_ptr, ULONG sectors, UCHAR sector_type) +{ + +#ifndef FX_DISABLE_CACHE +FX_CACHED_SECTOR *cache_entry; +UINT cache_size; +UINT index; +UINT i; +UCHAR cache_found = FX_FALSE; +#endif /* FX_DISABLE_CACHE */ + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Determine if the request is for FAT sector. */ + if (sector_type == FX_FAT_SECTOR) + { + + /* Increment the number of FAT sector writes. */ + media_ptr -> fx_media_fat_sector_writes++; + } + + /* Increment the number of logical sectors written. */ + media_ptr -> fx_media_logical_sector_writes++; +#endif + + /* Extended port-specific processing macro, which is by default defined to white space. */ + FX_UTILITY_LOGICAL_SECTOR_WRITE_EXTENSION + +#ifndef FX_DISABLE_CACHE + /* Determine if the request is from the internal media buffer area. */ + if ((((UCHAR *)buffer_ptr) >= media_ptr -> fx_media_memory_buffer) && + (((UCHAR *)buffer_ptr) <= media_ptr -> fx_media_sector_cache_end)) + { + + /* Internal cache buffer is requested. */ + + /* Determine if the logical sector cache access should use the hash function. */ + if (media_ptr -> fx_media_sector_cache_hashed) + { + + /* Calculate the area of the cache for this logical sector. */ + index = (ULONG)(logical_sector & media_ptr -> fx_media_sector_cache_hash_mask) * FX_SECTOR_CACHE_DEPTH; + + /* Build a pointer to the cache entry. */ + cache_entry = &(media_ptr -> fx_media_sector_cache[index]); + + for (i = 0; i < FX_SECTOR_CACHE_DEPTH; i++, cache_entry++) + { + + + /* Determine if the logical sector is in the cache - assuming the depth of the + sector cache is 4 entries. */ + if ((cache_entry -> fx_cached_sector_valid) && (cache_entry -> fx_cached_sector == logical_sector)) + { + cache_found = FX_TRUE; + break; + } + } + } + else + { + + /* Search for an entry in the cache that matches this request. */ + cache_size = media_ptr -> fx_media_sector_cache_size; + cache_entry = media_ptr -> fx_media_sector_cache_list_ptr; + + /* Look at the cache entries until a match is found or the end of + the cache is reached. */ + while (cache_size--) + { + + /* Determine if the requested sector has been found. */ + if ((cache_entry -> fx_cached_sector_valid) && (cache_entry -> fx_cached_sector == logical_sector)) + { + cache_found = FX_TRUE; + break; + } + + /* Otherwise, we have not found the cached entry yet. */ + + /* If there are more entries, move to the next one. */ + if (cache_entry -> fx_cached_sector_next_used) + { + + /* Move to the next cache entry. */ + cache_entry = cache_entry -> fx_cached_sector_next_used; + } + } + } + +#ifdef FX_ENABLE_FAULT_TOLERANT + if (media_ptr -> fx_media_fault_tolerant_enabled && + (media_ptr -> fx_media_fault_tolerant_state & FX_FAULT_TOLERANT_STATE_STARTED) && + (sector_type == FX_DATA_SECTOR) && + !(cache_found && (cache_entry -> fx_cached_sector_memory_buffer == buffer_ptr))) + { + + /* Special use case for file write when fault tolerant is enabled. */ + /* Data are read from one sector but write to another sector. */ + /* Need to invalidate both of original and new caches. */ + if (cache_found) + { + + /* Invalidate the new cache. */ + cache_entry -> fx_cached_sector_valid = FX_FALSE; + cache_found = FX_FALSE; + } + + /* Search for original cache. */ + cache_size = media_ptr -> fx_media_sector_cache_size; + cache_entry = media_ptr -> fx_media_sector_cache_list_ptr; + + /* Look at the cache entries until a match is found or the end of + the cache is reached. */ + while (cache_size--) + { + + /* Determine if the original sector has been found. */ + if ((cache_entry -> fx_cached_sector_valid) && + (cache_entry -> fx_cached_sector_memory_buffer == buffer_ptr)) + { + + /* Invalidate the original cache. */ + cache_entry -> fx_cached_sector_valid = FX_FALSE; + break; + } + + /* Otherwise, we have not found the cached entry yet. */ + + /* If there are more entries, move to the next one. */ + if (cache_entry -> fx_cached_sector_next_used) + { + + /* Move to the next cache entry. */ + cache_entry = cache_entry -> fx_cached_sector_next_used; + } + } + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + if (cache_found) + { + + /* Yes, we found a match. */ + +#ifdef FX_FAULT_TOLERANT + + /* Check for a system sector. Data sector fault tolerance is selected with + the FX_FAULT_TOLERANT_DATA option. */ + if (sector_type != FX_DATA_SECTOR) + { + + /* With the fault tolerant option enabled, system sectors are written immediately to + the media. */ + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of driver write sector(s) requests. */ + media_ptr -> fx_media_driver_write_requests++; +#endif + + /* Build write request to the driver. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_WRITE; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = cache_entry -> fx_cached_sector_memory_buffer; +#ifdef FX_DRIVER_USE_64BIT_LBA + media_ptr -> fx_media_driver_logical_sector = logical_sector; +#else + media_ptr -> fx_media_driver_logical_sector = (ULONG)logical_sector; +#endif + media_ptr -> fx_media_driver_sectors = 1; + media_ptr -> fx_media_driver_sector_type = sector_type; + + /* Yes, a system sector write is present so set the flag. The driver + can use this flag to make extra safeguards in writing the sector + out, yielding more fault tolerance. */ + media_ptr -> fx_media_driver_system_write = FX_TRUE; + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, logical_sector, 1, cache_entry -> fx_cached_sector_memory_buffer, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Invoke the driver to write the sector(s). */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Clear the system write flag. */ + media_ptr -> fx_media_driver_system_write = FX_FALSE; + + /* Return success. */ + return(media_ptr -> fx_media_driver_status); + } +#endif + + /* Determine if this is the first write of this logical sector. */ + if (cache_entry -> fx_cached_sector_buffer_dirty == FX_FALSE) + { + + /* Yes, increment the number of outstanding dirty sectors. */ + media_ptr -> fx_media_sector_cache_dirty_count++; + + /* Simply mark this entry as dirty. */ + cache_entry -> fx_cached_sector_buffer_dirty = FX_TRUE; + } + + /* Don't bother updating the cache linked list since writes are + preceded by reads anyway. */ + + /* Success, return to caller immediately! */ + return(FX_SUCCESS); + } + + + /* Okay, so if we are here the request must be for the additional FAT writes, since this is the + only time a write request is made without a preceding read request. */ + + /* Is the logical sector valid? */ + if ((logical_sector == 0) || (logical_sector == ((ULONG)0xFFFFFFFF))) + { + return(FX_SECTOR_INVALID); + } + + /* Compare logical sector against total sectors to make sure it is valid. */ + if ((logical_sector + sectors - 1) >= media_ptr -> fx_media_total_sectors) + { + return(FX_SECTOR_INVALID); + } + + /* Just write the buffer to the media. */ + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of driver write sector(s) requests. */ + media_ptr -> fx_media_driver_write_requests++; +#endif + + /* Build write request to the driver. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_WRITE; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = buffer_ptr; +#ifdef FX_DRIVER_USE_64BIT_LBA + media_ptr -> fx_media_driver_logical_sector = logical_sector; +#else + media_ptr -> fx_media_driver_logical_sector = (ULONG)logical_sector; +#endif + media_ptr -> fx_media_driver_sectors = sectors; + media_ptr -> fx_media_driver_sector_type = sector_type; + + /* Determine if the system write flag needs to be set. */ + if (sector_type != FX_DATA_SECTOR) + { + + /* Yes, a system sector write is present so set the flag. The driver + can use this flag to make extra safeguards in writing the sector + out, yielding more fault tolerance. */ + media_ptr -> fx_media_driver_system_write = FX_TRUE; + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, logical_sector, sectors, buffer_ptr, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Invoke the driver to write the sector(s). */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Clear the system write flag. */ + media_ptr -> fx_media_driver_system_write = FX_FALSE; + + /* Check for successful completion. */ + if (media_ptr -> fx_media_driver_status) + { + + /* Error writing a internal sector out. Return the + error status. */ + return(media_ptr -> fx_media_driver_status); + } + + /* At this point, we have a successful write. */ + return(FX_SUCCESS); + } + else +#endif /* FX_DISABLE_CACHE */ + { + + /* Otherwise, the write request is being made directly from an application + buffer. Determine if the logical sector is valid. */ + + /* Is the logical sector valid? */ + if ((logical_sector == 0) || (logical_sector == ((ULONG)0xFFFFFFFF))) + { + return(FX_SECTOR_INVALID); + } + + /* Compare logical sector against total sectors to make sure it is valid. */ + if ((logical_sector + sectors - 1) >= media_ptr -> fx_media_total_sectors) + { + return(FX_SECTOR_INVALID); + } + + /* Flush and invalidate for any entries in the cache that are in this direct I/O read request range. */ + _fx_utility_logical_sector_flush(media_ptr, logical_sector, (ULONG64) sectors, FX_TRUE); + +#ifdef FX_DISABLE_CACHE + if ((logical_sector <= media_ptr -> fx_media_memory_buffer_sector) && (logical_sector + sectors >= media_ptr -> fx_media_memory_buffer_sector)) + { + media_ptr -> fx_media_memory_buffer_sector = (ULONG64)-1; + } +#endif /* FX_DISABLE_CACHE */ + +#ifndef FX_MEDIA_STATISTICS_DISABLE + + /* Increment the number of driver write sector(s) requests. */ + media_ptr -> fx_media_driver_write_requests++; +#endif + + /* Build request to the driver. */ + media_ptr -> fx_media_driver_request = FX_DRIVER_WRITE; + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + media_ptr -> fx_media_driver_buffer = buffer_ptr; +#ifdef FX_DRIVER_USE_64BIT_LBA + media_ptr -> fx_media_driver_logical_sector = logical_sector; +#else + media_ptr -> fx_media_driver_logical_sector = (ULONG)logical_sector; +#endif + media_ptr -> fx_media_driver_sectors = sectors; + media_ptr -> fx_media_driver_sector_type = sector_type; + + /* Determine if the system write flag needs to be set. */ + if (sector_type != FX_DATA_SECTOR) + { + + /* Yes, a system sector write is present so set the flag. The driver + can use this flag to make extra safeguards in writing the sector + out, yielding more fault tolerance. */ + media_ptr -> fx_media_driver_system_write = FX_TRUE; + } + + /* If trace is enabled, insert this event into the trace buffer. */ + FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, logical_sector, sectors, buffer_ptr, FX_TRACE_INTERNAL_EVENTS, 0, 0) + + /* Invoke the driver to write the sector(s). */ + (media_ptr -> fx_media_driver_entry) (media_ptr); + + /* Clear the system write flag. */ + media_ptr -> fx_media_driver_system_write = FX_FALSE; + + /* Return driver status. */ + return(media_ptr -> fx_media_driver_status); + } +} + diff --git a/common/src/fx_utility_memory_copy.c b/common/src/fx_utility_memory_copy.c new file mode 100644 index 0000000..a5f7af0 --- /dev/null +++ b/common/src/fx_utility_memory_copy.c @@ -0,0 +1,89 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_utility.h" +#include "string.h" + + +/* Remove any previous remapping for memory copy when compiling this + module. */ + +#undef _fx_utility_memory_copy + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_utility_memory_copy PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function copies the specified number of bytes from the source */ +/* to the destination. */ +/* */ +/* INPUT */ +/* */ +/* source_ptr Source memory pointer */ +/* dest_ptr Destination memory pointer */ +/* size number of bytes to copy */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), verified */ +/* memcpy usage, */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +VOID _fx_utility_memory_copy(UCHAR *source_ptr, UCHAR *dest_ptr, ULONG size) +{ + + /* Copy the memory. */ + memcpy(dest_ptr, source_ptr, size); /* Use case of memcpy is verified. */ +} + diff --git a/common/src/fx_utility_memory_set.c b/common/src/fx_utility_memory_set.c new file mode 100644 index 0000000..a3be087 --- /dev/null +++ b/common/src/fx_utility_memory_set.c @@ -0,0 +1,93 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_utility.h" +#include "string.h" + + +/* Remove any previous remapping for memory copy when compiling this + module. */ + +#undef _fx_utility_memory_set + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_utility_memory_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function copies the specified number of bytes from the source */ +/* to the destination. */ +/* */ +/* INPUT */ +/* */ +/* dest_ptr Destination memory pointer */ +/* value Value to fill */ +/* size Number of bytes to set */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +VOID _fx_utility_memory_set(UCHAR *dest_ptr, UCHAR value, ULONG size) +{ + + /* Loop to set memory. */ + while (size--) + { + + /* Set byte. */ + *dest_ptr++ = value; + } +} + diff --git a/common/src/fx_utility_string_length_get.c b/common/src/fx_utility_string_length_get.c new file mode 100644 index 0000000..fd58edd --- /dev/null +++ b/common/src/fx_utility_string_length_get.c @@ -0,0 +1,92 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_utility.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_utility_string_length_get PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns length of string up to the maximum length. */ +/* */ +/* INPUT */ +/* */ +/* string Pointer to string */ +/* max_length Maximum length which can be */ +/* returned by function */ +/* */ +/* OUTPUT */ +/* */ +/* return length of string */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _fx_utility_absolute_path_get Get absolute path */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fx_utility_string_length_get(CHAR *string, UINT max_length) +{ + +UINT length; + + /* Initialize length to 0. */ + length = 0; + + /* Loop to calculate the length. */ + while (string[length] && (length < max_length)) + { + + /* Increment the length (index). */ + length++; + } + + /* Return length. */ + return(length); +} + diff --git a/common/src/fx_utility_token_length_get.c b/common/src/fx_utility_token_length_get.c new file mode 100644 index 0000000..b52c750 --- /dev/null +++ b/common/src/fx_utility_token_length_get.c @@ -0,0 +1,29 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" + diff --git a/common/src/fxe_directory_attributes_read.c b/common/src/fxe_directory_attributes_read.c new file mode 100644 index 0000000..95d0c33 --- /dev/null +++ b/common/src/fxe_directory_attributes_read.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" + +FX_CALLER_CHECKING_EXTERNS + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_directory_attributes_read PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the directory attributes read */ +/* call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* directory_name Directory name pointer */ +/* attributes_ptr Return attributes pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_attributes_read Actual read directory */ +/* attributes service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_directory_attributes_read(FX_MEDIA *media_ptr, CHAR *directory_name, UINT *attributes_ptr) +{ + +UINT status; + + /* Check for a null media or attributes pointer. */ + if ((media_ptr == FX_NULL) || (attributes_ptr == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual read file attributes service. */ + status = _fx_directory_attributes_read(media_ptr, directory_name, attributes_ptr); + + /* File attribute read is complete, return status. */ + return(status); +} + diff --git a/common/src/fxe_directory_attributes_set.c b/common/src/fxe_directory_attributes_set.c new file mode 100644 index 0000000..ca14631 --- /dev/null +++ b/common/src/fxe_directory_attributes_set.c @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_directory_attributes_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the directory attribute set */ +/* call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* directory_name Directory name pointer */ +/* attributes New file attributes */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_attributes_set Actual directory attributes */ +/* set service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_directory_attributes_set(FX_MEDIA *media_ptr, CHAR *directory_name, UINT attributes) +{ + +UINT status; + + + /* Check for a null media pointer. */ + if (media_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for invalid attributes. */ + if ((INT)attributes & ~(FX_DIRECTORY | FX_READ_ONLY | FX_HIDDEN | FX_SYSTEM | FX_ARCHIVE)) + { + return(FX_INVALID_ATTR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual set attributes service. */ + status = _fx_directory_attributes_set(media_ptr, directory_name, attributes); + + /* File attribute set is complete, return status. */ + return(status); +} + diff --git a/common/src/fxe_directory_create.c b/common/src/fxe_directory_create.c new file mode 100644 index 0000000..1217b3f --- /dev/null +++ b/common/src/fxe_directory_create.c @@ -0,0 +1,95 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_directory_create PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the directory create call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* directory_name Directory name */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_create Actual directory create call */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_directory_create(FX_MEDIA *media_ptr, CHAR *directory_name) +{ + +UINT status; + + + /* Check for a null media pointer. */ + if (media_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual directory create service. */ + status = _fx_directory_create(media_ptr, directory_name); + + /* Directory create is complete, return status. */ + return(status); +} + diff --git a/common/src/fxe_directory_default_get.c b/common/src/fxe_directory_default_get.c new file mode 100644 index 0000000..d92ba79 --- /dev/null +++ b/common/src/fxe_directory_default_get.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_directory_default_get PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the directory default get call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* return_path_name Destination string pointer */ +/* address */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_default_get Actual default directory get */ +/* service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_directory_default_get(FX_MEDIA *media_ptr, CHAR **return_path_name) +{ + +UINT status; + + + /* Check for a null media pointer or null destination for the default path. */ + if ((media_ptr == FX_NULL) || (return_path_name == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual default directory get service. */ + status = _fx_directory_default_get(media_ptr, return_path_name); + + /* Return default directory get status. */ + return(status); +} + diff --git a/common/src/fxe_directory_default_get_copy.c b/common/src/fxe_directory_default_get_copy.c new file mode 100644 index 0000000..2af8c45 --- /dev/null +++ b/common/src/fxe_directory_default_get_copy.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_directory_default_get_copy PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the directory default get copy */ +/* call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* return_path_name_buffer Destination buffer for name */ +/* return_path_name_buffer_size Size of buffer pointed to by */ +/* return_path_name_buffer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_directory_default_get_copy(FX_MEDIA *media_ptr, CHAR *return_path_name_buffer, UINT return_path_name_buffer_size) +{ + +UINT status; + + + /* Check for a null media pointer or name buffer pointer. */ + if ((media_ptr == FX_NULL) || (return_path_name_buffer == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual default directory get copy service. */ + status = _fx_directory_default_get_copy(media_ptr, return_path_name_buffer, return_path_name_buffer_size); + + /* Return successful status. */ + return(status); +} diff --git a/common/src/fxe_directory_default_set.c b/common/src/fxe_directory_default_set.c new file mode 100644 index 0000000..19b6d9a --- /dev/null +++ b/common/src/fxe_directory_default_set.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_directory_default_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the default directory set call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* new_path_name New path to set current */ +/* working directory to */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_default_set Actual default directory set */ +/* service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_directory_default_set(FX_MEDIA *media_ptr, CHAR *new_path_name) +{ + +UINT status; + + + /* Check for a null media pointer. */ + if (media_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual default directory set service. */ + status = _fx_directory_default_set(media_ptr, new_path_name); + + /* Default directory set is complete, return status. */ + return(status); +} + diff --git a/common/src/fxe_directory_delete.c b/common/src/fxe_directory_delete.c new file mode 100644 index 0000000..6909e7b --- /dev/null +++ b/common/src/fxe_directory_delete.c @@ -0,0 +1,94 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" + +FX_CALLER_CHECKING_EXTERNS + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_directory_delete PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the directory delete call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* directory_name Directory name to delete */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_delete Actual directory delete call */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_directory_delete(FX_MEDIA *media_ptr, CHAR *directory_name) +{ + +UINT status; + + + /* Check for a null media pointer. */ + if (media_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual directory delete service. */ + status = _fx_directory_delete(media_ptr, directory_name); + + /* Directory delete is complete, return status. */ + return(status); +} + diff --git a/common/src/fxe_directory_first_entry_find.c b/common/src/fxe_directory_first_entry_find.c new file mode 100644 index 0000000..488617d --- /dev/null +++ b/common/src/fxe_directory_first_entry_find.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_directory_first_entry_find PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the directory find first call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* directory_name Destination for directory */ +/* name */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_first_entry_find Actual first find service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_directory_first_entry_find(FX_MEDIA *media_ptr, CHAR *directory_name) +{ + +UINT status; + + + /* Check for a null media pointer. */ + if (media_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual directory find first service. */ + status = _fx_directory_first_entry_find(media_ptr, directory_name); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_directory_first_full_entry_find.c b/common/src/fxe_directory_first_full_entry_find.c new file mode 100644 index 0000000..70b756e --- /dev/null +++ b/common/src/fxe_directory_first_full_entry_find.c @@ -0,0 +1,108 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_directory_first_full_entry_find PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the directory find first full */ +/* entry call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* directory_name Destination for directory */ +/* name */ +/* attributes Destination for attributes */ +/* size Destination for size */ +/* year Destination for year */ +/* month Destination for month */ +/* day Destination for day */ +/* hour Destination for hour */ +/* minute Destination for minute */ +/* second Destination for second */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_first_full_entry_find Actual first find service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_directory_first_full_entry_find(FX_MEDIA *media_ptr, + CHAR *directory_name, UINT *attributes, ULONG *size, + UINT *year, UINT *month, UINT *day, UINT *hour, UINT *minute, UINT *second) +{ + +UINT status; + + + /* Check for a null media pointer. */ + if (media_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual directory find first full entry service. */ + status = _fx_directory_first_full_entry_find(media_ptr, directory_name, + attributes, size, year, month, day, hour, minute, second); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_directory_information_get.c b/common/src/fxe_directory_information_get.c new file mode 100644 index 0000000..378bc28 --- /dev/null +++ b/common/src/fxe_directory_information_get.c @@ -0,0 +1,111 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_directory_information_get PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the directory information */ +/* get call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* directory_name Directory name pointer */ +/* attributes Pointer to attributes */ +/* size Pointer to size */ +/* year Pointer to year */ +/* month Pointer to month */ +/* day Pointer to day */ +/* hour Pointer to hour */ +/* minute Pointer to minute */ +/* second Pointer to second */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_information_get Actual directory information */ +/* get service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_directory_information_get(FX_MEDIA *media_ptr, CHAR *directory_name, + UINT *attributes, ULONG *size, + UINT *year, UINT *month, UINT *day, + UINT *hour, UINT *minute, UINT *second) +{ + +UINT status; + + + /* Check for a null media pointer or all null return parameter pointers. */ + if ((media_ptr == FX_NULL) || + ((attributes == FX_NULL) && (size == FX_NULL) && (year == FX_NULL) && (month == FX_NULL) && + (day == FX_NULL) && (hour == FX_NULL) && (minute == FX_NULL) && (second == FX_NULL))) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual directory information get service. */ + status = _fx_directory_information_get(media_ptr, directory_name, attributes, size, + year, month, day, hour, minute, second); + + /* Directory information get is complete, return status. */ + return(status); +} + diff --git a/common/src/fxe_directory_local_path_clear.c b/common/src/fxe_directory_local_path_clear.c new file mode 100644 index 0000000..3bd7985 --- /dev/null +++ b/common/src/fxe_directory_local_path_clear.c @@ -0,0 +1,92 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_file.h" +#include "fx_utility.h" +#include "fx_directory.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_directory_local_path_clear PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the local path clear call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_local_path_set Actual core service call */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_directory_local_path_clear(FX_MEDIA *media_ptr) +{ + +UINT status; + + + /* Check for a null media pointer. */ + if (media_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Call actual local path clear service. */ + status = _fx_directory_local_path_clear(media_ptr); + + /* Return status. */ + return(status); +} + diff --git a/common/src/fxe_directory_local_path_get.c b/common/src/fxe_directory_local_path_get.c new file mode 100644 index 0000000..eb319fb --- /dev/null +++ b/common/src/fxe_directory_local_path_get.c @@ -0,0 +1,93 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_file.h" +#include "fx_utility.h" +#include "fx_directory.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_directory_local_path_get PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the local path get call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* return_path_name Return local path name */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_local_path_get Actual core service call */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_directory_local_path_get(FX_MEDIA *media_ptr, CHAR **return_path_name) +{ + +UINT status; + + + /* Check for a null media pointer or null destination for the default path. */ + if ((media_ptr == FX_NULL) || (return_path_name == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Call actual local path get service. */ + status = _fx_directory_local_path_get(media_ptr, return_path_name); + + /* Return status. */ + return(status); +} + diff --git a/common/src/fxe_directory_local_path_get_copy.c b/common/src/fxe_directory_local_path_get_copy.c new file mode 100644 index 0000000..f558336 --- /dev/null +++ b/common/src/fxe_directory_local_path_get_copy.c @@ -0,0 +1,95 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_directory_local_path_get_copy PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the local path get copy call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* return_path_name_buffer Destination buffer for name */ +/* return_path_name_buffer_size Size of buffer pointed to by */ +/* return_path_name_buffer */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_directory_local_path_get_copy(FX_MEDIA *media_ptr, CHAR *return_path_name_buffer, UINT return_path_name_buffer_size) +{ + +UINT status; + + + /* Check for a null media pointer or name buffer pointer. */ + if ((media_ptr == FX_NULL) || (return_path_name_buffer == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual default directory get copy service. */ + status = _fx_directory_local_path_get_copy(media_ptr, return_path_name_buffer, return_path_name_buffer_size); + + /* Return successful status. */ + return(status); +} diff --git a/common/src/fxe_directory_local_path_restore.c b/common/src/fxe_directory_local_path_restore.c new file mode 100644 index 0000000..223df30 --- /dev/null +++ b/common/src/fxe_directory_local_path_restore.c @@ -0,0 +1,93 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_file.h" +#include "fx_utility.h" +#include "fx_directory.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_directory_local_path_restore PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the local path restore call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* local_path_ptr Local path control block ptr */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fxe_directory_local_path_restore Actual core service call */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_directory_local_path_restore(FX_MEDIA *media_ptr, FX_LOCAL_PATH *local_path_ptr) +{ + +UINT status; + + + /* Check for a null media pointer or a null local path. */ + if ((media_ptr == FX_NULL) || (local_path_ptr == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Call actual local path restore service. */ + status = _fx_directory_local_path_restore(media_ptr, local_path_ptr); + + /* Return status. */ + return(status); +} + diff --git a/common/src/fxe_directory_local_path_set.c b/common/src/fxe_directory_local_path_set.c new file mode 100644 index 0000000..d7d293e --- /dev/null +++ b/common/src/fxe_directory_local_path_set.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" +#include "fx_file.h" +#include "fx_utility.h" +#include "fx_directory.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_directory_local_path_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the local path set call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* local_path_ptr Local path control block ptr */ +/* new_path_name New path to set current local */ +/* working directory to */ +/* local_path_control_block_size Size of the FX_LOCAL_PATH */ +/* structure */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_local_path_set Actual core service call */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_directory_local_path_set(FX_MEDIA *media_ptr, FX_LOCAL_PATH *local_path_ptr, CHAR *new_path_name, UINT local_path_control_block_size) +{ + +UINT status; + + + /* Check for a null media pointer or null local path. */ + if ((media_ptr == FX_NULL) || (local_path_ptr == FX_NULL) || (local_path_control_block_size != sizeof(FX_LOCAL_PATH))) + { + return(FX_PTR_ERROR); + } + + /* Call actual local path set service. */ + status = _fx_directory_local_path_set(media_ptr, local_path_ptr, new_path_name); + + /* Return status. */ + return(status); +} + diff --git a/common/src/fxe_directory_long_name_get.c b/common/src/fxe_directory_long_name_get.c new file mode 100644 index 0000000..8041d71 --- /dev/null +++ b/common/src/fxe_directory_long_name_get.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" + + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_directory_long_name_get PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the long name get service. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* short_file_name Pointer to short (8.3) name */ +/* long_file_name Pointer to long (max 255) name*/ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_long_name_get Actual long name get service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_directory_long_name_get(FX_MEDIA *media_ptr, CHAR *short_file_name, CHAR *long_file_name) +{ + +UINT status; + + + /* Check for a NULL media or file name pointer. */ + if ((media_ptr == FX_NULL) || (short_file_name == FX_NULL) || (long_file_name == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual long name get service. */ + status = _fx_directory_long_name_get(media_ptr, short_file_name, long_file_name); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_directory_long_name_get_extended.c b/common/src/fxe_directory_long_name_get_extended.c new file mode 100644 index 0000000..126db44 --- /dev/null +++ b/common/src/fxe_directory_long_name_get_extended.c @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" + + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_directory_long_name_get_extended PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the long name get service. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* short_file_name Pointer to short (8.3) name */ +/* long_file_name Pointer to long (max 255) name*/ +/* long_file_name_buffer_length Buffer length for long name */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_long_name_get_extended Actual long name get service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_directory_long_name_get_extended(FX_MEDIA *media_ptr, CHAR *short_file_name, CHAR *long_file_name, UINT long_file_name_buffer_length) +{ + +UINT status; + + + /* Check for a NULL media or file name pointer. */ + if ((media_ptr == FX_NULL) || (short_file_name == FX_NULL) || (long_file_name == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for zero length buffer. */ + if (long_file_name_buffer_length == 0) + { + return(FX_BUFFER_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual long name get service. */ + status = _fx_directory_long_name_get_extended(media_ptr, short_file_name, long_file_name, long_file_name_buffer_length); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_directory_name_test.c b/common/src/fxe_directory_name_test.c new file mode 100644 index 0000000..294f062 --- /dev/null +++ b/common/src/fxe_directory_name_test.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_directory_name_test PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the directory name test call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* directory_name Directory name pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_name_test Actual directory name test */ +/* service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_directory_name_test(FX_MEDIA *media_ptr, CHAR *directory_name) +{ + +UINT status; + + + /* Check for a null media pointer. */ + if (media_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual directory name test service. */ + status = _fx_directory_name_test(media_ptr, directory_name); + + /* Directory name test is complete, return status. */ + return(status); +} + diff --git a/common/src/fxe_directory_next_entry_find.c b/common/src/fxe_directory_next_entry_find.c new file mode 100644 index 0000000..8eb66fb --- /dev/null +++ b/common/src/fxe_directory_next_entry_find.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_directory_next_entry_find PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks the next directory entry find call for errors. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* directory_name Destination for directory */ +/* name */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_next_entry_find Actual entry find service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_directory_next_entry_find(FX_MEDIA *media_ptr, CHAR *directory_name) +{ + +UINT status; + + + /* Check for a null media pointer. */ + if (media_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual next directory entry find service. */ + status = _fx_directory_next_entry_find(media_ptr, directory_name); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_directory_next_full_entry_find.c b/common/src/fxe_directory_next_full_entry_find.c new file mode 100644 index 0000000..ff44d44 --- /dev/null +++ b/common/src/fxe_directory_next_full_entry_find.c @@ -0,0 +1,108 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_directory_next_full_entry_find PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks the next directory full entry find call for */ +/* errors. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* directory_name Destination for directory */ +/* name */ +/* attributes Destination for attributes */ +/* size Destination for size */ +/* year Destination for year */ +/* month Destination for month */ +/* day Destination for day */ +/* hour Destination for hour */ +/* minute Destination for minute */ +/* second Destination for second */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_next_full_entry_find Actual full entry find service*/ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_directory_next_full_entry_find(FX_MEDIA *media_ptr, + CHAR *directory_name, UINT *attributes, ULONG *size, + UINT *year, UINT *month, UINT *day, UINT *hour, UINT *minute, UINT *second) +{ + +UINT status; + + + /* Check for a null media pointer. */ + if (media_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual next directory full entry find service. */ + status = _fx_directory_next_full_entry_find(media_ptr, directory_name, + attributes, size, year, month, day, hour, minute, second); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_directory_rename.c b/common/src/fxe_directory_rename.c new file mode 100644 index 0000000..ae95697 --- /dev/null +++ b/common/src/fxe_directory_rename.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_directory_rename PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the directory rename call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* old_directory_name Old file directory pointer */ +/* new_directory_name New file directory pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_rename Actual directory rename call */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_directory_rename(FX_MEDIA *media_ptr, CHAR *old_directory_name, CHAR *new_directory_name) +{ + +UINT status; + + + /* Check for a null media pointer. */ + if (media_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual directory rename service. */ + status = _fx_directory_rename(media_ptr, old_directory_name, new_directory_name); + + /* Directory rename is complete, return status. */ + return(status); +} + diff --git a/common/src/fxe_directory_short_name_get.c b/common/src/fxe_directory_short_name_get.c new file mode 100644 index 0000000..11d647f --- /dev/null +++ b/common/src/fxe_directory_short_name_get.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" + + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_directory_short_name_get PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the short name get service. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* long_file_name Pointer to long (max 255) name*/ +/* short_file_name Pointer to short (8.3) name */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_short_name_get Actual short name get service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_directory_short_name_get(FX_MEDIA *media_ptr, CHAR *long_file_name, CHAR *short_file_name) +{ + +UINT status; + + + /* Check for a NULL media or file name pointer. */ + if ((media_ptr == FX_NULL) || (short_file_name == FX_NULL) || (long_file_name == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual short name get service. */ + status = _fx_directory_short_name_get(media_ptr, long_file_name, short_file_name); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_directory_short_name_get_extended.c b/common/src/fxe_directory_short_name_get_extended.c new file mode 100644 index 0000000..ed90e5e --- /dev/null +++ b/common/src/fxe_directory_short_name_get_extended.c @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Directory */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_directory.h" + + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_directory_short_name_get_extended PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the short name get service. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* long_file_name Pointer to long (max 255) name*/ +/* short_file_name Pointer to short (8.3) name */ +/* short_file_name_length Buffer length for short name */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_short_name_get_extended Actual short name get service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_directory_short_name_get_extended(FX_MEDIA *media_ptr, CHAR *long_file_name, CHAR *short_file_name, UINT short_file_name_length) +{ + +UINT status; + + + /* Check for a NULL media or file name pointer. */ + if ((media_ptr == FX_NULL) || (short_file_name == FX_NULL) || (long_file_name == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for zero length buffer. */ + if (short_file_name_length == 0) + { + return(FX_BUFFER_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual short name get service. */ + status = _fx_directory_short_name_get_extended(media_ptr, long_file_name, short_file_name, short_file_name_length); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_fault_tolerant_enable.c b/common/src/fxe_fault_tolerant_enable.c new file mode 100644 index 0000000..040071c --- /dev/null +++ b/common/src/fxe_fault_tolerant_enable.c @@ -0,0 +1,99 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Fault Tolerant */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_fault_tolerant_enable PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the fault tolerant enable call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* memory_buffer Pointer to memory buffer. */ +/* memory_size Size of memory buffer. It must*/ +/* be one sector. */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_fault_tolerant_enable Actual enable service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_fault_tolerant_enable(FX_MEDIA *media_ptr, VOID *memory_buffer, UINT memory_size) +{ + +UINT status; + + + /* Check for a null media pointer or buffer pointer. */ + if ((media_ptr == FX_NULL) || (memory_buffer == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual fault tolerant enable service. */ + status = _fx_fault_tolerant_enable(media_ptr, memory_buffer, memory_size); + + /* Return status to the caller. */ + return(status); +} +#endif /* FX_ENABLE_FAULT_TOLERANT */ + diff --git a/common/src/fxe_file_allocate.c b/common/src/fxe_file_allocate.c new file mode 100644 index 0000000..c104487 --- /dev/null +++ b/common/src/fxe_file_allocate.c @@ -0,0 +1,95 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_file_allocate PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the file allocate service. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* size Number of bytes to allocate */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_allocate Actual file allocate service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_file_allocate(FX_FILE *file_ptr, ULONG size) +{ + +UINT status; + + + /* Check for a null file pointer. */ + if (file_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual file allocate service. */ + status = _fx_file_allocate(file_ptr, size); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_file_attributes_read.c b/common/src/fxe_file_attributes_read.c new file mode 100644 index 0000000..51df19c --- /dev/null +++ b/common/src/fxe_file_attributes_read.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_file_attributes_read PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the file attributes read call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* file_name File name pointer */ +/* attributes_ptr Return attributes pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_attributes_read Actual read file attributes */ +/* service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_file_attributes_read(FX_MEDIA *media_ptr, CHAR *file_name, UINT *attributes_ptr) +{ + +UINT status; + + /* Check for a null media or attributes pointer. */ + if ((media_ptr == FX_NULL) || (attributes_ptr == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual read file attributes service. */ + status = _fx_file_attributes_read(media_ptr, file_name, attributes_ptr); + + /* File attribute read is complete, return status. */ + return(status); +} + diff --git a/common/src/fxe_file_attributes_set.c b/common/src/fxe_file_attributes_set.c new file mode 100644 index 0000000..f11b8da --- /dev/null +++ b/common/src/fxe_file_attributes_set.c @@ -0,0 +1,103 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_file_attributes_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the file attribute set call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* file_name File name pointer */ +/* attributes New file attributes */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_attributes_set Actual file attributes set */ +/* service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_file_attributes_set(FX_MEDIA *media_ptr, CHAR *file_name, UINT attributes) +{ + +UINT status; + + + /* Check for a null media pointer. */ + if (media_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for invalid attributes. */ + if ((INT)attributes & ~(FX_READ_ONLY | FX_HIDDEN | FX_SYSTEM | FX_ARCHIVE)) + { + return(FX_INVALID_ATTR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual set attributes service. */ + status = _fx_file_attributes_set(media_ptr, file_name, attributes); + + /* File attribute set is complete, return status. */ + return(status); +} + diff --git a/common/src/fxe_file_best_effort_allocate.c b/common/src/fxe_file_best_effort_allocate.c new file mode 100644 index 0000000..1df5d3f --- /dev/null +++ b/common/src/fxe_file_best_effort_allocate.c @@ -0,0 +1,98 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_file_best_effort_allocate PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the file best effort allocate */ +/* service. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* size Number of bytes to allocate */ +/* actual_size_allocated Number of bytes allocated */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_best_effort_allocate Actual best effort file */ +/* allocate service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_file_best_effort_allocate(FX_FILE *file_ptr, ULONG size, ULONG *actual_size_allocated) +{ + +UINT status; + + + /* Check for a null file pointer. */ + if ((file_ptr == FX_NULL) || (actual_size_allocated == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual best effort file allocate service. */ + status = _fx_file_best_effort_allocate(file_ptr, size, actual_size_allocated); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_file_close.c b/common/src/fxe_file_close.c new file mode 100644 index 0000000..87e3586 --- /dev/null +++ b/common/src/fxe_file_close.c @@ -0,0 +1,94 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_file_close PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the close file call. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_close Actual file close service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_file_close(FX_FILE *file_ptr) +{ + +UINT status; + + + /* Check for a null file pointer. */ + if (file_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual file close service. */ + status = _fx_file_close(file_ptr); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_file_create.c b/common/src/fxe_file_create.c new file mode 100644 index 0000000..e1a5170 --- /dev/null +++ b/common/src/fxe_file_create.c @@ -0,0 +1,95 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_file_create PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the file create call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* file_name File name */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_create Actual file create call */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_file_create(FX_MEDIA *media_ptr, CHAR *file_name) +{ + +UINT status; + + + /* Check for a null media pointer. */ + if (media_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual file create service. */ + status = _fx_file_create(media_ptr, file_name); + + /* File create is complete, return status. */ + return(status); +} + diff --git a/common/src/fxe_file_date_time_set.c b/common/src/fxe_file_date_time_set.c new file mode 100644 index 0000000..eaabe01 --- /dev/null +++ b/common/src/fxe_file_date_time_set.c @@ -0,0 +1,293 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_file_date_time_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the file date/time set service. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* file_name File name pointer */ +/* year Year */ +/* month Month */ +/* day Day */ +/* hour Hour */ +/* minute Minute */ +/* second Second */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_date_time_set Actual file date/time set */ +/* service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_file_date_time_set(FX_MEDIA *media_ptr, CHAR *file_name, + UINT year, UINT month, UINT day, UINT hour, UINT minute, UINT second) +{ + +UINT status; + + + /* Check for a NULL media or file name pointer. */ + if ((media_ptr == FX_NULL) || (file_name == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for invalid year. */ + if ((year < FX_BASE_YEAR) || (year > FX_MAXIMUM_YEAR)) + { + return(FX_INVALID_YEAR); + } + + /* Check for invalid day. */ + if (day < 1) + { + return(FX_INVALID_DAY); + } + + /* Check for invalid day. */ + switch (month) + { + + case 1: + { + + /* Check for 31 days. */ + if (day > 31) + { + return(FX_INVALID_DAY); + } + break; + } + + case 2: + { + + /* Check for leap year. */ + if ((year % 4) == 0) + { + + /* Leap year, February has 29 days. */ + if (day > 29) + { + return(FX_INVALID_DAY); + } + } + else + { + + /* Otherwise, non-leap year. February has + 28 days. */ + if (day > 28) + { + return(FX_INVALID_DAY); + } + } + break; + } + + case 3: + { + + /* Check for 31 days. */ + if (day > 31) + { + return(FX_INVALID_DAY); + } + break; + } + + case 4: + { + + /* Check for 30 days. */ + if (day > 30) + { + return(FX_INVALID_DAY); + } + break; + } + + case 5: + { + + /* Check for 31 days. */ + if (day > 31) + { + return(FX_INVALID_DAY); + } + break; + } + + case 6: + { + + /* Check for 30 days. */ + if (day > 30) + { + return(FX_INVALID_DAY); + } + break; + } + + case 7: + { + + /* Check for 31 days. */ + if (day > 31) + { + return(FX_INVALID_DAY); + } + break; + } + + case 8: + { + + /* Check for 31 days. */ + if (day > 31) + { + return(FX_INVALID_DAY); + } + break; + } + + case 9: + { + + /* Check for 30 days. */ + if (day > 30) + { + return(FX_INVALID_DAY); + } + break; + } + + case 10: + { + + /* Check for 31 days. */ + if (day > 31) + { + return(FX_INVALID_DAY); + } + break; + } + + case 11: + { + + /* Check for 30 days. */ + if (day > 30) + { + return(FX_INVALID_DAY); + } + break; + } + + case 12: + { + + /* Check for 31 days. */ + if (day > 31) + { + return(FX_INVALID_DAY); + } + break; + } + + default: + + /* Invalid month. */ + return(FX_INVALID_MONTH); + } + + /* Check for invalid hour. */ + if (hour > FX_MAXIMUM_HOUR) + { + return(FX_INVALID_HOUR); + } + + /* Check for invalid minute. */ + if (minute > FX_MAXIMUM_MINUTE) + { + return(FX_INVALID_MINUTE); + } + + /* Check for invalid second. */ + if (second > FX_MAXIMUM_SECOND) + { + return(FX_INVALID_SECOND); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual file date/time set service. */ + status = _fx_file_date_time_set(media_ptr, file_name, + year, month, day, + hour, minute, second); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_file_delete.c b/common/src/fxe_file_delete.c new file mode 100644 index 0000000..a515b17 --- /dev/null +++ b/common/src/fxe_file_delete.c @@ -0,0 +1,94 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + +FX_CALLER_CHECKING_EXTERNS + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_file_delete PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the file delete call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* file_name File name pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_delete Actual file delete service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_file_delete(FX_MEDIA *media_ptr, CHAR *file_name) +{ + +UINT status; + + + /* Check for a null media pointer. */ + if (media_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual file delete service. */ + status = _fx_file_delete(media_ptr, file_name); + + /* File delete is complete, return status. */ + return(status); +} + diff --git a/common/src/fxe_file_extended_allocate.c b/common/src/fxe_file_extended_allocate.c new file mode 100644 index 0000000..a82fae5 --- /dev/null +++ b/common/src/fxe_file_extended_allocate.c @@ -0,0 +1,95 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_file_extended_allocate PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the file allocate service. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* size Number of bytes to allocate */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_extended_allocate Actual file allocate service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_file_extended_allocate(FX_FILE *file_ptr, ULONG64 size) +{ + +UINT status; + + + /* Check for a null file pointer. */ + if (file_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual file allocate service. */ + status = _fx_file_extended_allocate(file_ptr, size); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_file_extended_best_effort_allocate.c b/common/src/fxe_file_extended_best_effort_allocate.c new file mode 100644 index 0000000..f3fb7ea --- /dev/null +++ b/common/src/fxe_file_extended_best_effort_allocate.c @@ -0,0 +1,98 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_file_extended_best_effort_allocate PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the file best effort allocate */ +/* service. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* size Number of bytes to allocate */ +/* actual_size_allocated Number of bytes allocated */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_extended_best_effort_allocate Actual best effort file */ +/* allocate service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_file_extended_best_effort_allocate(FX_FILE *file_ptr, ULONG64 size, ULONG64 *actual_size_allocated) +{ + +UINT status; + + + /* Check for a null file pointer. */ + if ((file_ptr == FX_NULL) || (actual_size_allocated == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual best effort file allocate service. */ + status = _fx_file_extended_best_effort_allocate(file_ptr, size, actual_size_allocated); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_file_extended_relative_seek.c b/common/src/fxe_file_extended_relative_seek.c new file mode 100644 index 0000000..1e9e2b8 --- /dev/null +++ b/common/src/fxe_file_extended_relative_seek.c @@ -0,0 +1,110 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_file_extended_relative_seek PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the file relative seek call. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* byte_offset Byte offset of the seek */ +/* seek_from Direction for relative seek, */ +/* legal values are: */ +/* */ +/* FX_SEEK_BEGIN */ +/* FX_SEEK_END */ +/* FX_SEEK_FORWARD */ +/* FX_SEEK_BACK */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_extended_relative_seek Actual relative file seek */ +/* service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_file_extended_relative_seek(FX_FILE *file_ptr, ULONG64 byte_offset, UINT seek_from) +{ + +UINT status; + + + /* Check for a null file pointer. */ + if (file_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for valid seek from option. */ + if ((seek_from != FX_SEEK_BEGIN) && (seek_from != FX_SEEK_END) && + (seek_from != FX_SEEK_FORWARD) && (seek_from != FX_SEEK_BACK)) + { + return(FX_INVALID_OPTION); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual file relative seek service. */ + status = _fx_file_extended_relative_seek(file_ptr, byte_offset, seek_from); + + /* Seek is complete, return status. */ + return(status); +} + diff --git a/common/src/fxe_file_extended_seek.c b/common/src/fxe_file_extended_seek.c new file mode 100644 index 0000000..2750ff0 --- /dev/null +++ b/common/src/fxe_file_extended_seek.c @@ -0,0 +1,95 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_file_extended_seek PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the file seek call. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* byte_offset Byte offset into the file */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_extended_seek Actual file seek service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_file_extended_seek(FX_FILE *file_ptr, ULONG64 byte_offset) +{ + +UINT status; + + + /* Check for a null file pointer. */ + if (file_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual file seek service. */ + status = _fx_file_extended_seek(file_ptr, byte_offset); + + /* Seek is complete, return status. */ + return(status); +} + diff --git a/common/src/fxe_file_extended_truncate.c b/common/src/fxe_file_extended_truncate.c new file mode 100644 index 0000000..0e90056 --- /dev/null +++ b/common/src/fxe_file_extended_truncate.c @@ -0,0 +1,95 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_file_extended_truncate PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the file truncate call. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* size New size of the file in bytes */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_extended_truncate Actual file truncate service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_file_extended_truncate(FX_FILE *file_ptr, ULONG64 size) +{ + +UINT status; + + + /* Check for a null file pointer. */ + if (file_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual file truncate service. */ + status = _fx_file_extended_truncate(file_ptr, size); + + /* Truncate is complete, return status. */ + return(status); +} + diff --git a/common/src/fxe_file_extended_truncate_release.c b/common/src/fxe_file_extended_truncate_release.c new file mode 100644 index 0000000..3bd56f8 --- /dev/null +++ b/common/src/fxe_file_extended_truncate_release.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_file_extended_truncate_release PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the file truncate release call. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* size New size of the file in bytes */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_extended_truncate_release Actual file truncate release */ +/* service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_file_extended_truncate_release(FX_FILE *file_ptr, ULONG64 size) +{ + +UINT status; + + + /* Check for a null file pointer. */ + if (file_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual file truncate release service. */ + status = _fx_file_extended_truncate_release(file_ptr, size); + + /* Truncate is complete, return status. */ + return(status); +} + diff --git a/common/src/fxe_file_open.c b/common/src/fxe_file_open.c new file mode 100644 index 0000000..bad1d84 --- /dev/null +++ b/common/src/fxe_file_open.c @@ -0,0 +1,137 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_file_open PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the file open call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* file_ptr File control block pointer */ +/* file_name Name pointer */ +/* open_type Type of open requested */ +/* file_control_block_size Size of FX_FILE structure */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_open Actual open file service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_file_open(FX_MEDIA *media_ptr, FX_FILE *file_ptr, CHAR *file_name, UINT open_type, UINT file_control_block_size) +{ + +UINT status; +FX_FILE *current_file; +ULONG open_count; + + + /* Check for a null media or file pointer. */ + if ((media_ptr == FX_NULL) || (media_ptr -> fx_media_id != FX_MEDIA_ID) || (file_ptr == FX_NULL) || (file_control_block_size != sizeof(FX_FILE))) + { + return(FX_PTR_ERROR); + } + + /* Check for an invalid open type. */ + if ((open_type != FX_OPEN_FOR_READ) && (open_type != FX_OPEN_FOR_READ_FAST) && (open_type != FX_OPEN_FOR_WRITE)) + { + return(FX_ACCESS_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Get protection. */ + FX_PROTECT + + /* Check for a duplicate file open. */ + + /* Loop to search the list for the same file handle. */ + current_file = media_ptr -> fx_media_opened_file_list; + open_count = media_ptr -> fx_media_opened_file_count; + + while (open_count--) + { + + /* See if a match exists. */ + if (file_ptr == current_file) + { + + /* Release protection. */ + FX_UNPROTECT + + /* Return error. */ + return(FX_PTR_ERROR); + } + + /* Move to the next opened file. */ + current_file = current_file -> fx_file_opened_next; + } + + /* Release protection. */ + FX_UNPROTECT + + /* Call actual file open service. */ + status = _fx_file_open(media_ptr, file_ptr, file_name, open_type); + + /* Open is complete, return status. */ + return(status); +} + diff --git a/common/src/fxe_file_read.c b/common/src/fxe_file_read.c new file mode 100644 index 0000000..5a19afc --- /dev/null +++ b/common/src/fxe_file_read.c @@ -0,0 +1,98 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_file_read PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the read file call. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* buffer_ptr Buffer pointer */ +/* request_size Number of bytes requested */ +/* actual_size Pointer to variable for the */ +/* number of bytes read */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_read Actual file read service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_file_read(FX_FILE *file_ptr, VOID *buffer_ptr, ULONG request_size, ULONG *actual_size) +{ + +UINT status; + + + /* Check for a null file or buffer pointer. */ + if ((file_ptr == FX_NULL) || (buffer_ptr == FX_NULL) || (actual_size == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual file read service. */ + status = _fx_file_read(file_ptr, buffer_ptr, request_size, actual_size); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_file_relative_seek.c b/common/src/fxe_file_relative_seek.c new file mode 100644 index 0000000..aaf7b6d --- /dev/null +++ b/common/src/fxe_file_relative_seek.c @@ -0,0 +1,110 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_file_relative_seek PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the file relative seek call. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* byte_offset Byte offset of the seek */ +/* seek_from Direction for relative seek, */ +/* legal values are: */ +/* */ +/* FX_SEEK_BEGIN */ +/* FX_SEEK_END */ +/* FX_SEEK_FORWARD */ +/* FX_SEEK_BACK */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_relative_seek Actual relative file seek */ +/* service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_file_relative_seek(FX_FILE *file_ptr, ULONG byte_offset, UINT seek_from) +{ + +UINT status; + + + /* Check for a null file pointer. */ + if (file_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for valid seek from option. */ + if ((seek_from != FX_SEEK_BEGIN) && (seek_from != FX_SEEK_END) && + (seek_from != FX_SEEK_FORWARD) && (seek_from != FX_SEEK_BACK)) + { + return(FX_INVALID_OPTION); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual file relative seek service. */ + status = _fx_file_relative_seek(file_ptr, byte_offset, seek_from); + + /* Seek is complete, return status. */ + return(status); +} + diff --git a/common/src/fxe_file_rename.c b/common/src/fxe_file_rename.c new file mode 100644 index 0000000..7ecc297 --- /dev/null +++ b/common/src/fxe_file_rename.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_file_rename PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the file rename call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* old_file_name Old file name pointer */ +/* new_file_name New file name pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_rename Actual file rename service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_file_rename(FX_MEDIA *media_ptr, CHAR *old_file_name, CHAR *new_file_name) +{ + +UINT status; + + + /* Check for a null media pointer. */ + if (media_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual file rename service. */ + status = _fx_file_rename(media_ptr, old_file_name, new_file_name); + + /* File rename is complete, return status. */ + return(status); +} + diff --git a/common/src/fxe_file_seek.c b/common/src/fxe_file_seek.c new file mode 100644 index 0000000..d8402e2 --- /dev/null +++ b/common/src/fxe_file_seek.c @@ -0,0 +1,95 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_file_seek PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the file seek call. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* byte_offset Byte offset into the file */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_seek Actual file seek service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_file_seek(FX_FILE *file_ptr, ULONG byte_offset) +{ + +UINT status; + + + /* Check for a null file pointer. */ + if (file_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual file seek service. */ + status = _fx_file_seek(file_ptr, byte_offset); + + /* Seek is complete, return status. */ + return(status); +} + diff --git a/common/src/fxe_file_truncate.c b/common/src/fxe_file_truncate.c new file mode 100644 index 0000000..f2775a1 --- /dev/null +++ b/common/src/fxe_file_truncate.c @@ -0,0 +1,95 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_file_truncate PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the file truncate call. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* size New size of the file in bytes */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_truncate Actual file truncate service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_file_truncate(FX_FILE *file_ptr, ULONG size) +{ + +UINT status; + + + /* Check for a null file pointer. */ + if (file_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual file truncate service. */ + status = _fx_file_truncate(file_ptr, size); + + /* Truncate is complete, return status. */ + return(status); +} + diff --git a/common/src/fxe_file_truncate_release.c b/common/src/fxe_file_truncate_release.c new file mode 100644 index 0000000..d3d01ab --- /dev/null +++ b/common/src/fxe_file_truncate_release.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_file_truncate_release PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the file truncate release call. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* size New size of the file in bytes */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_truncate_release Actual file truncate release */ +/* service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_file_truncate_release(FX_FILE *file_ptr, ULONG size) +{ + +UINT status; + + + /* Check for a null file pointer. */ + if (file_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual file truncate release service. */ + status = _fx_file_truncate_release(file_ptr, size); + + /* Truncate is complete, return status. */ + return(status); +} + diff --git a/common/src/fxe_file_write.c b/common/src/fxe_file_write.c new file mode 100644 index 0000000..66eea04 --- /dev/null +++ b/common/src/fxe_file_write.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_file_write PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the file write call. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* buffer_ptr Buffer pointer */ +/* size Number of bytes to write */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_write Actual file write service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_file_write(FX_FILE *file_ptr, VOID *buffer_ptr, ULONG size) +{ + +UINT status; + + + /* Check for a null file or buffer pointer. */ + if ((file_ptr == FX_NULL) || (buffer_ptr == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual file write service. */ + status = _fx_file_write(file_ptr, buffer_ptr, size); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_file_write_notify_set.c b/common/src/fxe_file_write_notify_set.c new file mode 100644 index 0000000..e075443 --- /dev/null +++ b/common/src/fxe_file_write_notify_set.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** File */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_file.h" + + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_file_write_notify_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the file write notify set. */ +/* */ +/* INPUT */ +/* */ +/* file_ptr File control block pointer */ +/* file_write_notify Notify function called when */ +/* file is written to */ +/* */ +/* OUTPUT */ +/* */ +/* FX_PTR_ERROR media_ptr is NULL */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ +/* _fx_file_write_notify Actual file write notify set */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_file_write_notify_set(FX_FILE *file_ptr, VOID (*file_write_notify)(FX_FILE *file)) +{ +UINT status; + + /* Check for invalid input pointers. */ + if (file_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual file write notify set service. */ + status = _fx_file_write_notify_set(file_ptr, file_write_notify); + + /* Return status. */ + return(status); +} + diff --git a/common/src/fxe_media_abort.c b/common/src/fxe_media_abort.c new file mode 100644 index 0000000..1ca911f --- /dev/null +++ b/common/src/fxe_media_abort.c @@ -0,0 +1,94 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_media_abort PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the media abort call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_media_abort Actual media abort service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_media_abort(FX_MEDIA *media_ptr) +{ + +UINT status; + + + /* Check for a null media pointer. */ + if (media_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual media abort service. */ + status = _fx_media_abort(media_ptr); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_media_cache_invalidate.c b/common/src/fxe_media_cache_invalidate.c new file mode 100644 index 0000000..d026360 --- /dev/null +++ b/common/src/fxe_media_cache_invalidate.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" + + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_media_cache_invalidate PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the media invalidate service. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_media_cache_invalidate Actual media invalidate */ +/* service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_media_cache_invalidate(FX_MEDIA *media_ptr) +{ + +UINT status; + + + /* Check for a NULL media pointer. */ + if (media_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual media invalidate service. */ + status = _fx_media_cache_invalidate(media_ptr); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_media_check.c b/common/src/fxe_media_check.c new file mode 100644 index 0000000..b11f1e3 --- /dev/null +++ b/common/src/fxe_media_check.c @@ -0,0 +1,136 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" + + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_media_check PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the media check call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to a previously */ +/* opened media */ +/* scratch_memory_ptr Pointer to memory area for */ +/* check disk to use (as */ +/* mentioned above) */ +/* scratch_memory_size Size of the scratch memory */ +/* error_correction_option Specifies which - if any - */ +/* errors are corrected by */ +/* the check disk function. */ +/* Setting the following bit */ +/* causes that error to be */ +/* corrected: */ +/* */ +/* 0x01 -> Fix FAT Chain Errors*/ +/* 0x02 -> Fix Directory Entry */ +/* Errors */ +/* 0x04 -> Fix Lost Clusters */ +/* */ +/* errors_detected Specifies the destination */ +/* ULONG to place the error */ +/* report from check disk. */ +/* This has a similar bit map */ +/* as before: */ +/* */ +/* 0x01 -> FAT Chain Error(s) */ +/* 0x02 -> Directory Entry */ +/* Error(s) */ +/* 0x04 -> Lost Cluster(s) */ +/* */ +/* OUTPUT */ +/* */ +/* FX_SUCCESS Check disk performed its */ +/* operation successfully. */ +/* This does not mean that */ +/* there were no errors. The */ +/* errors_detected variable */ +/* needs to be examined. */ +/* FX_MEDIA_NOT_OPEN The media was not open. */ +/* FX_NOT_ENOUGH_MEMORY The scratch memory was not */ +/* large enough or the nesting */ +/* depth was greater than the */ +/* maximum specified. */ +/* FX_IO_ERROR I/O Error reading/writing to */ +/* the media. */ +/* FX_ERROR_NOT_FIXED Fundamental problem with */ +/* media that couldn't be fixed*/ +/* */ +/* CALLS */ +/* */ +/* _fx_media_check Actual media check service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_media_check(FX_MEDIA *media_ptr, UCHAR *scratch_memory_ptr, ULONG scratch_memory_size, ULONG error_correction_option, ULONG *errors_detected) +{ + +UINT status; + + + /* Check for a NULL media or scratch pointer. */ + if ((media_ptr == FX_NULL) || (scratch_memory_ptr == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual media check service. */ + status = _fx_media_check(media_ptr, scratch_memory_ptr, scratch_memory_size, error_correction_option, errors_detected); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_media_close.c b/common/src/fxe_media_close.c new file mode 100644 index 0000000..3c0fc18 --- /dev/null +++ b/common/src/fxe_media_close.c @@ -0,0 +1,94 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_media_close PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the media close call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_media_close Actual media close service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_media_close(FX_MEDIA *media_ptr) +{ + +UINT status; + + + /* Check for a null media pointer. */ + if (media_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual media close service. */ + status = _fx_media_close(media_ptr); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_media_close_notify_set.c b/common/src/fxe_media_close_notify_set.c new file mode 100644 index 0000000..809fc4b --- /dev/null +++ b/common/src/fxe_media_close_notify_set.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" + + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_media_close_notify_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the media close notify set. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* media_close_notify Notify function called when */ +/* media is closed */ +/* */ +/* OUTPUT */ +/* */ +/* FX_PTR_ERROR media_ptr is NULL */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ +/* _fx_media_close_notify Actual media close notify set */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_media_close_notify_set(FX_MEDIA *media_ptr, VOID (*media_close_notify)(FX_MEDIA *media)) +{ +UINT status; + + /* Check for invalid input pointers. */ + if (media_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual media close notify set service. */ + status = _fx_media_close_notify_set(media_ptr, media_close_notify); + + /* Return status. */ + return(status); +} + diff --git a/common/src/fxe_media_extended_space_available.c b/common/src/fxe_media_extended_space_available.c new file mode 100644 index 0000000..387cb51 --- /dev/null +++ b/common/src/fxe_media_extended_space_available.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_media_extended_space_available PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the media space available call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* available_bytes_ptr Pointer to available bytes */ +/* destination */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_media_extended_space_available Actual space available service*/ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_media_extended_space_available(FX_MEDIA *media_ptr, ULONG64 *available_bytes_ptr) +{ + +UINT status; + + + /* Check for a null media pointer or destination pointer. */ + if ((media_ptr == FX_NULL) || (available_bytes_ptr == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual media space available service. */ + status = _fx_media_extended_space_available(media_ptr, available_bytes_ptr); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_media_flush.c b/common/src/fxe_media_flush.c new file mode 100644 index 0000000..72f5e19 --- /dev/null +++ b/common/src/fxe_media_flush.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_media_flush PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the flush media call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* FX_PTR_ERROR Supplied pointer is invalid */ +/* FX_CALLER_ERROR Caller is not a thread */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ +/* _fx_media_flush Actual media flush service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_media_flush(FX_MEDIA *media_ptr) +{ + +UINT status; + + + /* Check for a null media pointer. */ + if (media_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual media flush service. */ + status = _fx_media_flush(media_ptr); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_media_format.c b/common/src/fxe_media_format.c new file mode 100644 index 0000000..196852b --- /dev/null +++ b/common/src/fxe_media_format.c @@ -0,0 +1,122 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" + + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_media_format PORTABLE C */ +/* 6.1.5 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the media format call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to media control block*/ +/* (does not need to be opened)*/ +/* driver Pointer to FileX driver (must */ +/* be able to field requests */ +/* prior to opening) */ +/* driver_info_ptr Optional information pointer */ +/* memory_ptr Pointer to memory used by the */ +/* FileX for this media. */ +/* memory_size Size of media memory - must */ +/* at least 512 bytes and */ +/* one sector size. */ +/* volume_name Name of the volume */ +/* number_of_fats Number of FAT tables */ +/* directory_entries Number of directory entries */ +/* hidden_sectors Number of hidden sectors */ +/* total_sectors Total number of sectors */ +/* bytes_per_sector Number of bytes per sector */ +/* sectors_per_cluster Number of sectors per cluster */ +/* heads Number of heads */ +/* sectors_per_track Number of sectors per track */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _fx_media_format Actual media format service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* 03-02-2021 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1.5 */ +/* */ +/**************************************************************************/ +UINT _fxe_media_format(FX_MEDIA *media_ptr, VOID (*driver)(FX_MEDIA *media), VOID *driver_info_ptr, UCHAR *memory_ptr, UINT memory_size, + CHAR *volume_name, UINT number_of_fats, UINT directory_entries, UINT hidden_sectors, + ULONG total_sectors, UINT bytes_per_sector, UINT sectors_per_cluster, + UINT heads, UINT sectors_per_track) +{ + +UINT status; + + + /* Check for invalid input pointers. */ + if ((media_ptr == FX_NULL) || (driver == FX_NULL) || (memory_ptr == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual media format service. */ + status = _fx_media_format(media_ptr, driver, driver_info_ptr, memory_ptr, memory_size, + volume_name, number_of_fats, directory_entries, hidden_sectors, + total_sectors, bytes_per_sector, sectors_per_cluster, + heads, sectors_per_track); + + /* Return completion status. */ + return(status); +} + diff --git a/common/src/fxe_media_open.c b/common/src/fxe_media_open.c new file mode 100644 index 0000000..2ac447c --- /dev/null +++ b/common/src/fxe_media_open.c @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" +#include "fx_system.h" + + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_media_open PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the media open call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* media_name Pointer to media name string */ +/* media_driver Media driver entry function */ +/* driver_info_ptr Optional information pointer */ +/* supplied to media driver */ +/* memory_ptr Pointer to memory used by the */ +/* FileX for this media. */ +/* memory_size Size of media memory - must */ +/* at least 512 bytes and */ +/* one sector size. */ +/* media_control_block_size Size of FX_MEDIA structure */ +/* */ +/* OUTPUT */ +/* */ +/* FX_PTR_ERROR One or more input parameters */ +/* are NULL */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ +/* tx_thread_identify Get current thread */ +/* tx_thread_preemption_change Disable/restore preemption */ +/* _fx_media_open Actual media open service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_media_open(FX_MEDIA *media_ptr, CHAR *media_name, + VOID (*media_driver)(FX_MEDIA *), VOID *driver_info_ptr, + VOID *memory_ptr, ULONG memory_size, UINT media_control_block_size) +{ + +UINT status; +ULONG temp; +FX_MEDIA *current_media; +ULONG open_count; + +#ifndef FX_SINGLE_THREAD +TX_THREAD *current_thread; +UINT old_threshold; +#endif + + + /* Check for invalid input pointers. */ + if ((media_ptr == FX_NULL) || (media_driver == FX_NULL) || (memory_ptr == FX_NULL) || (media_control_block_size != sizeof(FX_MEDIA))) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Check for proper size of the logical sector cache. */ + temp = _fx_system_media_max_sector_cache; + + /* Isolate the lowest set bit. */ + temp = (temp & ((~temp) + ((ULONG) 1))); + + /* If FX_MAX_SECTOR_CACHE is a power of 2, the value of temp should be unchanged. */ + if ((temp == 1) || (temp != _fx_system_media_max_sector_cache)) + { + + /* Not a power of 2, return an error. */ + return(FX_MEDIA_INVALID); + } + + /* Check for proper size of the FAT cache. */ + temp = _fx_system_media_max_fat_cache; + + /* Isolate the lowest set bit. */ + temp = (temp & ((~temp) + ((ULONG) 1))); + + /* If FX_MAX_FAT_CACHE is a power of 2, the value of temp should be unchanged. */ + if ((temp == 1) || (temp != _fx_system_media_max_fat_cache)) + { + + /* Not a power of 2, return an error. */ + return(FX_MEDIA_INVALID); + } + +#ifndef FX_SINGLE_THREAD + + /* Pickup current thread pointer. At this point we know the current thread pointer is non-null since + it was checked by code in FX_CALLER_CHECKING_CODE macro. */ + current_thread = tx_thread_identify(); + + /* Disable preemption temporarily. */ + tx_thread_preemption_change(current_thread, 0, &old_threshold); +#endif + + /* Loop to check for the media already opened. */ + current_media = _fx_system_media_opened_ptr; + open_count = _fx_system_media_opened_count; + while (open_count--) + { + + /* Is the new media pointer already open? */ + if (media_ptr == current_media) + { + +#ifndef FX_SINGLE_THREAD + + /* Restore preemption. */ + tx_thread_preemption_change(current_thread, old_threshold, &old_threshold); +#endif + + /* Duplicate media open, return an error! */ + return(FX_PTR_ERROR); + } + + /* Move to next entry. */ + current_media = current_media -> fx_media_opened_next; + } + +#ifndef FX_SINGLE_THREAD + + /* Restore preemption. */ + tx_thread_preemption_change(current_thread, old_threshold, &old_threshold); +#endif + + /* Call actual media open service. */ + status = _fx_media_open(media_ptr, media_name, media_driver, driver_info_ptr, + memory_ptr, memory_size); + + /* Return status. */ + return(status); +} + diff --git a/common/src/fxe_media_open_notify_set.c b/common/src/fxe_media_open_notify_set.c new file mode 100644 index 0000000..aa2e378 --- /dev/null +++ b/common/src/fxe_media_open_notify_set.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" + + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_media_open_notify_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the media open notify set. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* media_open_notify Notify function called when */ +/* media is open */ +/* */ +/* OUTPUT */ +/* */ +/* FX_PTR_ERROR media_ptr is NULL */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ +/* _fx_media_open_notify Actual media open notify set */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_media_open_notify_set(FX_MEDIA *media_ptr, VOID (*media_open_notify)(FX_MEDIA *media)) +{ +UINT status; + + /* Check for invalid input pointers. */ + if (media_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual media open notify set service. */ + status = _fx_media_open_notify_set(media_ptr, media_open_notify); + + /* Return status. */ + return(status); +} + diff --git a/common/src/fxe_media_read.c b/common/src/fxe_media_read.c new file mode 100644 index 0000000..08fe46d --- /dev/null +++ b/common/src/fxe_media_read.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_media_read PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the media read call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* logical_sector Logical sector to read */ +/* buffer_ptr Pointer to caller's buffer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_media_read Actual media read service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_media_read(FX_MEDIA *media_ptr, ULONG logical_sector, VOID *buffer_ptr) +{ + +UINT status; + + + /* Check for a null media pointer or buffer pointer. */ + if ((media_ptr == FX_NULL) || (buffer_ptr == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual media read service. */ + status = _fx_media_read(media_ptr, logical_sector, buffer_ptr); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_media_space_available.c b/common/src/fxe_media_space_available.c new file mode 100644 index 0000000..169bf54 --- /dev/null +++ b/common/src/fxe_media_space_available.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_media_space_available PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the media space available call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* available_bytes_ptr Pointer to available bytes */ +/* destination */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_media_space_available Actual space available service*/ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_media_space_available(FX_MEDIA *media_ptr, ULONG *available_bytes_ptr) +{ + +UINT status; + + + /* Check for a null media pointer or destination pointer. */ + if ((media_ptr == FX_NULL) || (available_bytes_ptr == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual media space available service. */ + status = _fx_media_space_available(media_ptr, available_bytes_ptr); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_media_volume_get.c b/common/src/fxe_media_volume_get.c new file mode 100644 index 0000000..89a8966 --- /dev/null +++ b/common/src/fxe_media_volume_get.c @@ -0,0 +1,100 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" + + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_media_volume_get PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the media volume get service. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* volume_name Pointer to destination for */ +/* the volume name (maximum */ +/* 11 characters + NULL) */ +/* volume_source Source of volume */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_media_volume_get Actual media volume get */ +/* service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_media_volume_get(FX_MEDIA *media_ptr, CHAR *volume_name, UINT volume_source) +{ + +UINT status; + + + /* Check for a NULL media or volume name pointer. */ + if ((media_ptr == FX_NULL) || (volume_name == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual media volume get service. */ + status = _fx_media_volume_get(media_ptr, volume_name, volume_source); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_media_volume_get_extended.c b/common/src/fxe_media_volume_get_extended.c new file mode 100644 index 0000000..3943892 --- /dev/null +++ b/common/src/fxe_media_volume_get_extended.c @@ -0,0 +1,107 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" + + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_media_volume_get_extended PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the media volume get service. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* volume_name Pointer to destination for */ +/* the volume name (maximum */ +/* 11 characters + NULL) */ +/* volume_name_buffer_length Buffer length for volume_name */ +/* volume_source Source of volume */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_media_volume_get_extended Actual media volume get */ +/* service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_media_volume_get_extended(FX_MEDIA *media_ptr, CHAR *volume_name, UINT volume_name_buffer_length, UINT volume_source) +{ + +UINT status; + + + /* Check for a NULL media or volume name pointer. */ + if ((media_ptr == FX_NULL) || (volume_name == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for zero length buffer. */ + if (volume_name_buffer_length == 0) + { + return(FX_BUFFER_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual media volume get service. */ + status = _fx_media_volume_get_extended(media_ptr, volume_name, volume_name_buffer_length, volume_source); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_media_volume_set.c b/common/src/fxe_media_volume_set.c new file mode 100644 index 0000000..6d5e2bd --- /dev/null +++ b/common/src/fxe_media_volume_set.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" + + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_media_volume_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the media volume set service. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* volume_name New volume name */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_media_volume_set Actual media volume name set */ +/* service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_media_volume_set(FX_MEDIA *media_ptr, CHAR *volume_name) +{ + +UINT status; + + + /* Check for a NULL media or volume name pointer. */ + if ((media_ptr == FX_NULL) || (volume_name == FX_NULL) || (volume_name[0] == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual media volume set service. */ + status = _fx_media_volume_set(media_ptr, volume_name); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_media_write.c b/common/src/fxe_media_write.c new file mode 100644 index 0000000..1bc731b --- /dev/null +++ b/common/src/fxe_media_write.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Media */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_media.h" + + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_media_write PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the media write call. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* logical_sector Logical sector to read */ +/* buffer_ptr Pointer to caller's buffer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _fx_media_write Actual media write service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_media_write(FX_MEDIA *media_ptr, ULONG logical_sector, VOID *buffer_ptr) +{ + +UINT status; + + + /* Check for a null media pointer. */ + if (media_ptr == FX_NULL) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual media write service. */ + status = _fx_media_write(media_ptr, logical_sector, buffer_ptr); + + /* Return a successful status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_system_date_get.c b/common/src/fxe_system_date_get.c new file mode 100644 index 0000000..79b25cd --- /dev/null +++ b/common/src/fxe_system_date_get.c @@ -0,0 +1,92 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** System */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_system_date_get PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the get system date call. */ +/* */ +/* INPUT */ +/* */ +/* year Pointer to year */ +/* month Pointer to month */ +/* day Pointer to day */ +/* */ +/* OUTPUT */ +/* */ +/* FX_PTR_ERROR Invalid destination pointer */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ +/* _fx_system_date_get Actual get system date call */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_system_date_get(UINT *year, UINT *month, UINT *day) +{ + +UINT status; + + + /* Check for an invalid pointer. */ + if ((year == FX_NULL) || (month == FX_NULL) || (day == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Call actual service to get the system date. */ + status = _fx_system_date_get(year, month, day); + + /* Return status. */ + return(status); +} + diff --git a/common/src/fxe_system_date_set.c b/common/src/fxe_system_date_set.c new file mode 100644 index 0000000..f5b01c7 --- /dev/null +++ b/common/src/fxe_system_date_set.c @@ -0,0 +1,257 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** System */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_system_date_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the set system date call. */ +/* */ +/* INPUT */ +/* */ +/* year Year (1980-2107) */ +/* month Month (1-12) */ +/* day Day (1-28/29/30/31) */ +/* */ +/* OUTPUT */ +/* */ +/* FX_INVALID_YEAR Supplied year is invalid */ +/* FX_INVALID_MONTH Supplied month is invalid */ +/* FX_INVALID_DAY Supplied day is invalid */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ +/* _fx_system_date_set Actual system date set call */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_system_date_set(UINT year, UINT month, UINT day) +{ + +UINT status; + + + /* Check for invalid year. */ + if ((year < FX_BASE_YEAR) || (year > FX_MAXIMUM_YEAR)) + { + return(FX_INVALID_YEAR); + } + + /* Check for invalid day. */ + if (day < 1) + { + return(FX_INVALID_DAY); + } + + /* Check for invalid day. */ + switch (month) + { + + case 1: + { + + /* Check for 31 days. */ + if (day > 31) + { + return(FX_INVALID_DAY); + } + break; + } + + case 2: + { + + /* Check for leap year. */ + if ((year % 4) == 0) + { + + /* Leap year, February has 29 days. */ + if (day > 29) + { + return(FX_INVALID_DAY); + } + } + else + { + + /* Otherwise, non-leap year. February has + 28 days. */ + if (day > 28) + { + return(FX_INVALID_DAY); + } + } + break; + } + + case 3: + { + + /* Check for 31 days. */ + if (day > 31) + { + return(FX_INVALID_DAY); + } + break; + } + + case 4: + { + + /* Check for 30 days. */ + if (day > 30) + { + return(FX_INVALID_DAY); + } + break; + } + + case 5: + { + + /* Check for 31 days. */ + if (day > 31) + { + return(FX_INVALID_DAY); + } + break; + } + + case 6: + { + + /* Check for 30 days. */ + if (day > 30) + { + return(FX_INVALID_DAY); + } + break; + } + + case 7: + { + + /* Check for 31 days. */ + if (day > 31) + { + return(FX_INVALID_DAY); + } + break; + } + + case 8: + { + + /* Check for 31 days. */ + if (day > 31) + { + return(FX_INVALID_DAY); + } + break; + } + + case 9: + { + + /* Check for 30 days. */ + if (day > 30) + { + return(FX_INVALID_DAY); + } + break; + } + + case 10: + { + + /* Check for 31 days. */ + if (day > 31) + { + return(FX_INVALID_DAY); + } + break; + } + + case 11: + { + + /* Check for 30 days. */ + if (day > 30) + { + return(FX_INVALID_DAY); + } + break; + } + + case 12: + { + + /* Check for 31 days. */ + if (day > 31) + { + return(FX_INVALID_DAY); + } + break; + } + + default: + + /* Invalid month. */ + return(FX_INVALID_MONTH); + } + + /* Call actual system date set service. */ + status = _fx_system_date_set(year, month, day); + + /* Return status. */ + return(status); +} + diff --git a/common/src/fxe_system_time_get.c b/common/src/fxe_system_time_get.c new file mode 100644 index 0000000..f18e333 --- /dev/null +++ b/common/src/fxe_system_time_get.c @@ -0,0 +1,92 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** System */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_system_time_get PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks of errors in the get system time call. */ +/* */ +/* INPUT */ +/* */ +/* hour Pointer to hour */ +/* minute Pointer to minute */ +/* second Pointer to second */ +/* */ +/* OUTPUT */ +/* */ +/* FX_PTR_ERROR Invalid destination pointer */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ +/* _fx_system_time_get Actual get time service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_system_time_get(UINT *hour, UINT *minute, UINT *second) +{ + +UINT status; + + + /* Check for an invalid destination pointer. */ + if ((hour == FX_NULL) || (minute == FX_NULL) || (second == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Call actual get time service. */ + status = _fx_system_time_get(hour, minute, second); + + /* Return status. */ + return(status); +} + diff --git a/common/src/fxe_system_time_set.c b/common/src/fxe_system_time_set.c new file mode 100644 index 0000000..1c431e2 --- /dev/null +++ b/common/src/fxe_system_time_set.c @@ -0,0 +1,106 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** System */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_system.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_system_time_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the set the system time call. */ +/* */ +/* INPUT */ +/* */ +/* hour Hour (0-23) */ +/* minute Minute (0-59) */ +/* second Second (0-59) */ +/* */ +/* OUTPUT */ +/* */ +/* FX_INVALID_HOUR Supplied hour is invalid */ +/* FX_INVALID_MINUTE Supplied minute is invalid */ +/* FX_INVALID_SECOND Supplied second is invalid */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ +/* _fx_system_time_set Actual system time set call */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_system_time_set(UINT hour, UINT minute, UINT second) +{ + +UINT status; + + + /* Check for invalid hour. */ + if (hour > FX_MAXIMUM_HOUR) + { + return(FX_INVALID_HOUR); + } + + /* Check for invalid minute. */ + if (minute > FX_MAXIMUM_MINUTE) + { + return(FX_INVALID_MINUTE); + } + + /* Check for invalid second. */ + if (second > FX_MAXIMUM_SECOND) + { + return(FX_INVALID_SECOND); + } + + /* Call the actual set system time service. */ + status = _fx_system_time_set(hour, minute, second); + + /* Return status. */ + return(status); +} + diff --git a/common/src/fxe_unicode_directory_create.c b/common/src/fxe_unicode_directory_create.c new file mode 100644 index 0000000..12d38af --- /dev/null +++ b/common/src/fxe_unicode_directory_create.c @@ -0,0 +1,108 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Unicode */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_unicode.h" + + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_unicode_directory_create PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the Unicode directory create */ +/* service. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to media */ +/* source_unicode_name Pointer to source unicode name*/ +/* source_unicode_length Unicode name length */ +/* short_name Designated short name */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _fx_unicode_directory_create Actual Unicode directory */ +/* create service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_unicode_directory_create(FX_MEDIA *media_ptr, UCHAR *source_unicode_name, ULONG source_unicode_length, CHAR *short_name) +{ + +UINT status, i; + + /* Check for a NULL media or name pointers. */ + if ((media_ptr == FX_NULL) || (source_unicode_name == FX_NULL) || (source_unicode_length == 0) || (short_name == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Check unicode zero in source_unicode_name */ + for (i = 0; i < (source_unicode_length << 1); i += 2) + { + if ((source_unicode_name[i] == 0) && (source_unicode_name[i + 1] == 0)) + { + return(FX_INVALID_NAME); + } + } + + /* Call actual Unicode directory create service. */ + status = _fx_unicode_directory_create(media_ptr, source_unicode_name, source_unicode_length, short_name); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_unicode_directory_rename.c b/common/src/fxe_unicode_directory_rename.c new file mode 100644 index 0000000..fd6d274 --- /dev/null +++ b/common/src/fxe_unicode_directory_rename.c @@ -0,0 +1,122 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Unicode */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_unicode.h" + + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_unicode_directory_rename PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the Unicode directory rename */ +/* service. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to media */ +/* old_unicode_name Pointer to old unicode name */ +/* old_unicode_length Old unicode name length */ +/* new_unicode_name Pointer to new unicode name */ +/* new_unicode_length Old unicode name length */ +/* new_short_name Pointer to new short name */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _fx_unicode_directory_rename Actual Unicode directory */ +/* rename service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_unicode_directory_rename(FX_MEDIA *media_ptr, UCHAR *old_unicode_name, ULONG old_unicode_length, + UCHAR *new_unicode_name, ULONG new_unicode_length, CHAR *new_short_name) +{ + +UINT status, i; + + + /* Check for a NULL media or name pointers. */ + if ((media_ptr == FX_NULL) || (old_unicode_name == FX_NULL) || (old_unicode_length == 0) || + (new_unicode_name == FX_NULL) || (new_unicode_length == 0) || (new_short_name == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Check unicode zero in old_unicode_name */ + for (i = 0; i < (old_unicode_length << 1); i += 2) + { + if ((old_unicode_name[i] == 0) && (old_unicode_name[i + 1] == 0)) + { + return(FX_INVALID_NAME); + } + } + + /* Check unicode zero in new_unicode_name */ + for (i = 0; i < (new_unicode_length << 1); i += 2) + { + if ((new_unicode_name[i] == 0) && (new_unicode_name[i + 1] == 0)) + { + return(FX_INVALID_NAME); + } + } + + /* Call actual Unicode directory rename service. */ + status = _fx_unicode_directory_rename(media_ptr, old_unicode_name, old_unicode_length, new_unicode_name, new_unicode_length, new_short_name); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_unicode_file_create.c b/common/src/fxe_unicode_file_create.c new file mode 100644 index 0000000..135bdf5 --- /dev/null +++ b/common/src/fxe_unicode_file_create.c @@ -0,0 +1,108 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Unicode */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_unicode.h" + + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_unicode_file_create PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the Unicode file create service. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to media */ +/* source_unicode_name Pointer to source unicode name*/ +/* source_unicode_length Unicode name length */ +/* short_name Designated short name */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _fx_unicode_file_create Actual Unicode file create */ +/* service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_unicode_file_create(FX_MEDIA *media_ptr, UCHAR *source_unicode_name, ULONG source_unicode_length, CHAR *short_name) +{ + +UINT status, i; + + + /* Check for a NULL media or name pointers. */ + if ((media_ptr == FX_NULL) || (source_unicode_name == FX_NULL) || (source_unicode_length == 0) || (short_name == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Check unicode zero in source_unicode_name */ + for (i = 0; i < (source_unicode_length << 1); i += 2) + { + if ((source_unicode_name[i] == 0) && (source_unicode_name[i + 1] == 0)) + { + return(FX_INVALID_NAME); + } + } + + /* Call actual Unicode file create service. */ + status = _fx_unicode_file_create(media_ptr, source_unicode_name, source_unicode_length, short_name); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_unicode_file_rename.c b/common/src/fxe_unicode_file_rename.c new file mode 100644 index 0000000..b98cb3e --- /dev/null +++ b/common/src/fxe_unicode_file_rename.c @@ -0,0 +1,122 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Unicode */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_unicode.h" + + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_unicode_file_create PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the Unicode file rename service. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to media */ +/* old_unicode_name Pointer to old unicode name */ +/* old_unicode_length Old unicode name length */ +/* new_unicode_name Pointer to new unicode name */ +/* new_unicode_length New unicode name length */ +/* new_short_name Designated new short name */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _fx_unicode_file_rename Actual Unicode file rename */ +/* service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_unicode_file_rename(FX_MEDIA *media_ptr, UCHAR *old_unicode_name, ULONG old_unicode_length, + UCHAR *new_unicode_name, ULONG new_unicode_length, CHAR *new_short_name) +{ + +UINT status, i; + + + /* Check for a NULL media or name pointers. */ + if ((media_ptr == FX_NULL) || (old_unicode_name == FX_NULL) || (old_unicode_length == 0) || + (new_unicode_name == FX_NULL) || (new_unicode_length == 0) || (new_short_name == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Check unicode zero in old_unicode_name */ + for (i = 0; i < (old_unicode_length << 1); i += 2) + { + if ((old_unicode_name[i] == 0) && (old_unicode_name[i + 1] == 0)) + { + return(FX_INVALID_NAME); + } + } + + /* Check unicode zero in new_unicode_name */ + for (i = 0; i < (new_unicode_length << 1); i += 2) + { + if ((new_unicode_name[i] == 0) && (new_unicode_name[i + 1] == 0)) + { + return(FX_INVALID_NAME); + } + } + + /* Call actual Unicode file rename service. */ + status = _fx_unicode_file_rename(media_ptr, old_unicode_name, old_unicode_length, + new_unicode_name, new_unicode_length, new_short_name); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_unicode_name_get.c b/common/src/fxe_unicode_name_get.c new file mode 100644 index 0000000..223502a --- /dev/null +++ b/common/src/fxe_unicode_name_get.c @@ -0,0 +1,101 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Unicode */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_unicode.h" + + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_unicode_name_get PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the Unicode name get */ +/* service. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to media */ +/* source_short_name Pointer to short file name */ +/* destination_unicode_name Pointer to destination name */ +/* destination_unicode_length Destination for name length */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _fx_directory_unicode_search Search for unicode name */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_unicode_name_get(FX_MEDIA *media_ptr, CHAR *source_short_name, + UCHAR *destination_unicode_name, ULONG *destination_unicode_length) +{ + +UINT status; + + + /* Check for a NULL media or name pointers. */ + if ((media_ptr == FX_NULL) || (source_short_name == FX_NULL) || (destination_unicode_name == FX_NULL) || (destination_unicode_length == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual Unicode name get service. */ + status = _fx_unicode_name_get(media_ptr, source_short_name, + destination_unicode_name, destination_unicode_length); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_unicode_name_get_extended.c b/common/src/fxe_unicode_name_get_extended.c new file mode 100644 index 0000000..9aa5cef --- /dev/null +++ b/common/src/fxe_unicode_name_get_extended.c @@ -0,0 +1,109 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Unicode */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_unicode.h" + + +FX_CALLER_CHECKING_EXTERNS + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_unicode_name_get_extended PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the Unicode name get */ +/* service. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to media */ +/* source_short_name Pointer to short file name */ +/* destination_unicode_name Pointer to destination name */ +/* destination_unicode_length Destination for name length */ +/* unicode_name_buffer_length Buffer length of unicode name */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _fx_unicode_name_get_extended Actual unicode name get */ +/* service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_unicode_name_get_extended(FX_MEDIA *media_ptr, CHAR *source_short_name, + UCHAR *destination_unicode_name, ULONG *destination_unicode_length, ULONG unicode_name_buffer_length) +{ + +UINT status; + + + /* Check for a NULL media or name pointers. */ + if ((media_ptr == FX_NULL) || (source_short_name == FX_NULL) || (destination_unicode_name == FX_NULL) || (destination_unicode_length == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for zero length buffer. */ + if (unicode_name_buffer_length == 0) + { + return(FX_BUFFER_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Call actual Unicode name get service. */ + status = _fx_unicode_name_get_extended(media_ptr, source_short_name, + destination_unicode_name, destination_unicode_length, unicode_name_buffer_length); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_unicode_short_name_get.c b/common/src/fxe_unicode_short_name_get.c new file mode 100644 index 0000000..ec7aa21 --- /dev/null +++ b/common/src/fxe_unicode_short_name_get.c @@ -0,0 +1,109 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Unicode */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_unicode.h" + + +FX_CALLER_CHECKING_EXTERNS + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_unicode_short_name_get PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the Unicode short name get */ +/* service. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to media */ +/* source_unicode_name Pointer to unicode name */ +/* source_unicode_length Length of unicode name */ +/* destination_short_name Pointer to destination name */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _fx_unicode_short_name_get Actual unicode short name get */ +/* service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_unicode_short_name_get(FX_MEDIA *media_ptr, UCHAR *source_unicode_name, ULONG source_unicode_length, + CHAR *destination_short_name) +{ + +UINT status, i; + + + /* Check for a NULL media or name pointers. */ + if ((media_ptr == FX_NULL) || (source_unicode_name == FX_NULL) || (source_unicode_length == 0) || (destination_short_name == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Check unicode zero in source_unicode_name */ + for (i = 0; i < (source_unicode_length << 1); i += 2) + { + if ((source_unicode_name[i] == 0) && (source_unicode_name[i + 1] == 0)) + { + return(FX_INVALID_NAME); + } + } + + /* Call actual Unicode short name get service. */ + status = _fx_unicode_short_name_get(media_ptr, source_unicode_name, source_unicode_length, destination_short_name); + + /* Return status to the caller. */ + return(status); +} + diff --git a/common/src/fxe_unicode_short_name_get_extended.c b/common/src/fxe_unicode_short_name_get_extended.c new file mode 100644 index 0000000..d38d765 --- /dev/null +++ b/common/src/fxe_unicode_short_name_get_extended.c @@ -0,0 +1,116 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Unicode */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define FX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "fx_api.h" +#include "fx_unicode.h" + + +FX_CALLER_CHECKING_EXTERNS + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fxe_unicode_short_name_get_extended PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the Unicode short name get */ +/* service. */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Pointer to media */ +/* source_unicode_name Pointer to unicode name */ +/* source_unicode_length Length of unicode name */ +/* destination_short_name Pointer to destination name */ +/* short_name_buffer_length Buffer length of short name */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _fx_unicode_short_name_get_extended Actual unicode short name get */ +/* service */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* 09-30-2020 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1 */ +/* */ +/**************************************************************************/ +UINT _fxe_unicode_short_name_get_extended(FX_MEDIA *media_ptr, UCHAR *source_unicode_name, ULONG source_unicode_length, + CHAR *destination_short_name, ULONG short_name_buffer_length) +{ + +UINT status, i; + + + /* Check for a NULL media or name pointers. */ + if ((media_ptr == FX_NULL) || (source_unicode_name == FX_NULL) || (source_unicode_length == 0) || (destination_short_name == FX_NULL)) + { + return(FX_PTR_ERROR); + } + + /* Check for zero length buffer. */ + if (short_name_buffer_length == 0) + { + return(FX_BUFFER_ERROR); + } + + /* Check for a valid caller. */ + FX_CALLER_CHECKING_CODE + + /* Check unicode zero in source_unicode_name */ + for (i = 0; i < (source_unicode_length << 1); i += 2) + { + if ((source_unicode_name[i] == 0) && (source_unicode_name[i + 1] == 0)) + { + return(FX_INVALID_NAME); + } + } + + /* Call actual Unicode short name get service. */ + status = _fx_unicode_short_name_get_extended(media_ptr, source_unicode_name, source_unicode_length, destination_short_name, short_name_buffer_length); + + /* Return status to the caller. */ + return(status); +} + diff --git a/docs/deps.png b/docs/deps.png new file mode 100644 index 0000000000000000000000000000000000000000..e67db1b9e14996eb0bce07b96362e384b49ea2ba GIT binary patch literal 65876 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ð$7<~NfoSF*E{?5w@kw>6_M0R_s$MyypdZ;i0#uby%%$7S}k z(OivBxvl0UHE=g&E7S&>+PWCjYpR^+1j9}9Ek7PBi7y3Ge&>R~y6ri-{6Qj*uoci65ew z!#6ogKNk@VasRnSf8#}KoGhdr45Bc|u-1$Esrcksc*xXHjzO)felGbf6lKc7Dw)0> z9*_TIf3x|m>ld(o+7lQwFh;&6=x$CJw@PbIrruQisC%GW2)&3Ge_0o8<>vB{%o&$B z1y`RAo4sF&;<6geuPSi>m^tvBf+w>&NR<>SOL6;jZgqV@Y?(>lb(^Bk^kv23YINNE zU0SI!6t=ZlqU^#qSJRia-tDt;D;m$HGQ2BTmITgJZx$A#Y)8+`kGL0~nIGIpX7{MT zy!_*sn0WC?R_>6pIh!h&D8(RRhf8q#Dlj7ErURka+skcBi?1*+b7aFC$75Ji0yy4X zK4F!5P8)t&4Dd{7PFSDCLo=7@CxXqFR7(lBQH97noHhg_Ri_gVxpRMn|fBQ^) zwtl(vx$kzT$juI*R`!!bl91lonZjR+)2?EXtm%Epx!2W|!>tzc7zNj;m~Iu5ja$EO zf4F03`$pY!bggVSA(@^6EYt_D2d@+SS}H;RV*&W-F09?@IAigWlUgEF0_S7hy+D2g za}v>6g>w^^e>bM>^HFF#ewlo=q?p^ot(zg7rq8xu#d5V zM;c<>1dM+ig2tfccj)>+2kkulY8>A$`ycg4-y&AM)~j92=glQ4VPVKOLZ#SyooIPL zf>YKrPMcl#;O6gOf55gFJh!2Q#Z)Q+9~AZs(&??sUkk!=l1zUbpn9W5b*^gNSmWRp z(q^;rETOV>#kf2)jB2VFTGEz(WE~zO_xF6?uQ_IQU3=Sh?ViP2Bh!d}c$|=S^Mw6q z;UY2)$qAVeb8tIOC3pdIMRRe{0H$wItjYaFI&wF+OVbEE5^hvH^)e$+0wBA!cs z6gxu<(Hzia|7iS}wJtA#u0jUB_Dm z{Urz-goYB1FRwN~qP^5q87cu@mIVVFE1TnH!$n~xLF-HVSckM%YZ19UNCuu4n5nVo z|CaMv&+pw~yJzF&;j&`myWe)#r9&q-$?o;cRzR1qo-ZIHPdZ8smulECwh+LIsqLZE z!nL^(7@o=%4n8Ov8)4V#vnulwFLXeZ&0F-@Zl+vktXV0Nd>7TFEkb}Kukfo-`a|{R zTII$Hl!i+nwLFI(XW^dHhz@#~Bfv{|A&}`v^1AsV!6(Fu(@` zv*;81CQ6BXW4;l$o1r$ln`P9IEaLWI?n`7>Xtz{Tm1Yu!Q*j0f)_e2lSQ+9h^IY?2 zv!~V(@)Oe)RY#}yiLCZBgA#Q_#BET_ln|Bm*Di>-Jtv#R=nhG~x*z!jGC9L|c2{&g z=F`rZUv+okh$vwU^?i3M!gr48D2EL`w6a4C(ILq5zxbv)eViB}K{t*6aao3mBBJ;S8cN1pBC;ztkO5E_B?9b4Pf&68D5Mw_1fU(jL~E zntGcgrM4V*DOjRD`@5zd&c3?940Di~AYRw>A$nL(rY_$Z_>?FP1qM2eFKScjmz3l? z_Auj*@tcbMfDoVX@&N{MO?~OJC*-mQK?bR263SZyiDkkczcI#Q5TdkvohzJYuGN8P zE0XfDc^c5MO&V;cEynS@uuRMn8f9Ra)@$Yozjh_xKS7B1a1u;ZS{^1d?6^m4^)vU> zCxD26b~MB=R4)GQ_s9Jf_h=#QC$rj9pnNg|>JcyRw^@oP^SHh#z!XoLdDqVP!DD2f z8ls}xIt-Pt-{8I-wE1-6!)uFHRZyoRr7BV{SQxKFaqIKp!KBST~iGQWMnC)P(Fi)4FR= zU`b})!qE=G{ymMG#(WWiQn&vG3Axk#b(PpLQ$uuk#vHekTRE|8d@WC*5IP1CqdZs7 zuW{pe^5lEGF#+*&h}D+?qaN5}i%*3cUO$X(K+``1E0w5}2N??I0;&h_M~IZ1f_eY9 zT$^+}fjj{i@kIjC7y1#SaGnQmiBS=9O@yFk9)DoO+a{WcC$3*F0yL4TL?gjRj+U{A zIJZb~GOQ=h7qPufLGf(SRtW%^;N@J#{vzQ$vqGhmvZ2ouz!AZ8f$M3|Xxg?jhjX1^(31Q?b07V$8k%2|#KCAl zpJ=a3k@Xaoq!)Z|0>_9CwBN;>bd90s@)oKOA8bE^_x{Q}ZLM_rHkBM<(X3taH12mG zbH_aEj#Z#n1i7CvMV}p0rNOPnUKf+D&;o)rG({3`nqETJEl;Oya8-5Mi>Gb)ZrE-6 zZP#2@Fb$MH2_&6WPJM!1iX%3^)V(LBcm;3H+fhdX?vi5_P+q0XgwWgL+O#sS{nBjk zKsj8U3$~3(|M)=5FMQ!l?s$$apS2 z=D)}ko?DSt%a)8rIHsQlJwpa5%<}XdG!kkxf^-Rrz;A1dx0A9ny0D(ZX)ZvHk zTJOktNupe|wn+tC*K(dvRSaDl+#QnB#d*s(d=?25$ubx; z`%z6sj_XF}ZZ$c+^0-w)9m-|EjvU<>$9`q*ZEAZZ%hZteqmVvEVK|N%N20_qPJ_#i znf|Ks3GSzUkme||%E>4obFaQT_9?VR9px+ZYf$RJIfAg>3$1minh*Wgxv-6U18;97 znNvX&``%+6wHA^y;}aL5LEiIJ$J(F|m0yI)4m+>kp2#!^mwZ;Yrifx<_^V|Ne09t| z6iLbkx&G7-z~Sb;!eg|(aA^OfmhDpOT{C5Ioc6T~-`QrO4q5xS%|#*sX-qQO3<5Zd zq?#neJ)h$}v%f-dTPU^SM=Z>{9+-=m@>5ALdy!Qj8@Pd1l3|EfFmgiW=S=dB$KG!s z@rFM18VbtKdhnyiOW^Rc*Hy^6TjC>DSuC5 zF}1WlgG+V6@th*N#||DmfyK`E4N5;HQipq3og-jvieR1|%5>Ss&2!s?TMn_O%QucpiTHHr;w^P{&Ytld!vMaTYirEd;nL)& z#a5@`Gbq1RQXP=$Kxq#VQb<+H@zc*^!;bq$_R7{|*QKGy)syhlM?nEgZ60eDYg&<} zAO|?2be>R~1+P8zDoQ+|^%Dfajf?LeS|#22XQIiC8Y46zLfN3vaDyf#SU)NL1(B-*YHHDJ5l|JrDU!=vaX`HCxXJ&>0qf{eA^O}VXJuW$&15`Ec96SL zY^;Sca;_M{&Xx?XQ2;)?B2gi+R1~#^(U!^`D9!6KK#0hHcOw)fY45C38gxMYyzqt!ZE+E22UMcHY^i zEJ5%9wS`||I~-DjB+`9$Pp1ddr?D4rTk?uE1BnzahWh;6JAB?MvE<3=2DX*d?HZq!4~FxAa2>s3KlwjM-SZ zi8p(rX81T@`TZaxI07&^X^|nvR)>#x@Kvb!^LSfoO5Y;s(|*cj*wuze;2+2V<>B0M zb>k5ZmjARCUla67#Fw^SGs^(qj}*1$%b~?Al9b5qL?&nBlP;!m!?dPj-pZD#rE@Vz zwvE^*9dD21d8ohv9Dft8eQ{iCf&MwBJR8R6GfO-93VuY-xVkfp#>#;k9)m> z`Tm`|%!xaD^rnJbiiG5ukUYlu`M2E4Sb02HfCBp?;(uNoM40i0m1MUZpNZBs$ZN)= z{W*SSeT@H5Sl&8yustTp_mg;$?e1WuBIoC|kExD8FhUpI=jNFB3a~3hQ8eu;_!#{- z@w}&#C7t{Moh+g#!MZWbF~nsJZ5%YGwl*&eMgz#C!LBFg-mQ;odSVk~Lr)F-`W{uX zo!aXkG(TS?@r6-=z06IZtO%YjOYd!($0)%*g$hwjTbm?sGaw$-V%D#`r_KuhO*mOM zDED;CmT#9Nd=p*zLMCkFTMrHBQm5LyikrF2^zE4ZZOopD<_k01f5S7bPDdWVJ2IJz zg(&0CL(?V}LWqB{LrnUN!#lMmZri31tJ|*j>0|xfA+` zqZBDT6Htz~8IOo^C7gvyviWUw=RN-MswrsZpIgGZ!3j_d?saIi*oW>-=&%AaQJAJ# zTvj7p(8USoVYgBa2?}KhPGFW2)r)k5p>cR81Ab66bD&tZO&w|Ha=dKQTqhrbm?H!x zH37fZ6B5S(`hh;TgbS4hnJW?5Ie!<^5~R+tN!Q%o!mQ@IvtSq)V)&vaE_w!Aw}=2s zhSE-OLwurk+xF4OeU;|@pK#!{k`)nK`k0lLco)Lo-xRlIp$yX*bd#HdyJCj#Y?A?4+pKEFR}^lz&{ z^lU(1<7o6#&?yQ%*~>eUsP`f)_Xc$1fDnXFsDEg?fECizEb5|->h z>8Et<%MZ8!lK4R&iNNmFe$N)7qU^@V7}L9<|0)8pPy|TrWcz=aicp(eyZ^=l{4Y^X z!iR7&&wBK?bI$9BDkotGh9FRLI#0d||9`Yl-pT$sN&GF5a_ja_`W~oZ|5N3``9GHa ze`GvjqbvVhg8tvnLKEu#2`K#ywj?ux`A>)c{S2{h$p1vF|NgTt(VzYIzu!Lb{r_oT zgTM>Kf12@^Pjr{?Ki0T>|8@MAG79q_cp=co(Vp>go6V0sJ_SRE4~3c_-*oUH_#ar_ z-}OB4o%~_!Ah;gE=GxK0zLB7(+cy{23Eq=Z5Et;R)_?cSZKrR^*q4#3&#$O3gF%%8 zGUx}$x}S8rGiAz#$wd=PZI;Rks-&7 zmvTn`Jyistk{RFne&0!Q>LUG2C>9aY**RlWn0+QSF9+`idaB`nk5)o{8QyN3$5%EQ za^)}CEQ#%pJu7$Jb?DUOo2Eo{uNhg0LPv6P|Q=679cuK;!Og|L>M(_NkKc zLAAdX7lEUp*~d2-$;fH0adh!mAzPW`$gJ#FPkeT`WG=ft15B2#oo2z(oK$|O90ScX zFS>tK6+BAD8=r9gtZ(%h$Y#!qr4f0zq0p@sR-#4Z$WvnL{QZvC1H*&cg|)38Y0rWB z@)8F5FV%D73dM_ufY#@qWT~a+dyt#HO zE&jg!Y2U(u+QG3^cW86#6Eu5dTXxey`cYsw9ExMQQ$*QcBa8FT8R9SFIVd0FWzu^d z2h}8|A&aJx;xDeQBI^UL-Wk4o2gV-Th?Qa)59R?0l;ybQ;c9| zSfjJorOEx%{6V)=vkL14xkj~0%Plgd9kv=AfC#O`%Wf|1T2TDgLk|Djvv`Fz{XE=r zKXDOsDy_i8VxpByFO_l9#%79PW=N}@IR$|WRMp4l*^Se{0Y!mg0B(_;ONZ-qXWjnE zDc|iH%~gjD>Sk{`5ML731~a(@mIf(#1+r5HE+O>8+t`*DHW689!oMd1odM{z!u36- zD_wOQ5;dBu8$r|$%vIG@X`rCdvPNfa?Wn2*JT{g}keDRu$Jut?&8^+$mtz}F_HFK( zr3Zubp_VOBglVXCN}?OMyai@UGRdG$h-#}TQ0(&8E2jQ;Kk_=u+O7pO;q8#pmmW7P zs@B&6C08_-vumpuV`{6Ixk2m$*apkY`(O0aflR{G;es>S0+z;kRfY;@pg^_XgZ)}LqL@I{rWh9m zPd!ZoEdwnb&F-zcmhoH7w9>7i#+1fS(ypbEHgZXL0T7O%LdW%Iotau&9(CFFopqgE zpAEk&;G}riT`6;-VX>n9*m(MxExz+fQ4VC4D`Ef1hxs3?RLja)PSR66-mD*Iy8WfF zqOqc<^Z6D;xk5bwlv_B%Ho(@=Y|?7J%K$QyClt=K^KTma@Ow@F+IrZsUAo_D-AbEk z2{upmz@k%C*O1pZ=;nGt1xJH{MDA5FhQ}3|0SRU$F#OA%MM~Sk*}t_P<*3mK+LnHI zP)Gyi%YOds?Q_n(9@46j2I}7w$z`{LR=YReNOH?uR}bzsPCvzmEnVk{hoy869WAd9 zjdq&Yb61T~QAPaMg8+<(?36vnUNEeTc*b;M{M=dfzZM1bf0oDF<0y4&;iEdl>^I+= zK72s0>?48gj+SJ~&*M*oFY=D9I% zhI9JX>{@QRDn?0=N1%(r4C69Tlq3x7?A_`H2hn{k*`{{9Xm)q$8Qgp;#MEhJzQPK# zp&Y^VzyW-p0pS{cTn1_@O|QKPBNzF&-@jo7*nWJh`x%2l{x)uLEth(&wG8ktMSmN| z|3m=z$7epN3BZf`-x>J&Urg6|-_ITdbV2)r#E@Z9uje@fx9uSw4_(q0RgOU?t_v>XIsSk`v?;!1JQk>}qbm&eIKUKc9sSQ10pY#{1aa%+{Uzf^%{+ zXufDgA!edN`M!~Pz-|&QAud%J*O@G(Pc2LSQb<$y9s^R|pq1#Z44?gVTh^J`*^&LQ z@Z&kty>P}rA^8PG0j7i^f0I9Xy>H;7QNl;4w|2an-Qo|Ub&G(vaV}63p##*A2>;u} z+l!x$b(v*prEJDk&2-$lPa9iu(hh%>wSGPXHS$H%0mRZ!cFwKy`S7A$ z+Qf}nQ3Xi)sTbYp!(>r$dCu%}-+1=yurdGEln(xsFG6ZUJ_WyG$mr-)cXS3ywvKME zf66in|s?o24Bt26f zQ?F%ATJ!QQKU9O|@bw?t1B{{gx@RUd`s-EzJeW;}f&T3?Q#%OsWC2|k{K-=6-IXl| zX&`c%q6y$%cGBH_dZEzcx3KX)#3b(vf8~Zh&$+g0jFV5SE9EgrO8BhHFOBlUfl?hV z9iX|nlXXinn6g*M&22w;Xo!$SqmMBWU0youQ9GPjIP6JIDH!ZHhE#Nd3856dJkxi7 zhJH^h+=+NK_g{k<9mviX;81Oj^En->(-zLhg%Qp`ro{Rg6m>SluuCJjM55UrgY(`8 z(KCl=*nm#_2STN1Fn-|X)|#uF*eQW9H=sT8fDY4#B<8(6IfC-zu~8NJWp*F$-n+|Z z7bA!dD!#80JZ}TC8KSLZ8sAE!?q4_^eY1PKXdiu9VvmI?kxJ(HbA`2<5bVx@=5ywW zZc7{4-~2$dEYH6JK%=|;_TkchVWZ}5;nbm#Q5D`5jj03Z83YRt39b>l1$TM5XK_hw zXnff`UTYX*9fa+rh2duXix;4f6Vks)bj0G}Qb}#0jG@uiNjpdyh;UGqH5=aweAOrD zWdMa8Z=PVUwao8drPGssenBbc&7Zk_P135&e7C%^7qj1pqu){CToHPJGva&er6JE} zl@=*tEf`D>=UNO54i1juLMMn0CK`$hCPgEdD-+`jg6uP@M+*@Yg(Z&;N^w%{{_@wfXz&FGJg9ppI=WzKgnGP7GEp*_IEi{H zoGM}|*UK)X#?aU+8-1~*R`i=#4PtU)maphCXr-$NKdb8QgFSbkJ?HXRoIYzOtHL0& z@Qt0t_Ly0|W74_3HZ8FDwlg|LXXtka{DA&Yie@;{-<-Y-!$&R5&H6`iBH_V~X*w`v zND42GH9IWr`l)P2sq;16)T!>m^6b2zL$L^DnUnUUC_bdxHa3}5KN=I~);DqgOB!#!nL*zep3T7l@gd3RVBa@!Oh09bwTs zDuTcV)-e;6Knq7bI%kft7eg$Fcp5zCE@%$i4Z9%gSTrJl(GBS)M@%D0R<@B5$gktx z@nha0y{PRlOvoGaV6*t6%H{2z*azmWN(ze$AB#YOujA7lpM15AmS)zt)-?wOq5r3394fZl^=MQw-c#b2grGtJ zx(uryDRkykB>IUM*&|uL(d^6&7Dc7U(5~*wTc1+xPBq@9`(PX=<(h+RGxk6E07T`N zHFXPeTL4`0%0x5ZJ9iR1tO%%<+iP^}TyTj2YfcokF0B0q*u8WlA~aSq;9TF{eF@6L z>b$Rb6LmM8Wg0h58Q06jo=?O`1n0LDUumeY>ONP_N1s_78*&qsm$=aU8)ID- z>+YxwD&>?-e~@-1G*?t@7#P4xjw7-H88TI>R&(X7VO#+m+m1?vJpkWG2wk0kV|Y}i zS{tu-h%GPvSFG=<>4#HSYgTWC0-sm>%lCapREYefM{Lx&r|fz21dLC&kRAPyFn)nJcmN_#R%XhVx? z^(gB1;E3HjKnk0W6i%IQQ@A5)+2?o-yP^>$H~qm~3>dON=5>290-7-Og(X?sL0 zI3JU1C@^n67lR-ZQ36V*iYOXJaij&elEI`gWM(XF zn0ru6wZ&v6uk9G4j;xfdG<}&mmI^!rSfImCn1Nj=R;e0*1SfOk(qh$D>wS|jTt=9( zU-E|~H$QfU_S@zRJ=nY{&$w_O6!(!8!I>COqRYUaA?g><{er}*CZboNH)tx5V@U@R z=0FU2^>;8<3~tbxM}esayf-Pu1a~?gY_nq>W;LylOuQR(bwyzOo`%C*Y3R-%f?M*u zt!4N6V1`ct;9TEXs+sEhc6$uv{T)pHD0^=O>u;}@)$$iylWTq*2~q`JhuJMJVj;oBD4fOY6<1!X}_RTGKo_CMI>}$+PGrG3D`p z`Q~XQvm#woY1lN(Mnd;3)-p%Vep8e@fp^A$*JBxck`GNjna_Mv^>b{32eTB?E=YE9 zLMX9~R$RGllv>hTY%Kxvw={3aRC_!Qh0v6WCmtP#HTa;NZVx3c18dykCU(g||K5au z|Jc@`HsFjz_K8IkrUEB8u~LvAmV(FuB3zhonUD#(Y+3B1v|j$}&&FQse~%zBY8IJ< z6kFppkdsD_eUZ25_&P<?MMQK$9lUZ7!-pPMmk`-wo${u~*R>MB&i7yGI zV1)=qU&_;&5IXms8v{AI>eFf8K!<=GGS3@QP;;6W;BhDk2qU=DO~-x*7Z+W0F|!XEn^BPeqX-Bp&Z6hNyA}qPyC%2XTG6>((u*XSNPU%- zNk{(-P}FgYuKo6nEcmR}u3Eh>rvl+)7aw78UL`)8QiJALip*)%C|nUNR#U}U>wSsY zP*x$LJC`+0g{osyYJoW8mk-daXvLV3v!oWN#&W}7gz5!dqw$4!PA8k_Iii4Nlw`mv zjW+;*ESCeC-@KHX0YL$3`Rjsd`^rUI=v|4fA0O@UCaer+*#LR8<`cFkhq83W++^2z z2CRJ7YJLWKZrPS|;e>H1xaNVrM4-Bc)rgSDO#es96fv}n$W9nYNfyXy7y^+Aw2we_ z8)GRM1RIdwj``u zbuBCmjs(lWS86J9vT7UXsRC86EHX^H<6uv2WXLKxDESCv^BaWkM!JM7LTVrlv55LG~ylxKuS=fusH*Id1oeE>AODJWisAlBc+eHFF8)o)2 zaz#1JLd>Y1z8cF??z~t8R|{qd(=kb!7^|d`?m-c+|) z4TW7iH0rtb6ko0TF;bO;7R^GAC1lO}Ie7U-HKu)2rjUL%x0W3&sNMJk2C4B`Jw6ng z4zfhuP->Qw!!p9yeKD2r{-#ZoK==65G}&`*ue6M;Bf6+oxwwB(;nG)gIJBCj71OWm z&VbWnkCNi1q!eNj2~CIDP&HK`5g^qjLi3cn#s97N8K2eW2=#cYU8bfhrRKJD!__Lg!K`CR3;J>cTag{&=By1r@bTd?_n1d6(dGV zomD;9Ggpz}FDa=R&%Db4x1J2MP&bcTX)evHK7;0(R7Be2wdZ|O9)lEYm+1GFX|$jQ z%*S%1m`IX|fVGLReJW6u*B6%(U%~=x%qkL{NWQA%7#&+}Az{nNq(kLaxP&+A20}?O zKr^T=#;iRhIi)ER)!(`8HCFaJlppXYDlO}}1L^u^-WnsR75a={mala_K~I!L7tx22 zr))aW;l1eGxN+Ju^G%}bT?7|DFYBZRy45YjY9#D^Q zONU(2gUVp7tmn3h6cq8_a{08qBTTI5jXdAg_~vh55F-4?#KQ7o~37)uA~yw+Ny=q{RBjzStQW zV+x7rrG{h)Vv(Tjx zF|xCmulnV*9Vn3O#lk z`aW@e1P(H+S_=2KilwPtF|qWvh93NzlcLc&x^L~~Bnn*-J50PwGFauAnCL=j~AYoLA%I6$(ww$u_P1)T!sF z^{(VZNe{{dh~kD>6tQTW{x%bkZ$p!?87Qq`#h?-ETZh4C&rQ>Pak7RaHo->aNHZx) z*zAeL4h_QvAopF9F_8-9(jzV9rx*GjHYTUk4`xcT)YSlM#GvRFizs+KW_>RKKW-Y- zT;~GEh9c8SC=IZWI>1Sp39DownYy;>Cq4Ft^5$awOzC+ep3V z_OongWSE-7u*9JUs|5H==j!~$$F`^BLptZIRXCh$xr(7|jCxmBiHO0Sc3F&%Y`tQ`Pv*|4C1Zl?^_D=siYR}E%t z9<|if0Vpb9eYk{&NckSuGz?|!WK!R2Q9bt|wtLb39_FI_8+$WcQuvG(SSfDxU~e%R z6F)zv>hPt5GFIq(Npc0Dy}jy*4{x%FC(gfOAvewY5;o0c z8xjVX^H63Jis_Ob{7pHPum#n0xN>D)NzGPY>pm%F5N5B-@@;mi<8z}zGa zE-of~g=~HAB59CG_Jd>FH0lF1B8S>wlYn9&Y8WSoR4pmdCxesqie2R;20R)*nx_sp zIxi`MOM#>@QFv;*#MqQHqGEz1272`pd=Bi#M7BZF0``<56jmP6F$ALNP}b#iV~wG4 zkYX7LO_TZwF800B3Kl{LIbCSK`0_UzdLz^9Jv=mmDuA-G9h5Q%Ifa`{MQ{`3_)sX_ zzlo>R;V1Hf=U;PB(_=-V$Q8G~fHx}4H8CV6h(J}kkyJa8GIcO&yBAfuQ5V}v%B02S z;HeA3MrXo66wcelc0YW;KOGUSr>6Jo6MdEb;F@WJwl7&oFAe-|rV;_|GjBO2J)K)} z66}GUgqkQdSBb347Y3wmPLfD28#HG%02=Z(n*h5f?JQ-17d&q-lkWU%rZmSTGS;Tp z()y_42Q-Cr#&^aB=q_cn5*OIt6o;lyf+N+M{CtWNT`KnakL&;)zSI1TiGq7k$*B!< zc%LwBW$o)yJN&2VGMg6NUB8(hQtINUmYY#Z`Uz8wt88w7_kTP78w-Fb=!9BCj?491 z-;_4DMBYGtnTOUpfb+xMMGZIJp?z+?i_+@)Z-C?I+=wlO8b5F(jYaH(yRO#=?OCVD zFF)uf@UU5h`4DJF0V1v=q~C9|pAn^;9fFuuVzl?lU>&K;Ch9xHA&WEc%2H*B03Oz+2^gm$nng^TE% zbq&=JIL0D!TMKiyX+0OL0Nr{?u`g67b=MXrwLl8fkrN z+R+tZ$7bIuhHa$oJ?@#cf~%y5ZtGSP7K28wSms)kGCAFg!7^)ID6Q49$}_bn717|# zym5uG%T9AS%;*mZejs2E2)%H+D(%lQW?(qp^!DM!MW+XuR8Wn(izkoh4BK$in21i zTw#R_NOf(eW5S~P-<0TqSe&N_p@1}`F4eRM=> zu}#w^h)NlX&W#rhb&nUHa-gYpg6tD*Z5??@+PBSb5V3c@=N&7f?T?jM$bP`07OJwD z7{Wz6r@Y46713@I&%iMFhlrcuf>d66PDPxOQVh2SFp)rL+~~pws%nUmu$0+}02uz} zsa>t6v$CvZ$eNE=P#qz)F}`dXP`f)~hN}xef*dpP8Pw$b?2JE(Cs(X@A`t(S>4aX+ zBPH&xL|_P$z6C~_H5zTKBqbpZPm?Hw9OY}OyhJfory~%aTHjoiDZy-QY$!}hhrR9^ z?;PZfIahI#q(JA{%5|MgYTCYp2^iHhBI$~! zk=!D2RK)CTSR!#a(WbLFxh36pPlxtdt0AooWjvYUEC%ve5mp({4 z9A!~7wzg|#?%<9*br;` z0Q?DB6qTZ%g|p5-AZS()h)`bjN5fJf2N@N;7jNyp0XUw1EQfEGgcQQ1>~<)7wKcRD zTaEI4+~5>v#X-!{S4PI2^r~$9zAY6?8WI2UJ6lr?QE)ZOn-S7%Uh_f(z8X=s5t21h zjAz!_x$Wnd3^LN+qUp0pl0y3)8gut-<;}x%vPS8LQ>&Y>xg~Yj8{c;rmiO9I(Xt?Z z(5va0mjOhy!Is1WYVNmu3v>(AJoK+zvzK|k{QI~}mBR^)-+Ri)iU2Yqs9t<)QLkl= zQih98q>@u%?WHYAvY=XLi<)1k9Er-fFfO4A53P=-E-8H=-eAf?r;LI>EuK!aqDqL? zPAbC0Qu%djR2*kUz>^U5NxlbM_Ymz%mNV0qL%G~t419l7xGDf^4T73uPO=FkR^j9x zc>mzOWD21xOFVWOO=}NxuahuX8k$5JDu!6{H5oPBQ+=wManT`&Z#Q%fv`eO|P*i^? zpF-S%5x_i|J{E&FNQ@trrxgDLPL_#ohsG(3&I*(${L0>&5nZzx0%VlC8d`-A3#JCM zf~IKVK>&;YVo^j4scYTFZ&mYRL1!X_5S~n@k(ibf?g1?yT>DcjH#OH@D)KWg-%;mi zd#BXFwuF>#Vhil2#ZR0%;kC#Xsl$h7pNONktS#wbr`O{bp8A7wVh~JjhhR*GixP^& z%Fn1WQy`fJnhVf`jfZPA1cXAHOo^_rfv+k}_k}+uL+6H!s}YQf#?4(uYn+^whIuIH zjfF8g^hxEkkQ1+?zZJ36?Y9Povm!Tr<}WPVoHjSkIT}dH9iDOpHrkZ?F0$a#vx-*) z!O3a|S)sBs0qqhB1SOJZpWc_iRW(A`Ze8H|Q9nu<9}mlMy4#o?g?BIhs?O$lKlcfG z+$wvLo|B=n(kQGMEY+%UCC(CE1NL$F7TjzT`r2Vo1mOQWingY*fC58jt(0Vgt^lMT zqjJTUtc9%*EdbcDvNudVi&B7u-r+WMtFiv-f=fOF-9oZvk%lVe8t`?x(OxhG+BqXD8g^fiH?F{i|$a|ohT1qM2A z&iqnsQg}y$zGjXUv7+Vl7n4lyl1$SB$YMeSg_%%OsU%m3J|s>OSzY1qjjnuQ#nQ9Z zwI{;sJ~3aH0Up4g-iYW6W@5`o%5kkARz~vbeU5Ob=OwX<=hq@~0VAgy@05{c&Iwk6 zB;^Q(l4j0kr^HgDqX%Nfhq5BkqR20iAeXe0bhlQNPg8-d4_7wm7o*eLMx+kZ`K?n_ z710&KYCxv!900rO6w+obG)$kIL`hndulp!dI~)f-*Z`$vl4WK2?kHlN+pbxR0D;sN z{=cZ`Mt|7w6o*NfrUxV-?AbdE6Q9jyvBU1C zZEVo(?7e?Jn4c41dF4iv!?O}|YyM^C$siOCJ-G6gE#4vF7ZoeTfxsr<#Po{Noau3m zBSqH_0Da#;Q=vg(r6yvU1u5x)JkUe%?={9k0vkOtV5KoUE6X(xy89uc7akTUquM}; z?Oi;JzT_^da#DTh-AamMWj%5uS{?rj(X3v*5NfgX*1ZPwV*#8~^QlRUN!V_6^-;6; zX)=aZNofM_oO+&j){4>o5-DxwIqf0R?{<^X6+jy}cQuLLtTW4nT|(_o2$_{FeBbd> zrWc^PbvexxwIX7rRD)qLn=6xoo8e7VmS>!rpM`$48VD!sT9xX6k&&I5KIH4pPD;bV z%1A~>M|=8deG%}-Z2U&8OCaH6k`xIcUV@&fOPo>n3QK|!mgkC7Tn!HwYOb`zy;*HPhJOy!El#qfcY zMIVtiTIv#UYLPerrh0Y+z|k8BEG?BgH`^#Ss3ST-;S*OAWQpu>tC<*9^xAKrE_Jpj zG6Kh}*ekTpF5ZjG!VhrWu`YVo|8g-s7Y%+|pRxOhw80Tv{f0*z4-;Q;NSpfAj_C0f zSu1M(s&oUZUK3vM@vWxlrgGoUE@qO#h@i@AbK}&n0!))(2?^ggpdXgt8>ctLyQW=W~tgC&I^~l8;i~Y9nK58UjjgX0480c%HHm-s*xdFj@@Jq{6KaE;>?q zMu%2~g~HRP_~JRm}4%^znkSxdygt z_XxOQd7iwtqsk)yixA6b-~()Mu>H}*{XDt+@i(Wqkx%lU0EMWi6@Zd6v|zwD6C4iB zA!PHBJtg<|q>DohLmaVP)^FgAaUbZ>3h8-mgqa$x`6X<@H8qW#^Kh+GZ<^PoMqOge z?l#1h+92OK%!&aynRv@%#c7IX$ZQ81wtCI8`zQSjrFm}K zQ)nSICaZOFE^bLt46#?w0QUHqRU5_kX`J z?zjW5aPHYF=A3Kov-h?!rZo8h)yv{_B<}vUOhMNbV(oK85=+1@{P1q+al|em@rT^wPf#N; zaYI9JPa|1ONxLG5*LCems5P9B> z(1@A2Ov)J!tVxI5O}gB`yWeVSLPFozAyEECW6z2A;B9C^tkrIwog;}toVtbb+NdQJ zy>LN1-GIo`5A6Uy&EsU(Y6Oc|z{geF?CpMrqu@O}kC?rVlj$p7N~6BD1$rpEMc-0* zVxJGk+iUO7NRuy_X#Ve<_z_~6ax&T8JLL$@?*wIII90647(Zgv%>zwqvFK7$L&_TW z5(~=i-ISm`i z;`-ktF$VRBcPqXLHHRFfg;m_)Yg4t*Q7X0*#BIq^_K3c`mxdB_5s}80s#mF84cVhG zHcsqQ`1qNY)YEZ2NScO$R7xn6GJYW(bzC?xrWbT7;zjTYmlri{I;9&y5VZYf>iJBE_Q6Go%!?Ew7wVc$=G0B1qy+}>ROyOL{yvl?^fLB(zd4oA#XR}mxz44HS? z7H>wPJW%$NamW`Fe5zJr@0ruumczt}!BOIVD!_Mijw-4CB1m(Y_s#Yf<1b4HrtzVp z4y-TXY-S~4uiTF-;?wgf5Y39cZW^G`fn@jdgxvOhF=q z?8L!pdaM1B+4^0>v#Ni|x&FeyG>;fE6OP2PDw?L6@L*3ea-AZsmf=_Wyy_b;{T<&K zYdq~g8f6*W*0;l2gdZZ&7LS6MHL1rX_DgG%KB7QzAJc(WgC?)-*$L%63u?=U zCEMu4Hl#eE3hej2f4SA&9^lzc6@&0&_eOpa$$s0!^A{_RteYZT|9)O$Ydsu>s?o&- zefsdQX2BMVGflMn`s(3~X;GOX#M^wma_pqx4&mbGVzh=p zr8R_-ZScb_^69RmevdZRy6ziYcHepRxZVL3>5Z-U3xyp)n|LhFq-WncGRn*J|5p%* zKxH~d3TGnXminlsY15S;4dbvW#uo2vGkEoQnKG**$(a6T>{aG1Fdl*B`u^t}fsB}w zZq7j&v;LK;WVe40BV!Nvjg!AEW~KxZOa0@mtB`GQmQim$Ltn4Zrbr6HTl?Zm;+7x; zO&In#RN&~z%hfxr_cD9VaRwzUMW%D);&ZA5YROAL3LlfxuTRN+q#FvP(y`-!&A$-K zUgI${gfz2FSBvczXmYJhe#Mjmh4-x_wMd(Ji8dq)e|fh0+2PtWgHGcezBilnZLt;)s;l;C_$G3r)nhr((PMrXxd;| z;iWI^_w#W=);wt4SuoexUO$*NguQ4~q*vl`NaQ6^Xu$s8Qb6WM3HkhlVVEBhM_39j z7n=&xoWT7%B)lpyJ@OpGcN{gHEj!+}eC)A?AB)vU>PIKklx0@K*iMm5$%x}yk!Ydj zJfgYc$>|j@1-LcMQ|?>01Rc3qtkfANR?6mfjFDS3(}>L$(!@Fu$cDQksKmQfztuod zV~8P9S;RZg=VcvL*SEYMQLo<8-W+C{>v^kl`86>KK9v?DUlAs;I}BG1Q=y%Jl+%xu z3WAlQU)cz0b6p@`D+jkm9ksF`cR%7!6xkounsJ`~x?0NNp{cNxkS*1zt98b-2|3)< z&M)~fruWKpZ=)fYn^8VM;ZIZ(5k8Xj_ln$LsD>#a$rd1F2kH2B!dgNFy1#9T32HI> z{c|NPWy@b#5lO56j56$wpxhqMQ9Y2bO1+~__g(cE5&@O8@l()hWry;O8N(?(Z<*um z+lHnDp)$8Ng0-(Er=bCwsEWCatmdELC#Ptj)n**G(1TYgbZBGt5i7{JLqt>!S*s-T z;JS+JwLiBP2K%p<@*oAcmsyDfm0dWS`F~%gjDe8!PvlTEhdZ5!nvf`J(<@s-)OODZ zUpZMcAY(>KNJRJ+bE|L$3g<0bnRUU?#QQ9G; zv(DaS#ZpZpc4j;+V$+@f0v=<5Ya*h}o0>@2aLTD@GT$T~>`>t@vGp>x%lzM|HZM|k zSggeed|+J(y1C~~ZVoD?cxB6Q3fp&Tn$?Gn@)BrJ)%tsuntaPwH@V|#)V_r{lDT;_ z-FaZs2Sf50b5c&6Ny$D-IfeB#vkbMNUFiG8NMB``-GCt{S|e%Bl4kSXezpaD(a+UN;!n2y}Fz zv@&pum1(d@gKaWq{<*y};y*vaU~`zmOOkHeK+pyKZ7mR~>9Gh!IrZfH-KooHcBCgz zOOmP*|_6e)CUPDw!Wbd=uWan%v!Puz-5u=~L{IU6wP16wY7{>+46z*e1rnuf`I>*fX$9 z!rAnVVhJH#RLDht0pa%6z*hN!Hx|t_{Yd+VUxP|o?>TDE41D(odJr<2=Od`o$dUwBGgXOO%x zCSCucdFaqYP3xt?9jX;9TBZoeHcZ>0C`LYbtIPP72+{S4Ku1gu+EM@Zer8 zOvCUvG?O%83fDVhGG)24F&IfX32BZ1B~{6~d-UNjHJ%34VMPp%$o$uwoJ8UZ;_Rw< z6M1B&w6Iu9MJS35KiEU1(h%}^Xa*Tg-9}qTz(SC4n@8+>@9`O4Q))PP-xy`{Fmi~N zpM~SyLvzLB@Vkd|GC7Aj#6|w|6m*Bqyz_B}gI!HDuEZD=Ro@$YCt_XUtF!mT`2%B_ z@L22#xGmI(7mNbo2|bf|zfi?pRKW=2SvIDZ<6Iu)%r)GEg$>2V$W2M*E9EycNNQ8O zz1hGH?&zW!`J?J5sn?*`@2{IaQmbsN;~|_!qnHIkABb=uV>$*Hfi8|?SLU)!rLc6x2Fs~ z3exqtof$XhAhP79FN46J2Tyz9ek!uHNa(BP5}q9}j>7Xl)H0SFstPaC#C0e?NbZ@3 zF^6|6g|u~(x+2EKC!BiXFhOq1u=1e|B?h6@ zc=Q&#vJ+#gp%aFv3cg$N!KGK-Ow_OEB-&_3e)B9%cWP7Uy z3gtp35^vL#lw&{T($n=c*?*Lmj!UF?t<_Rs5L)kE6KTC|G?Og}H2~o!J-+xVT9(#S zj*3B|G$!P)n_KvmaUCj=3{HHhpey%hKQ>ge%&$IC^MuLf~|Hl;izK7O8GY2 zpyy*ro4;skJTbnM!1I*mg4+W2GuJ8%ieXRqTyJ<}n7Ch_J7oDny+c$^2EDjimowL5 zgD9b&Dl*Yq5{mrgaRNqBf2EWooq0_fA^?qR zX(&1b+pdko`szwRZ6Dhi?G?n>5jyJB2W9PI!4C&IoJI0Ik)|oUskKFzq`-oP}40p%;NaDZL9bb)o>h} zWH@?OCuM3LiX~#V4r{4^#NPCf<$dc|lr<}=eRc-YRzPVVLVhx)$Zt4Wkp*B~8OpAf5SfhCMugS(AO+%(f6}zH9PrC>48&=L%O-H(X3#HUy$egRK8@h-RrHln z4uVBVsCKE!tNE51K{cy5=KnEHRz2#c`b~t3+W-Rk$3)T0^}oE~DJhzv1?G+WJ2JF? zIL!*=2GwIN{X(StQ?aq3M7TD45bCBf`d%M%zYkL*)Wb64v&>3F2?whTbFC;{qLw4; zat+OY>!+nOVbrt`8WR(>Eu7t0Q!kY0yQ9tt zeqVVzge!mWb|bs^4};q>S^CoYF2OwA)V5AhIJZ%qf4IrPw)ZS0RJ^Et_ehsL#^~!Q zZUN#axN^ws3~F@0SMJqUsjMZ0sb$AhaIw{R^|~(cFsb#m8G`Nht=`>qS8D1IO>8Ab z5oFoAbmP{mxh0rmHgHie@kGS#x|n*bi%)WP6@(&(ntulxu^)6*l%wWLPQ*kd0mS6&|C&L+a$x9>_nnLpJ!hCriLP{ke;V@B>0eqRZ3 zM;XUTC<$fZjq`J{!><#rIEX}nAVZpx;)Uy?E_XD7vvR)jP5hInW{FcgnJrYV0Yxzt z-hLMi$FlV+XC|n;t5#gS;B?ZO~bSq zk><$gD*l!d--G>BqK4Px60?rhGq-$1UaOwG^E5{P+suF`lp`b6ek6~fPL+t)?pBSu zZKNl**mJFhp)#(+8q=cOwxo_SRSa>9RsV%TKP;gpkrSB=>ASi{C`}+B?_;k?(rQgn zrb?kc`Ga0RZH!h%iHj~6N$eV%K~2;oBME7EY)Bhmq!!(m5oL5o&6)z?(AS{2BN=w^oOtH zDDMlyQq6OdL;CTNf(#KjYF6nWonswt&fohgBM@vvzXZOsuZOgR~7gxXh8XbPZo2h2{jMz22A9pQ6qBIv`cfxVlt0WMDRIk8R0`%a==Kso#cZ{??cW&8KGG z51NPdoD(}k2N#N-l4~60qi>T8;WX->KgDWE#rjJZ9Z))4?}S;4abCkB;dI8AkgxBP z6EJpAhgH+(T5=fS$o;TKKJaR_ixEP$!$6V?gj1^)LBOWGWAvT*lqYrR=iV!=jaVHf zL03xpBSR?~>^vz%S0;i(U;75B2nRD00e1VYgC5T~94;VY^>)bLy~rrf-Ak4_yy;uB z?%5kR;NHJ1pBfWd{&;_jmr(fzn_I$)c{$D$?&mL|gdmA4=GPErBRMf`CFUxQxE8_@ zpXj)|Ik0?H|FgZ$b8tb>*7omJ??X>Zy|%|>TD_Ve^BmKyE(ZS@af=L_WhvC*B~yaG z8ZCstMPyRII0+hai0+|z+^U6}eQ>&al{KH;gn+A6v$?7Sc1)ehR(yn53s$wi1SgH& zxueL5LTlniYW7>?VZ~NT2$~9R-BP34GJ4Fd|j2yFeB9m7+kf!8fIU!gPyoOjrb&0CHq$RUxir3W^ z6cD@8djk{SGClHUJuV&e?XRMX+<%~^XJq%}ygqu)<}duyw-zkwc+1jyWIpGZmF%p0 zwRq^(Y$j9Sp=ZNLrKBdG2(zxM`&d#7K`s_@mr42td5g7uept~dPMw}lBe`bl^ZjQx z+k2vI!D}ApnjN8q!}2YiBiqLINTl@f&S5HZ_2BcEQ?SP*}jNyRYfdp`BQ5BL^o>Eo?oNv>SfAw4~1 zTI0Zkjy$%&)uTeuqeUkp`wn4?*H-P8Php4~um`eX={x+I34FBJJlbwraxsjUqKz-S zDB5-`;!v##bpk{AwlfGH6eTT0t}=~t=;Af$qtqv?Z$4+h2EZ~p#FyD-AiC3HxESUj zs3L?(8=ImnWfv^};iV^1sR|(aJc9$Dlih|C5)2h0NzNX`5=`>GaG&4o_!Xz@nr<}zt}+K`bLU76dK}>`98)dz=Suyy!J!jX<5UvCd_H_)z<^yF;A8JS8b39> z4#?x(3{Y3}Pca@zyk1;6F}1DtXl|`~EI)j_&a7YdXZ9XSaO_{6j!u9q_uL8aQ~Th4 z=fC@|_@R3VENRYoE%u85)*o|YO5~5hOl2jvjZUV!*Ev2yiEID#;Fk}%`diUjmMf{l zBxs3y>99=pB8t<$;72z%7a^$}PhcaC2{dTFr0yFU5J?bKKQBbJqt1wEDXFx1-G2_( z2o|z?g|bCBj7sb47ve#N)KPTkmPB;10MWbvW%emt{C_>Ck2&C*>PD^fyOETVa)0(> zda7(w{J3tWxOhj%_g;kmulA;5iCw`@{Jr1F1sP%B=&$*2pO0F`q89~VyR@e#&a2By z^jx4zi+2J(E%FhrxxT{q8e*9SD`aC4s`HcS{kQJqcmbZ;DaSF{U-H!!uxMhUbr9%` zzK+fTL2Nj#tPupH#&5aV&oZRlMZ3&|3D*CzBNvTRR{t%hqzhkMmICYDV=Wp1y#MN0%?U`baI;y_g*p4DBx z$qxGYII&BW32_K?DyAkMqQcNLZ0CAv=zuI5V#6=AK0<#-u#Q>6BGe-LE>J{9N0xp${n92;5&y;e5GUjt zF!a}L*x!|R#D7E4mkbAGzT+mg4mPtMx}jejj;oMhSP|*kqlDe8xfNGF)+Q&6Ds<+j ze{QCqhFC;32Yv)K>o{eu7&#&-qteby3cUrE^gBrzhGlP-VAmsVowlt*`MH((!^ow@ zv+Z6Ro5QR5tNO*2d2ibnakA_-f>fS86_rVITMo~@ZFk6zi6l~uUAti!`CXA!pD-%Q zX~S_1;^4-&lCH;Fp91wq-|f?n3`N^vs>{8OsdK|YGJ*9DOClcGtt>d%vUiwkn3p3M z8EyMInYo`vh7kJRcJ4!OY&Zx1)j2mbE{m|^ex*4I#Yr(mH(7 zJmJm3f-$vC(KFAdk7INM#G85}0|-tK@UUsAu=H_#{EMr}Nu4R0tG?NtzdqJ0o^-Fx zDwJ>k(n29ARm7;tZpAfwQl-@l3||B1&cITXaw}cBC))FdWEQ z@hTgegUp0&3r#m0PHm#QwmLyx`EQaxJ@G|Y>axfPlH)e8D+y9#+JS?kG0&{C<7!z);qkh!W>U_SQ4>P#(|EMBMK1`nNhZL96i_uzGQc6Pea7fmAE*4TLK zL%)1c!1L&XyK-f{a`m&m^-z-JW;P=I+)U^^MZxE8GVAfL+4J%9(=mg=t;{{@YJ!IT zZBylalZL)q4~gdnkN3ti|EjgE$3hyo5T2m_KTlR5LUgT6uu~dJP1(E?(P8kS-|-kr_HS|Yy!EM@%8nJARh5ad|AX|_G`DUMn9q)odThTfM6j}lJq3lAjOW; zgf2xp_nX4Kxbtd=BLvt{cI1=ANJRpUoN-@r3{lKU|CV4R8?_ixdJILK$vP?OG{^$h zD%gCfIcXUw>9DYp`rLT^oylv2iCp-?6s>n`i9_kuK2~m{3k$$Q^`|d5wB5A0{L1XF zjV;r0%Joo+>Gma4s~sy5gERZ>eGIn%0$7_AH;E1D)t2r>>B+8Ny_VjK$>;a&JK?h6 zuXpL*&OzzjmXHO@7+9zRZOT=Dg>kE ziALxm2i@!GJj*A8NZ9>i_uBjT`ptzaHTCl9O^?BYq?(!dG0@%GssC^X0Yo(Di-bC?v_#N$~MCvhd@c!t>+G^COM$?QfrlKA(rh9ieNS zl@s}u3;D{nyZut|CN6v-zH;rg;?f@M5`6=Hc_rHCVI%9Nn+7;})N`97%Pmnd{n)jS zU*GF;`o-DLYa~x=wolu(ZG8dh|2!n!z>`jU;^-$<> zz17h;=N$qY$?sgdbXFtzJ>7(2k9ZK4&l2lBL9ly1E*R+PwVn+ypt}fQ zW82z!9@KjuyhOk7F7xukHc$KGq3v_#@oi>3`03blhd(O=&n-i4&H3bT<>}DirW5aI zt5gV>*5|J=$^D-`p(};w3x(^mz$llNjn~Is#~v%^nb$41!@xe3UavfF2MwNX`h=fO zfn6(~_AB44-~0US^ZxsI?DOoga=#pWbM{YOFo~#*?j1Mq+t~JQ9~aucGxlW9yn{Q= zuY$;&L12l5J$mDftGpDMw1mKc(*mbIb&~H4IYBD{W=~4;D8X`+Nw6z~qzhhC^Vs|7 zi*=2ma<*|O`tVfTQD4RCh z;G)@}NAuF;fliS3(M*Nyz;MFSa(t14oAJHQ!b*3d+o-N(;1Jk@g7NjkgZ2GX4Ft=$ zL~_UE5^f78+6kjpCAhQ7jWE~ezB3~LH7$M-7@!Z-`VuT8%a;`{Uh*WjW5GffgY6H4 z=wE>8u}A%~_&Z*V*AA?9e@gOvTKE*iLoIy0r}8{O~aP{80HY zQ0Y1A;x!v^wDtRD*5`3{<#B1{vH!i$8S&Mnna`D(@HvO@Ik7Y6%a$sq5B8=Hiy=wv zlSA02Z;vkuFbiPyZIBIv{2dWsBeSrJg$Z#osKKsgvCRB)db`z=;d&>I9|i^86w+0V5rIgGjn&c|K!7Z>==;+>2>**zy4mvogRa6XIf@{&gFrPJ#hO>ti56{n0#yJPm5p^VF(eDHn+2y_LFMeaI zLX~?2VY8>o^8DiUnfkWNWpq;`BPbRzVV?DJTdxCn;hSGs55Kb>QiU#i$44Ju^6~|b zgy6N0@X619p?jaaL`T-b8=w1npZfrkht=WXVaEjuU*U%?;fKbo`^KHdzJHQ#Cx~O_ zUrXE5k@raoPc=%CzE4r-$;QSqhvQXDnH1+UvRsS@>I=kzjUMpGa+(`rYImzi4=Jg% zR42$4X(h0x?nenEEO+?)dt=K#Z4W8$#x{V;;u?(bKF!t6L)NEd#brx(q!x;S(YdLb zidy5J9e=72!5|-3UxGjDC_qJwhwZd_-7)KaQQ>h)q4oLk_&NIdCK}I+F|F$bGoDt_ zpH`8{b|7LNB3EuAUE0nwvK}^Rvbx%quiLJ@R-s8;M>dHI{_h?e>sn(AwN>Xj@5T&a zO<9fU7LOZO7QAimX?eA5Yz8h9^bF>KN8EKQSO&K?9Gp^ENRkHow)XAa9vVC^?HkXs zDsg-sf!`lw8ZQbBhNIiDhzxFZZf_7C&FsL9{I1d>6j^u3_MHsgiX!6)qH0zWVaR3%fl5HhBsc!tgwc%K}Kd za+^I(E&O~t+J2ySe77O|v=O|}gZ=+1Ko4j{LUYcEer4TaQ!=aOx&^Ps);gf7y!0w6 z2dAAH+ZbnOw?K#rjU+{%Z<)k^hy|l+e6VzT-~}H>x2Ljz7t8s^;Mq;JuHeXQ($*cl zfkEQnKViypereL?`C^n|wUl_=)6b?2Tprn(aq?1yL)K2}7molyILeQg$ zBwI|WYkI1j2ndyGk|e1!mMPv5R%s>{0_^GZFMyy11&~Jv#{i4a-FWl|5L4@0z5k?Q zfAmAY!PAz(({A+BZn#h)UeL;I#>#E^%3V307Y@w{yw3@|!P8mN^QOYn=9~6&K&oI% zvZ0MU+7Y2ARkhKfqjk(4JK8o00W4fe7z%QKI7w_paqqxA4inRfx zkaaIIR}n?gnAMEcP`rNJtyiDwoA)2TH#L=dn7UY1WMt0m+!G|ur4*KymXwxyWElu6 zJo37ptU`r3BKVX3>orH7z$69(feD2$PWcw80~U4_f>Z`3fOof+mUq6UtdMy5-C0s5+9uv2)r-GOA_*KBNY+?eYHl zc{5HSmD56bw+Y9$Kg=<9uEy+mx%IxdFmpi7F906>38*m#HNyYgz0@xZF_|xy4yMNcf@$< z$OMRe)@y$j*ZegCh79-YJ}z`t1#)gg+3_O0w_|QN>EY9L{^ypti$+ZbC-P-$Tsl8w z3f!)Tvy4c4fxBi*L@h%Pnr_+h@b0!p|-88#SXsI#otJ^()?K zUe=f}z+NveImCD4kcGN-Vt&4>PzdGJs}N@CY@K|pgCB8dDW<36*?!?GA`RuLLBvDl z!{B2;WQ)Ke;0Dp~LLiWJ*ng$ah^;X}$UrKo3G6bVmtDJobnNu%f@CeH0(MjeTQMdv zX;Ih5%Vi0@zT|_U!+HJo3qJQ}&9j|E6Skg_4E}nXr6#9?8J6}3htu^QFakHzd-u(6 z??(jAhhA`qi-6N+k+&w+iQ9JhbM=+xhpQg^(j~)zcsi{x$3&A1rx~qw0M>P7luG=~4+v+mk(WXy*DLPz7OSVJ+R z+GhHIY4I{lH{aSc$&3AS;W1r92hhZ%4&j2|xbNZ_cq*Ck8F=4r{emY+|1Lv@00m*U z>IYw;Ra5YxX1Ucv`09_bMM4X3ZyyMRZ|~pSj`}>s*_3P7Szh+w87Rt3E{&xF8~6aj zI;CD7%-YVBY2B8>L9|@WTM<-NHqPpMWaLXxVg>1x>oz)o2m!`F>kLHZSUBGN9mmqN zn>+Lq);CT;=r)pPS;{KT4;mhgfISb`iQpIq;uWJNtJz38pX2uDg`e`??Dp&3@tqtm z&W2KyIX&hDF_i~FOOt$n>R6+K1@_ZUsEkz>LsWc+Kox6i-D-sxJTVL&@z-HUVsOs12m2f1lIFrq|x9$EYZRRInylIY6fw;mj z$uO3-n_Qpgrxa`c&wqZ{w%wX;52duaU$W!vK&*yPL^Xh743AOs1VqVCi%IGLJp<3v zp1~xRQA2SE*Ztzc39Oxgfq?>rEPXAllzd)z2)$-eAc&i?0Yr+Q>89VL*sNw?L)%#xtZCujhRb= z3SiO0r@6V-Gz~RAwPrUp+{%h|HMN!!ZPml=D;s@1rV{HTRmW!SKR@jjC6YS$c!?V} zB*$}jgMm?SkO71Qh88)4yveldMC7AG5{)ThGwkq<7Jj<suV7FG?JLlVWT&4 z&&KKDw2wvehv9m6=>6Xs1#XYy@$Y@`B%XG&dM+jyC9Ycu8m2!dYZEgTT)-h%ZaW#G z0dOWiE8|&vj~aI}ua`ZJeWB6%KUV!WD~i8~Ww?K5SAN@R-|;ih8hK&6LS19Hv{Vud zqXbJ$ZBgy7GrYdqldLL3XhoCupK0Y2><@LiNgsHl7V!{{3aTzF|3O_zsC~zj8t=zD zSmakz7BfvwR@h!6KR7{-^n$Dvwwft3j^gX}j4RCYZYn9E_fZRjQYny=mMx2oAmA{a zt2SxBUpjVBi#(`oe>xu(KK@f>tgXYO^Yd>Ikxjp$3row1f8!|#Y>T!jUF}-y1!n+} z$%|P32Ev%d@1I!749+yj;^Z7wbrEcPc#nc6`F{SezAm&zZcZ)qR%;~8RK46JdOzS0 zW#`jmHGV?CIH0dXxv6Cwvj7AM>gz(xQhtA_~ zRp0k$Axj$rHMPr&NrjzCAy_kzrL($@@=ZGOshq!A$?UX=Jx_jBwww(BUP_%&cUf|p zUGE8>oZmdGU%8s`=AaV}W~clwN44e_8z|>H2EN50?V4}cK(f$pNZUYo-!n0{3{IT= zY@^|^g4BoklZ3vn6{?W|=B_%u`QEPQB~mHZ0XAFTF-ZRBSpYs@%HqjwK$36}aW{Od zI>Tt9G3E~4nC~Nq`6d>H%GCzKqI7Iqody$`EUej`_NQc-8z~ho=ZvET_1(4-K%Q}w zmmh(|tNpv;^Xw%)xILV3K!x$sD%SIHfrZ0PduyhKwMl%H=vn9h{1=(Zaiv^SR3>GPU z$2Sh@J(#T=wK75K2YCMs3Ve!kdBB+P=x@>s@1$t+fzLZZ1e>+}21uT+9yWq5{4h4= z(BTV7P!{%*Jpa6~nEcN)NtdW*#TQ%hlA`Py?vcLE!1`?IZdJ)83@9fsszwcq08$3n z<|^`R7?ey|FL2gR5zT+Zd;65`IKoEv>V@6(Uq~q3dEO$Zm#S#O_HnhpGw;Es zL*v|_DAP(BamllJhnEux1*q<&%W%{p}3d2&+0E=BqyJ1m7uKnCnUgAp`3uGEM z2vdUwyN?;B?kDMa8kPFgR*dC9rIgn%zun7=V=ptn0@!>H*vCOln>#)!38c^?U3)5! zCJVOhdS-~=+Xw{RzkHxS-z&_DA`xEho*0u;R#S62ALdEZoT+{{3lQ%aSfRC`%>6aL zGcmvOUt6DhopbHLKR{u<=)ln|VIT-2iFi^!Pn4*Ya^O%=(`~(S^s;Gxykcn0Oy{yn zbVHdN%MtguKFXK{;|Ak&9p;+F&n$sp^|<=`jpF@`#Z0Y*N*9#y^Mj+f>_7VP#bfK# zlqHy0PeSFxFP6xQ$B;^0wC{UO{kj=QuTAUwi#_A&!`q##tS%&ULQdLdwpfn0E=Rg5 zRH$n8Zv@>pOLXKmMIe4KGL#z)$Gw_SEd|POw($@M)-?OlQQi%dF2VxJSK&*p8NLJaV`^ z+4&#^+3}ku>$CDU&^6|ohLZ*I=Sw0l_ghOQJtF@y7&Epl`NXSR75KiL8ruk4d zS>Sbl6-?sodbR_iFzPq6=}_`F=ejVz>gsAnSz2p_m+*M&{P$)%&349OkM~1UV5Ls| zQLHTU9^fDOk8VM#8X7CW8i0t(o=v*K{85Rx@wH!3RvPQifk{YzSY(t)1-`9#5S2T1 zLUohCHNYnLge=;%yWo3^CWi#PZMuR`LFDsrzWYzo(7m=aaAv;!_s#A*?p1x+o2avz zov}zvU!1s4SN<-pIKcoTOo`T2w zWp@my=KjSu|3Q&|LLx>v_tSKg0GstZCw7hYVq1{@UMxy^zjs}^a%2_}pTj1w*wCT% z0(b3VgT3)=g&yE*_j9#hLt2dvCZHl15-%WxvTZpH%Gm-m8Z({b@d)`_>u_oL~5!CcIyA(`#I>~mbfMje{J|GNT#|$dvS@dvd;wrj za=RW5US;c5YK}=?)a!HYxVt{x;z%)&%>|k}pM-8!p|Ls6(^DXRe4eWYNZ|#LXglpc z1oJPNQ6>aP00+b?H(Tk_n#bK`X3J3EkU+y=J{qR2cqJj?59&O&V!T>B7=?^?0h_79xyKgLV2_051mTE#qPNA{na5KOCvQi zC>($vo0gsN~^E29ymsAB!3-y?XqYOiVC?>v=@P% z^>k4f4*nY4rIeNU|LeRkyr~^It#@ENxUvV)&0@@0U$?mvL29Q1XG{( zr;33ES^%j6;^6p0{Qn03I@Befikx~TwC_s+>olM4x*iHZmz&OdA`k5Qg4tJH1}wLk zSA;ca>-JKie*aV3-3aFWkU^TIm}SmY8TSRD5&@arYBHEuuHTyK_%x$q8~H}?4%8{E zjQjaX`Z6wLt+8uh_K5xX%5j8m=cEgL)XHn0_7iLs?NPHLQFn;Ws6)>HWWZ>-v z$2HgJxC43wIzK=2nGWGE{N$khFZ2H^XN`Wnux=2jLHoNwh4hiy6iC0Tj*E8yM+JgrQE~Alf zQ1I`9k(il#g0dotkgG&Gj+(ChaH<&8lNmOxmoxmwEsh|J($C)_g92sq1)-T;{!MMO1RV1G^awO1*cNYPrMfK3 z`)&{L^^4pB6nQ>>IsZG3Mj=yR?$hx-5b!Drg46l3c-R|6HVrwBJ0oD8kA%m1UbXNP zjDpj+7bVU}Hh@Yt5ru$#6JQ8eZTngfQIqpw>MT7jjH6ct@Q7QvbW?I4J7MprG|;*H zUv>?bnaEz$&yrmiDyY}4sgiWrf&bLYwN2E~{+-zWh9OKzQaBVs&i*_OV0%mbZAf@x zLuFHdzvr_RL&>Zk{nvKEh$26xe19L!P4~l~-Mi7U2Mou$`u$+_1ido=sGy8E2gHh~ z*Zgg_+i7+2&%-<`7l#WjK=aYL4C8amT~_)$9so+9ZGFN5T@+j{$@#!P0OXqjcS3gx&hw0B)@#~q3N5x+ZfTVEa&Qvki!xqD!nct$ZdyNiqYj>kK3`O~w~xCY`#^b7w&R z9Rjs>reWL{#q~{^kjs)2bUeOCy`?XohY|q-5ZfrGYkOS~7@hrg9^o%e*G#c~Ufi{hgpAEUjVlVI@j05l6dVh(Swdd=K984slZCT3 z>RMVL-};A~l`s-n41~VyDh^gN4jM)L<{?W10@@mMhCr!j3sMv)lgTAX5bHZ584EyW z5v%cOCsPU~ytL*jxqkb$UN0|cW}zcj)b zU22Vbkm6nsX9)`rzTWzp{*~E80Sx4TkME=4PQ7mRr_hfYFC_-woG}`k0sjyCjoy?) zBQSa(`nVRu5f2yYeL%oCETDrRld@Nm(V_yLi3vg`qti#2SS2miM-2;Fgr-|qhPcmJ zQ?4GDYBYLD6R{B>Q<7}ffY#6`*S^epexO;-jKpf83-5=We=oHR@=2j)6iz<7%eJ_D*BBKS! z+Z=}2_s&O)7UqqhwA`wrCWx86;rpHGadZo+u_z)Q=9=L+z>$*CZv;mG&VrU6rH08% zr{bU2CZ;1Dnk0W-JR6>&j9SUBH@=)Ys_w(@p|XWE;DM1i*zUVXo?=AT=rk~8e|Mn^ zr;Fb5Z2~+1m|m1)Ka!MjPW$^I5j0{USYM>x*6Q6sma|)9@Ry+Nac8aV=-cDkcfKBZ?wOR|gys~lizMs+Gud0XA&Db)^<)3%s zv**9*yzx8{#jaWF3N}-81-kJ?4`2_z)p!`-p(ay_1*SN6X}{NN+9HDMP0)W>1SEkb zcrnI^%!Z=QF&kjjN4deU?+6Wt;tKfrdyT^i>^#UmNJWZSXGeN3ZFE)&KSHF*blY)qassc_S)b%*5n@cjz9nEmxF9wa_0dE6{ zF`z0m8P%JIXUcVmK5w`F3V;VPrw=G!UeHiR)=P3H|0ad6{RHw2Pz>DXrKahz>fBs% z8V_Pl^D$79*fi}Gq{qWSu(Vz%gZ$VTN{K6l`fuUuAieLI@LR}SZ>ZxTQK~q%)SfOO zIo@3@R4JU;BwH0%FmL-~45Q`*)*(zc>}BJ8GN(ms=p1M#rkygwp%Eu93~Z(Y=oAEP zc5a#bc&UCH1oBxqRGi{^xfV2x9$8?_aAGYKbG~8~`cX9g6+p z0>H>%Ymn3HBYadjDA0FI@ozlCSTca85EJ?A@eJ!5W6Q}oxhTP3(9jp$bkLrmnYXMrj(~TX%R*n zB$u?%TA#WapNs83?85J7DVQyp1M7?7V(>^(OKWL1~^gk z#Bkv!m=o(%oVr{5X}y5LgwDI_>#4e0JNZGU#q|_4LVV^e3QY{=xj<_b^heAL{wsu} z)TM-@>%EEX#kUrd`34Yb5b6+WL|PXWLR9yXBl0OC8sB&#+!bTy{eLu_byU>d*Tsh( zdgv7C4nexRyFo%gq@+Q*Q9@9njJUOHi6q2%Y@M3-7)PkZl< zvs=%p&?~nTUNg^J?xmb)L(+;oZVLun=9gqGJyR&n#-3aD+}so?an(**V- zXceCk4S^nL0i21sy-K7VPf+oPR74ItZ)#%ZQ1HcRmwe5uFZ^2Xz5;Pe)R`XMx1I`$ z4iAvE!K`-!Mk4*K)+r&xgwfF89I!i6ofyl<{~>QES&~I$7tD*IyH!GqzBWCPn4tc@ z5LEc{`nNMeECutjr-2Znqa4Odq6Vm~s&U96pm*EDcc zm5sTMgsn1nGO8~@r9&YQZd5W^7_Bra6j-8>P&#QSj2a>hrI#AN;EpY9I|?_RXY7w3 z4>hdj__-Jbi{b+W;n2Hqh<9+oK+nmM9e7VXWNL!g;|1>~X@4Si#x z)YMea>+^n6XCy=vwX`<5AnvCX6&0W*bFnsgpcBB=$U_MQBR05VTk7j40?h{|5DN6) z@1V`j8MuCP{EZrnvymAx28uLz83RGv1Ny%_QE$iZ8)jgpe7FdCxBwEAWR$lY zqH|EEYrIVh4~=5F)gef#=x+3qjr}W^cmbMOhY=L*NIe-<_xG@bEb8+!5%#qxXn|@8 zLo4^nb;cW7S!nm0-4qsCXb&1N(XsO($f0d*L|Lp#$_-4d=)p3ae!4HcrW$s%aahMp z8!4E+D|4wrE%SBPEC-&_>6C&cJj&_ejC88l5A?0ZsVOU3s3fW@73ZCr+pqC-L`xP? zw3{{IYPn~g-r<7ejs$EQGA!LiS}1&6p}#Q`$x*z+FTPu8=c)Y!2Uufx^|*Ou$Gy>W zfGIs=3*7&pZ2{P--Pitk6CmK^%S7PBgPysipC;f%OFuerMb;7Y{%GOn%!=M|bPV9g`rGkPFE8p(37ruMG9?uaxk%cACm8amv4p+%U zfyiQ()bJ@`YGX~A8{@Gux`Rq5tqsA;hPY`I5KF^rf00(N7yRPW@|Qy@!_|GnbZcPjDy^*U=7k(wXAJ^bJZI@%=;%g>Hma&u-*|i)1+MvXXxB zJ#}m#`&gfdD%2`c+gc|-ju1Hxxslbji&Z)7Jpm+P#D@-L8I{Z|v=`F{6)+h+Ez4%Y z(oiH#)Xhjfx<``-3npHF-jMZ8C}L+!3bc$_+vtDOK|<zT$!s)X4Y4WnvuR9fVT?hf=HD-R!icyg8RT0r&Qj6J zO)V4nEZfdssKr1a{i&<#RIAPX=CLXA?638)OCzJtLq%|dB;|v+(LhP~^TqjN%m%RV zaMu<+zJado7TDIW!8jMvMlS5OY?l}G(%KK`Dw-Kj=vc(6LxO6SW1s(%iGn%f?tvbY zSeCdw&$FisVZu`j2j5--LFNGXpL}!c6KXtMJUl`lkAeFGHdx%t8*iQ|kj>MIghCq?~+?N7aP{)ndDn}Fb)YhqwpY3$G ze{@B{RU`_}?(5RB+{T5-%D@r|dc%KYjv4UUN*B;57}Y%)G*$L9QEz45IlK$7Ijmf5 zp)HlueQkug46G<_FA~Q!@HQB6zh#_Vbv&-G@z0S zDihQ|9jpi96~F7^!q`1vd8+L0Iztg7>Dx3!Q3Rv@i7ACAn9yA^uu3#O3fkJ*Ue#!6 z+^*nKt6XYPLf`D~vHCK_Xvd zceb9!_D$qFJY}Pb{F605DZ{aFe7}Qu_bR5Lq2|z_ePK4!po0ko7l+Np2B(fDkuAT4 z;@Sp(F6}4b##MgOqBMm}@W(pt2}QON3X)K6djAl!(Q_Q`=wM@m6EvL;4A54nndm3# zBiEXH>v!|&vdX^JuPz&NHREN z?+o*D@*f^&&Ad3}$|p^Dj&E#nO4O~Xe3^F{M1tao6yK7wt(v>TPiOjw-s3&vJI_T( zKJRl(AIMge7EI%>s9M-A^k7jXRou+Y9?aH}3hZ85U50 zKgk*u2m-PAV_6bV4SnygHmN9$3=IVxCK!#{TtN4&xw;7urR*TOXc6$XZO3_T-sF-K za|Z;&BOnHD9;wH0{=KG#a9VcWB1X@9b{oyUh)vmrz|)^VOIGJ#Jvyk5i++u;?7D%UnZVa4tTZ*5xlf zs!VLgn122pW&H`wWBo)c_JnYGe?G>=vziH0K7iLzc=rC~d}mDBXx&%8UYo)z`-fGu z{16Ee3gK?Ez;!{s?v}{p)#4Ki5xw5YlTd;r*CDr z(orZd7#NWM*ps(Y%RjCt=QAD4Abx2U7K>!t#YwBKkc3v$k7}>-=n)Lh4Wk+5c|={9 z9t7)o1evG;Qlz$y7n&47d&_(DVz!=8mDKlFvPCJo@G^l4zV>gM)EML#2au+)-_oqm zom{aVB)sLnKE$-tjilz?T11Canxva4n%nr8=1x=R9F0db=>*AwFbpWhO7kqi0femL z%0p70LZ;~b92$XRi_lwW+^Jdkv8NTE@!zuhOn?AN#!B3sP2zH4;E+FYTkfJh$eOsO zi9P?Sl#`7*iqwUGhmt*Do~BJMcfd*izUEG}W>9Ik@ejO+&hrhtLc!qmKQ&5jAQy7U zF0ci5!l@~d9HLzTxf`Zxjl#*_psp&TNQdO77JF#_itg;Rjx`rF zJ^A_yBfP5R#G9<2qe+TB3BO~p7YFars;aou^iL;Ph881(gC-^|1{PMSMs?uti_xh9 z^@BrPSI&iTsj1`g6!HBg;)>S1gf@}iSU*EOi?)=7Z$;_1$mF$qrQ2u;4S%C0YCnW; z#Xk;}-iz)mTBV&<&+uo5W|jQntcP7xU5vONUh0ydY%0d2wn*97!Z@OgMfwJ(Di?xO zK4!#n*_bkBmi-$TU;X3S++^Y3|M%;_KvT%|-AA!`gWEhRK{8L9Z`QW3IO3M4uEUNa#+*I0TzS;)9StO^sdAVvBfpFL|JHCgzq$py?EfB(Tp=@56XY<(p9 zpo9!^NLWCnOY8hWzjMd32Ii56= zV6$zcy-+EAnaJD%x9-tSt28KLpVNFoj`D3DrQB?K#X;LL9R7A7!ErE~cr)72cWNy8 zS?d)Smn?-9Y0NhVaqf@_c?xhAGF7cj3`f}6jTIQcOK-q$O%iWxB2vj}w-Imbho+ID z!#4hH4HR@6Ov-n99Gcc1GGd42c(U;H-x^W_SkoE-h~;pK4ZWbyaEDqg%ii8d6*BsJm;ZzwK)d(;i;ul_!R;_S*O-VSGxiuorZmb)aP#yvt&hh9^P6Rja^?}y^FitlE{xWk+oc4^EYX&g z{*J9T+-|~Wkim?PITQO~!gYKhkjwgPA@spJ*FPy`ywBYQ(5>gq22tDN={9bJD!m+iJX^%U#NmJets$Gn*%KzP+X*rx=@(EH!XOm0bN$idne`4lq3zH zC4kA@WKIJ%a)xBkb)8l3iFwIIyowPpo3`JD{r_U|iON zs(t7=M51L^OC20%j8vUtT&PF0r&3Hd<^N)8Z3Yd^%~HAiNG6jkSL~%I_xx4yf_!wB z9}O;MTd$6&KWQ5+31w34h?#FDe~cq*-g0+Q|M2W<-ZOoz0xBn$O}^z z;Sf67<`75X>vq~@4$nw!w+#`W4G{zp-N$MD&7NMSM4s$JqVa-;m%}%>Ex~hX=o4f} zR|^bakWG(ztQTqs`Cx&_exZtV!6I*Qlyc z9~+gC{k^haRGW!J3vN4Qb1aiLd`TMC^(ISRjtz!A<9n`WRM3E0mS(0nf=r1qq^nBh z?wQvrz~U0DR^t(neNAD08SVNrVQFi}w zcLeUa2*kL>gJ}l==POGPp2-J<1Q2cb9h3f|%@Wgv+X|v$$b=HYP*IKU(Jb_7RQ@2z z2z(Hq?qP0EeVM}1ZSMG{ZJx1&mimH2``sxfWtf2hAG1oj>ixUn^Un#Z#%>(Ww-`U1 zc2P&0iDxWv64&3?+TbLFI(1$ip|@vYhBZstyQ-cs6iT0R4o$yB)!xyJ36C(dQ#ty< zSw}3paZ*A|jyA|@%Sc-EiM-L8pF6cB@`gF>`LS@!H%yzp`r_Qp)}lAC9;DwZ?-}+S zEx!3SDv+nv=W!Te`8mUgg`sLth!x5Ta+lAnB9v?N>2%UFN^{sok|{MRWb=S2RCn29 zyn0naERF}G(_M$(@WVD{1z!y}iJq04d&$qf9*zX0%cxv2T47(^pWmz^pUPlNUUx< z=E?>E(?dG%#;<$`-t2}Y2o7YBijetudQW6fqVPVq7|WnMx_?p^_IufQS>ItL3G)8J zjEpr$ms@EwhcmG?hJOA?JA^ipCwj*fgY0fG1CC8^#E>U{8J-~s%2lWpzdA7@%9vPL z*y?lW!b+9{ppJAi50#pKl{WA+=cP^wqX5n-3j=Uo62uf><1Jub#Nh&Z)gM4_{cYq! zD+6K+LM!CA08s#7qo0`Mh$8rA5Ju&YvpH=P*^QUO6DhVE&3%igM8i(D2-OPx0}Y>S zcqeAzRT=#2HPiqbeo4cjCLvutV=*Bi;IGSLxi&WS-oD^=Dt~9scCWy2dE;NGznol7 z7eDW)(<}au)bs|#IzOD#Z%?FVBVrxjSF&Qs_NKx=CGxc-b335e=fGCFEf@v}o-&SM zB4J8hrBd6NdPzW#tls)|Dvv( zEeqna;Zb!lr-mV!GoI*`rf%}Aqua-~sS`)~u<8IvhpKQ0D%;;-N8zdJH< zq>!aF`^5J|%08mtxt$6e`P!W>EOQLI_H4%ftTv{*ZIzg;Z+)PSRVHY}4j?kXVdwPX zQb)ih0Hnzb5&;aD@t0SbF}6TY%L_PY*}KR{s;}oG`b-_`2%46k209c89F0|lg@x_y z3w<6yr52fED4Yby8L-)?C_exK`~LRIuUe}csM8->j)5us@oUypjGxC4Xo)yM{|#|F z8&{$-b@^3CS1W)}fD$0B^|FoAO1FE8ygy}%Xjpt6k#;Ni$nL5qv|ne`GcTh|-0@|h zgrjzR(%+37i{)#@@3AyMOA3S$>?NecnkqMHOuY1R=Vts%lJ}dbWu)~-%6gR+>1Gd~ z`j558`$Tl|KZJGyaFX$ZDrn$3h3}r@Gd^a9;`q9 z-Dr}YGvi}eF(oou7L8u^<+Tmg{F;+GQ|RU2Y4&Ui6-8 zSErj&0AahTROeC$@Cm>T-hThP9kQDVSeU6XLI6|)G_w;bLCn)ZnCmKgDj8s*EHv<; z)M(qn!90hZUe-lPwU?r%J)IMCqa$Tn7$iSm%?FojHBqKdTTU(st3aS z&AZ9BNIGhs;|Wlm;)J6Y&_;E%`9_`zy)Z$en-d)D1xRz$1a`J+Gp`kAK@9H(dtDVZ zC)1jrj-H`xn{#vv(hH;*Zm;g@2N&94BW>r2> zJ29a2ya3{0;lIr!cY|1|0E|Gf9eB}+X(f!qs0M^7IDYQmobLh}{e>I=hjF-k_I@SK zW+5;L+zjpZ=zo5LQT-jpFdhoU0)aS|lVHHjp1ldTK_NK^r`Lzfipu!@md{8DCLFy}^?Im^a=Yoi zi)%gXXxxf!s=`A$uOWvzy56tt%S)`+2q-~m9lJ?8DR)@7g8+-D~e`lXe&JwQb?LfxU(KT<)C|`FCFoj9<>zNl_r5D946h)`}k7; zq&Bsl#A1OQZuw{GJ*P^raGAw|zo~k%y3nCeALK^JWt(exvwpG8jERTVv?%;8^DM zahGhd=iTUfwEz8MM8dyzxAYP1(JkaJ)Ku3m;4Dtl;W8U10re%dB zNBvO8+YY_X9s(YTwf$U~RvvJ!o`whjTr~(VC%Ey5V~PiG_s@^c_vR*_oR zk?j{UIh^wrMCK8I6oAz*`WL{tH*X};uCoUqWq?NR@&|yC(iIRFX8XkBEtji^%%^)V zWD?xVWxyL}$@R7FbTVM&28g96z^-b5p$rf+u8?z;zW^!lqV69#Z>G*96SO#e73-o1-A`kLL_@4mR%#wJw?6d8c4;!4Mfq-x zI~-bB{lV=HHIe2#Zx({EmbK@18N8(`H&OMWf--4{T-Pk%GQ^Xr!|*tdrr7=%`(+1V z2tj-a&2?ae)!xS{r>eVhOm$&y9j^JNd#z5X@|zznl;dypQQJMc^@^C|4PicA0XYzP z&%ub_D6pVRLB?TwijG@9={Ipl zgZ~(&7-LH?h!C~N<4qVzSq!uLi1^cJV^HJ!W8x}p;+|kr!JJe}vwZU{uQ9P{j*A-> z4RMssCczU|rIl4C&{yo!AtFz#yI#R~m+@fOaNGst)#i%0*#X(h*f*BF*%!KQ!vV9` z393pFPH}!i9IcqaM5pDHg%V7Ev7Y~dJp$AIISx!7{}CAA5~d-&sANZ(_c zU39cP+R0lJc;cjI+KGibA)n9B(M`Q*mPmof2gg(ir;tVAA})=3JEIc27AM zQ{ryo*=x;n9J)?&HV7)(3ZxA3<-se9crOeYkRXxvON3Lnf1AAgK;{&O3rL$EXR{DQ z2EcW}^*qNWX9gJPw;r;9Y?SaFMGTggfHkXM`U8+<1d+Zs6oU--uqV6-5(9v-35SFH z#x8C9f}U$mH7(2`0&K!|xqg2Un6JwN;KgR`q*nlveg{7M_*+SYY5TuGjZPlIEZ<43 zBa>`c)#S|n6<>avRxmw#yKxZnkl%5C`^w8LhfaoTFsO*nY@022!Q1}KEIsWvK9Urg zrzicJSdEqbIOQmKR%9f_nAy{MO#O=y*4b1Zn_`bO#gcdm8CfN5F}5=(%GdqHP<&Nd zg_zfS?Q6zrr+aSII|8bpQ2| z;4I?KFq36-vaT2G30x%vE{#5!dSAC$Iy+H$tnM9L22y)h5AH%qRJZJ|IO0DE_npBw zf^tk<{bcX1pN(<6+#32pVxEbv z2C(I~_>W!eZ-J$f?iQ=2Y&i*NJbhDSyYJ$_>UXeZkp$HB2Vi~PPP3>DM}&R^nAvD7 z;+~IYWQC*BP`{KZOWI6|=1@xYCoulXT+2gCe~LC%Butg9 zD1}hA{NolMY0+?D+<1n9jl1Kwa$6a3#6V%q$jq?AN%lVKd6A~$SL=!2CD_jE6Gk;8V?=oD0W zY^qBo^cw#a)5vbdyqA~AWmLOaC~8Xb$dE$_q7cmAulYIy`JC{fHv{w%d5Gh|0)pxuKvT7 zeE~mRX;?kz6omByu>}Mi*OJBofU{>G-c}+w5)vb`#2MyotoTpHdtgFVaY~Le>F{s# zd*LZjdxS`i@(U|(kx4i*X7n?|F!K8cwvSO&w8o-nFhATsUa9#?+2hPY98EIfy)D8V zy&=Y#R8)>!q4GNF5cnf5wBIvG7=ix|FSaq_+~U@EfWLk*2d z#X#B;ckL$&d{jw%bZp(ZfNnS5ARmD*qOQHnW)(h?gF^JaPMeRE_Ty>rlufjwb(OOH z*Obg@yRgg{kcEa}!l_uA%Y$h1ceypwC9OjLSmZdK;O?BtF)~NKk_p12%~?-7FB)Yc zi~&jH$uZyC%VP(UV31g+e7leXfCmHvXZpsjm$#qV#`+&X!~=oWKeuQ9Z*LAn{S^e6 z>vZbH)%*28gp>Aj-f5pKd~NLo>l&x91-)z-=8>mZGDtAudrs!T-KGmz4L>-slJ zIQkX6yEkX*wvZgk=9ICggooXrVp?is=ye;%25+%un^!jP@7PQ{ljDF3;@j(mNupeC zs?lKz8J~6#(nDdZGkHHqTVg5oTv^dngI3~oWw}Z^OrHCFR5rnzVvd84xOuFwYC>OG60fPYq zvieOxa6)QFrjV|9U!F&!W*(@_d&1Z-7N05u9ZB``P*Kjwjoe~l$_-J<$lN!MD z)HylW+5g`iQL?6yZL%FAJx%%01Qc;VS>VKz@mRdF&m{UU7vWG-#{Aj;dxZ-Rg<2yC~{l!Z39E=i*BUV<{PKlgbP zI6P^+SydoV3dYavzoUB#EuQFFEXHYQkgs6*2nH@?17I_~goHLh##IRarUnk*0$*~^ z9)Y-d*l2Ti64tXYFUe#j?OV7|AJ(}w9b!@M_3)q@mSamlrry(rhSuxQ1m-iXc)`1! zNIgu$%8XJI7%6B?I;WQsLYY#u+mclrosQI1f@R!aE1hJMPSD1Lbl#lO9d~@kW=aOB)X-csT7=;vFpAI}upJ5Riz z$ykcKe&W0J$A+CDvSB-X3zG^n^hj**Yoaz&aa9pwgl!I{Znh7H59sEkE~+D!?#9v zbQ>x8QHM*Z(r2I6apx30P`Tht5xLmW=7nhvb0THu8#>2s&m<6k>tFcw#^<7#v zNPGE~{CBxdvDQ(0FGV2az0WU?S$T=(j0@@rd8= zWZg?hUC6Z#M3V=EfUAqx-?EWzc@F|aw%VHe;ui~^4(7B#sIM_pRYK^U*NmyIqDM@) zx7E3~J^_jM(~n@3(=55qx2eWLdwb4Rm|7Z;(MoOwPPWd^Daq4pyzOiqh(!CYRb-HD z&|?p^QKYj{XC5632&ZEb@4{&dER#$LM6r?YVE%sp?>^@Kj_532GWO=VANCLr%8O8Hp9ty5YG6y_=S|a%{d|YWX8{mjbg!=0rTtj3_sBj6 zr1u^J_=jT82;}V$)CYiEn?U;tf*3&OG(#^bc>(SjU=^#Y%_vMGf}m``=#0FX48TO{ zLTbK_j5z8<-uOw-tt*=+Pg20n)}R$R>$n=S68z)to}fagIR5CGt1jZUiLMNqxZ3eh z5WHYuiFwGu+WBuLTKPKjEa>?#vBTtKZG+E2ge3cv76G-af_(nG$|0IgkvY&;LNpUx z8>-JfGlvJ!PwA-9tlb;QLRrS#=ukIlBLg`iA@tI2FxqA}DFSf^A_tYJqv*>yddFb7 zhKP~7D=9Z!H}HJ=6!M)H7fM=NBzzzK64bWj_K3Y-%tw3R@DW_zNtv}nDHJrCh@`zA zUe2YI-#5wQ4L`4#T~wuM?b}Bx_{)8ms||(pyH-J}_utQ>-X@1{`C#Y9qIC<6%X~dV zbIz{xwLWy2ZM(w!;7_ns*~KLH)nTeuSbkrG{t^8x(&N@xCd+$iryQrE_w`~U3eCg1 z1qHjFf7!oD-t0{RrV-fVku6}J0JDVeK01h2JvIAM4UABVsz<33p!IWF>g*^ujwRth z_+i}RL3ih~0FU&~>CkVd_8OB3x<2FOe@$)o1H>z$@O}n=1#09T$d!WkJZK4#A;-Sr zH+=7MK&a`@yP}F@AguJ<5`-15A-1g2_^l@ne+k;m@j)>3pliGVon^@1OgrlSa?fNy zk|P!@{HCzW{sb^*KoZe@wq;g|r?jo9dpmY6@&V*-TY!zGMD5-O77knj?DHOcKSjzd zSWyIM&jLUcZI^CJ0G;w0OxfKuh%61`Gc%=#qldJA3-4uHa?NLxP{L7W8$`A8J%oto zA1_}o{nNSZ6)(ZIFv<|e$Uc#2cJ>Ls>-rOXls%4=hbKS9;2JB5BmGEhDfvvGRwIrbyy_X#b1i0^CZq#oI$BW=D+`E9mIhe_jf;pXXVxPG4k z%{A@4`*Ncf+c_Cd;8J&l4~HDEIU`2`ii^7$_Wm{lW%Idb_oHR%W(Hw0Gq{kMn}Co9 zMkFqPp$!t>>+kF5xi33*MgPD_awCzC&AYGLTq%0oNgvrbkbfvSIA!u}d0GRmrOW?W zm=Xt4eqh{Cz9z7(M@#sPeUDHH!85ItD#^y@gB1S*2x%=sxKyZOR zYe<*e^RzX`Yz$3|QC-OF2)0l1vce?D;i$xx*LOmPrFtHgf+`Pu|2&iN>#fY0r$$DB zgu9wu844~PZ`_VCYiE>Ftys*RzcW1|2b_RkV@Xs+e>w8gVlKt2XW2U;6q0 z&LPe_Q{b#3Vz6Lp@B`7##%GH_gA-bClfz^oOfdtn2y5O$0$&_j^25 zSz|3|Za{6($di=tYS#u84g6Tdw}yZPwmADyxKuM>bwImed1y9>b%Bt<*S&ghu>}GR z5y6>)jHjO;Vin3FD1V4OD1cN0AEenjgD^L+Xn>iU;>t&~?sW}>@Sl^Ija&73fhPli zI%LC^0xL+sLJrWgYyz*Y3j8NP{+&(e3T$0Mq!&R~BLy}^Aq2~p2CX2o$M^fcbY8uz zTjP9!N4c^R&I;uH$F=BZjw&Q>L5VeOGdCRO*DhXm9h~APHKKH<*qF0l6~(_2V)TTG z1xwQR1VvHKiguqagl$$`pyn1TKp&#pf|=$pEdr&LyOee0I}8MsSTRbP{1aXw=@ezT zUCg-^(%_KEq}CMY!pKY;5+KU}4Ls0<=3lwpaYcS{=FPsLLvAfP5ghcB7N%u&!w5y5 zHr=21JuO)Chf5f}dJ)j&Tu3Xc02T3|Btwp@ln}=I75)s50ME18je3Nf={*GU++`{X zwk+K931BMF1I1a9aItXDnsrgXFxYZ*wC0DM4F3aut!y_8h|f3lRfw;{LW9WCBZ&BN z`fSI)aYt9vAIvZzL){U`J=GkyD?ETqCg^u`a#I zLybk75IP1FWi^y`XAoZ1kvS$cBmT>nK$svl4ZV64@ZkqwB^EC~w=!ibVucK-X#iM1 z*%$)n!9+(V@pc+e0suM}J0p#=Q_TgLZjFp5D*$2_Sb72eS?G_~ zBv;b{AJ|$3uIW6l36&^n4`q$-fSxn0e=z_6`i;>PD{1(sieel&KxN{la0H6V@5=1}Bg5Pis8u*P0RW=dOiP`$782u6dhVG?X}0khvLz{!JmJ9w{v_kx{ZDpIHw_ze_%j6?secPnDzechNqv307U<_c$|&rLUVOC@*v!_=v|2`P{khEg>*H9%2h zk>I< zL_0=O33VJM#Z#SJrOW~ShSQd@DY_m1%#Ld0F>1V6#Jes0)}4r*isD|CZd(8CD(4w! z{=o2r_PwYWD{SZWo3XpuK@n{FFKs+@De zcH)>-tw>Z~(Go}Q@9@e)v@cq4{q{Xepa)SIH(`A9<75I&qV!PLoro^|<&ntYJes?P z_}q?M0$yPb#fI+IQYeyvh47B;4U4c(sIWAv8^jIy^lgf3%ga6Cup3l!xZC3`I#U5G z_u2LLo=n>AJm%Fi`1eK19+6Mo$RSAVvzecfpiu8mu-we*M$_NoFz+m{ZJef#-2S*D z{74=q@LbBeu%|xv>AM3u1%#N|-Uq*T7JrQ&BzOGmNQCKb^REbEvApw9tGbAJ%rQK* z|4IKZJmg_nqJqBSju(=a;0AlC-4X@@Kw*Om3Gg#b*=r#d2u?RLU6yR4@ zRK)&}`rYil1|l;fxe{X4)rPee5@0W43=vCd<21tT1ZGZIqskeDWZ)&jBLb(l;fn`# z88~zG^@I>adV^UbYp)!!s0qZOL1R)6qB3Amtr;nFyA$~UT%S$Qn+oygg0p(G();w; zX%%bpcIn4|3!Yl189mo!ArJT94j8V7owk>X0$KxXyV?bMYwpIP9Af>*_DC!kM4?HK zdM@gS&dZuFHk%%cD}}<`Agl)0*_+Lu?xKn$EQ>kD6~2Ez9(9h_53-co?(Swr92-*>?S7b`NRKR)s~D0q-)xgioVXa$EzUk_`eHM-iwc%V^H9n6*Oy>ogD&@T!>ni=c8{sviU$`nB>41H)1Pi1S<2-U_kr2Ab<(@o7 z28LZMrmtx01~tfUr`DGxD+~UrJd9OFJ6hdfp>Ay|^U|9kp+lqdNUt~K=Ps;*7)l|fueWr?6w1(T6!}?{ zsbj0<;IrMBhrH__R9*h$)J9qb1}IHkHR!ae*+kNu5Ug-Wf#a0~-us61dNTnx4Btk= ztH|Fa!O_)6O0wNrWOu~Jl5_4Oe)V@1=kS3Y^8zOr)xSI3I?`#qHD1}bK zvIxW;ti%B)yWMe`ooFrPdI6Ey6Z!xJV*DxC=*N5#$Rhyu`G!Ml&bxs84o2P_P|G<# z{{+4}V!xv3TF0g!m7~rixIhqVq55sk5B`X4VrdiuAh8duAfWi!kA@D`@Fia=^j^03 zKL@Lmz}j*WgbV2rzxm-WgwG@%B^)yAK}Cp{ja<^%0aFz*Zm)wwLGA zhRY`QWgHC2`}X1}~pzM}*Pf*pEGp=%m4EpvbnO z1gOVV)itU|dA?tUJ}>{FU);*bPKc6ZK9SVmmQ*}0|A_Driy_+Gcg2|Uu!rsB3gtH& zW`d=I&HkE^R}N6Ye?;a}`RZx5#*Ndgy0Pn_`cZwMKNLrB&Ae6Cddu(Pg#M--2|RyS zRq^7>@F}+*$oev&3LWB(Y+f{^sqKpt_*bTo*X zK*Q#=rR(?7{XgChfgoNNeA)Z>#7Gi617cK&)On}Mv~dRvV|iOjd(CU_AKz^$v0M}A zy#QHpunTtxP~1Zfme72FWBc4jg#7!Qhrle8+cF3dThb64Q69biWi?NhgfO|jsdBg0 zUA8FLffXn1fOfJxDT?o zdEvJ-luV1+wyUe4DpHn2sXfgiKx_MMGz9&ayveaMtudy5BGu@OIeg+};Y}O{;Wvd1 z(M9*o&H=NA>aOCDD|&xR(w`m+Z{iJ-UYRp*%eSM?>*DsgW;w8@yyOdX>)KwIA@v}3 z>{@@U&ANbhgyTI!Xc(j$#7FWJ4aJU*m$}_i|gq%t=$DR6|w# z>(-^&2vuBk$FbSF&Yhn-i*X0%`d`bg58S6eR=<2%8C<(_KDAf1bK&*1%+AgL{7cZE z!|h;8qf`ogQ-juuxSF`}dm?APE>4%1F#`wkqlVtb0F$Ffh{ZZ@cft@DC1fku0}sN(Q2AWuK9 zLK63-!0Hjc)1NT`24bp0={-oA=%UL<8u*uupRAP&l*_Aj2IsCx=1v#LD%gE+^CNW zS5h}~qb^N&8sGWSPfspR$exaLk*|9!)G79fNlU|mDe>)4ex?mu@%>;6mR!?Q7Fr-0!xuMW*q zIx-#I4+YaZe4DQfhkg60IzDlgW6=L)W*{}zKGo>IRxy7icX#-0MtTkHX_<411C_*i zdtz}nY(Lzxg+uKRp0NRQMtCs)+dy;Ib>S@7y!o<*16?8e`DWnLsOOn&lKTQfPIt`dvS-6w=fxKn{#}t#k_Mmow^}f!ISnKpo~Koq=zg}SIL1mlV6l6*Yx;}+ zt;2K@W>Xt?=UeL3JNtud;#p6=_2+5&rhyl=h_7upD5KA-UDU>0-wmS*#hg(}mU(~L zHD>jBsn>PVXAq55&vptPYGiL=rCuAf@5JK6`{wB3 ze(9lYBkdU?vbKF-=nD!Yut22(!P#qx-pp>5ya12Vu>nVj$8SKbGyt?{5JBg9g?cN@ zz#rrK-(a6E&p!rXY6%+hs&LmH5!hNyd^N!(r_b?S@6}4Gbx4?k;Ipe*+rn|NBR@7! z`0T4^O}wJ;j!uA9evM!H%#vN#6^>VOkg9n%q=I7taHOUp^$6v;8r35=6@gvKt9{Mi z(xuzXc185N;g*a}JAoat{Ald%GfH}6S{+B~Wgj#L&epQ>fa9@9St?CY6bLmhQ8&n} zn3$F}LgA>}p3B|2(623fX{YaMIQmNa^JmB2w~nt3&tH1GM!ddWKo@vgX=gXR7q@fn zRa+bIxpZkVBA4b&L*PjTpK7LR=3DKdq#51}|9Y`ITU27$K z`f*ODG&gmB)ah6+m#E6}b}jekEmS_g!xX?~ znQ-aCb+*B*DC1UUS<4c#=6(k;VhuXJ2`ZDteL1;0sw>b-bFo^SsFeTx(IsHUnf7Q0 za1y+4$+-`i;BGhBCp#Q;DdwM4qe)h&)Te8IW~S;Lvwakq>W_BtO1lPI>?#fP;ot#t zbb;Nmf0+zkRm+JgIX?Xem%JFz3ih~-D9c!w;H!nTA|xpfqxu&>IpRjlz=*9-(VN7w zh~)`NK<}i_{rn>Lyvpv6#}F5IGRi~4i@avA=q)|1wGI_=SdQ^QHnOd@E6{kt@E(qNxehr08rzSl* z=@BmexLPK!#0A?Scn(iaa3qwkx-d@Eo#5$wGgMHcIDV!$MMvoS~n?`c#7YTFzrn|D9886u0FbT)gsEcNUN` zn3K=aBv_x;jV*t{=lo-7OIP^h4P{W?E{}bKL~SQ30eu`Zb5^wMd{;Xr;Uj@eWK4t^2BK*R^30LAtxUyE`WxlF}X0NGS*?-Q6jjQX(KF-O}CNf`l{(?={!G*4oeB zpWz*U_`?ZI#vJ1uR~+XNJE2S*%EV6&%HS;utuA$RN3Fg7+QFH&OftLCr@pndx|=GPi)CiM>Dmk{BG$xe9D`#VWMi#Q zoGur!uP4|fO(*M-yiA%LD>jz}=(={wP9m8Oq~;-)z&hQa3M2-&Xy?Omu=_dz(B{ft z!|ozJ_#5zFggS#mt<93Y{W!8>ZvmEHYaqIgb!b7q2YB}QZbkE}3Q7I~yq$J{%Rq|; z`0Y(X#7m=FA1|wObh^Dk_>5pvBx3~du{-+)a_yinC5!B>cJ+rDQ~;g=s*ox}-30)m z?m*&KgWXKILY5D~r%Rnr9uqS7f6r6fMZoNsdhP}T1}j;-#m$w1#TI(y!_Nr93j3#4 zE#_^nt(+9nB`PE;*oFH%RjQgu?0PChyrkpFVtqi_-P3K43 z@3Hd7ibKb7zRaDYsEn0dK8I54&l<#hr6rGrpYIYg7ILlmO4Gq?s5t`0GVTxW0+)iT z=D2@+E`$HT!J%!%WTj{KtN!$qu02X^nX5IR)a=@vHGl&!AlXHRIbUm@4vt zH~qZDiMPySxTNt~1`EA>AjmiDp0kp6%!Y_(al2DF;dpB2_9*_)>+CacAmmQ5WTYq_ z{Vz**kRtiEn?;V1MGVZ>4z97z;?gnT$tDe59QBEKHV?A08aUR^KpU^Soq&UpI`U1F zo11$uQXi=SFvCwiUblcY-WfC=$7$FL8uhaV&^_#xsVYloh~f&6KmpU&UK)*%-vE^a znyEo(_Xa3tqn||AP$WD?;r7}KhT0y@ae%ba5(!7 zHt;t@MG=_l&Z@h}A&- zV0AtT-YmQ%&t6$s=MQVcmw^~FH>X)#h`4`&;4Q?q;vnRTbg$rB@GM zZ9d0^)&q$ao#d#ggR2cW`>^N$&yE+Pq7$n=Ub%RL$)Dd3L;0YfC+o^uMsXEBKweN9 z)&K-xl&V<^+g~LGhkzJq-+%DM1rw2l*Iot9ck<3m^$Gd3FZ%w zJ=hLbMqr7RAIS!_X19LxhW>+sQXg<4`!uaCmkq!;(Am2Mc8DU?*JQw<13HiayhAY0 z-X0Sg8MO++2LV^)3R;!N4ND(>toCIccK{d=6f-)gXpjKe6zLKCGcEhw;z1Pms4hq$ zN$dXvQYYKDVubPD+t+o0RhO|V=nerzLy^htkPgVMdOS`saJpRs>5$PPd)%MTqJZJ- z6EuqrbbQ)+gu8$2@>Y9|*c(ej#DO(P`!PsA{P<0m9;&c0xo)2OaT?5Sn1l_c-)E8j zfAv`gFc2fw66)y~2W_|lf+#5m5U>)08v1G9$S))HXb5X0rsj39Z}!bR!$dT|Q-IKf zMNv^JAW*4DC1B7H=?u+wv2cki`3I9lIAh&@dn?`Depp1b8{tS=Tn0bzeb0<{oZ*O9 z+!@xEmd4R9tfv%y{Imj>H!l6T_e@3*g|uZMcSt7@PiwZTWXu!WI1Yh@bL~V%zzB6- za!UTB)Ii&KM$~Ly`TJxn=A<%ssdw^<69;h^Rp~a;`u3I4RZ->2+6RvvTCl`?ZxRiN zijJHMk~YlT(#0mm>rzUE!b-g)aJt&b;31MyiDjr^J&S(6B2013b@1vXd{m7Yh&p1E zI6O(!SyY+sViw3ngYuvHlf^sUuf`-}^BkZl$mA<=DWlK`bIrI^l4*V&E>-WTJb5j= z(&s$DFiDyKK7ix57VRUv`!C5EmZ_Gh7adE|>0}n?m&y_N?Nq(?9X-f4jpc~ldx8zI ziSPIqc_W6N=HfUCQ59rlfB06@Q@{;J`MSPHTFOSC2JYtWArHUW_ zD1ig%tN*0_>cv2mnP(ho*yWZ%7>6eD*Jo+`AVuH_V#8ObRr=Vt|#XbUsvGDK4Env5P`miVd2@H>` zP}|%k$L~c=)0|C!OhN9s?hj3)MfU{Wo1KDdmmt#iEpS?6|1c4Gbdn1GHZ;mKb=?lk z&p0tjwF4(ER1Xoa2Jgs&z1Kf3%`F%t%B;;ID{yLS1twN?Y-+N!u79%QT^|~?>rlIp zgp|OdUia<3Qk04E|Dp`zyo*H`&`ApCS+JV zB|$zeNlKA2c;8*7gjtAm;W$E7$Gs)LMe3bSn3GPGE~SvsgdSzHMp#VT_5~uB$7=peGOF3-_-V&WHh-&C@~bfYh)cYzRYF|kMZuE!R>7`b;ztfCpl1{ z>Y*4dqI6d>f>lYzz_VDWZTWDzb6gRoy~hXd)_zkMaVdOi&f-7Q=i?4xlcUM1XE( z%{-E|*w^-$dU_iq)+`>s%juj4TLS2o!PbOgetH2o89=L}2m95<{ohX0@sn%e>{00S z81+WPB9gUX-tJ3f17PRcnryX}l^=px@D2<}e*mLFATIN3&5kAh&ZTT*yvk!sO}10Sq1k311@C{U{Wd&mgoPw($pi zEXya2z9Q$Ab#yc3HC1G4ppJ8u2FYs1g>xDv6Ivq$kmgFM`15a3dFmHoNNLC+EnwDC z7vii`9f5kF2KU~GvYB+^VBj|r7U{&XE+_k{8DdwaS%u zPU#})!RTxrw9|GJwy=3*Lh5<$Gn3IT5eDx9br!q&ibTvcxOcdY)QMiAiAEC4qNZT{FZZ68yQqdsRgW6&nd$s7j0t*Ld( zo_ur)4Rlq-!w{6IWIf{G)`Or$eW;QR&#|wBT!UXS+cB8jm8Su_AUrVN)b!L zT}a9uqSO3VhN5RAqvFB1w~Y{oeiSYv$>ExVlu*uBlEG7A8G*0&FOcy3bFkn(c-Px_inggmrW>FI zVIk%_L}u5edAt{SW_sJ9+__INah37>{Z2+5PoV-vJm+LsIdxRLKCxrqVbVck_TCG@ zr}|19i>Q@Xqc&eUdMN}eaTc;t6cbnaN1J!U=c|-cbgNoQ9 z_?0eCL325Men>0trmqHmUEAQz-$p5gDn9+daK-I?c7)5cCaag5|G65uDoLc`Z_4^( zP<%%@s6W>q+TZTG$7D*OMwyP*gzu;i3#OX{{M;F6#Wrk+>*ate=xr_Qmfn{_35o_* zxrI1VcPa-*ulmL6(>a1Im(b#|_rs)8lt?cG3+kiOsVk#7Wq%PECq{?$M#mx(W7uwo zsp%8)hwPXk9ljTg%r##?TlpA$REE(cX2u{3>3xV+{u24b74FVi-%<9#9S1XbUt)s{ zS?~VL#Uj8i8DnUanet0qe#2fxKPFNGZQh*uPODu92Wge@mHU|Blh|VkjVVf|KcJZX zb2#Rs-jVS7Op%Fa&zw&b$$IaF*7!uAKg$g!?#4@h-iDd&d#L>~wNpbrK}u0FpSqAn zK}sG=O0ECEZSrn5c!Etlb0Q;&&$3A1%b4DZDzQbrrdVNp9gdKIxx_LKK|&k*yHE`J zJ#A*B1=8TiX6o}>M7Ve=3u#tOPP#$c1qKzBukm50KSk@!%p`LPC=?RLY#HSV^3f2z z7bZhH5xGL5%4HJLLrNe?+gF7L+ET0&w7uHMoPXXV?X(6c(XD(^y*IT#W-^1o+Q{3d zi!+A9jYOB-%rFtdTnPUABidlIPV7BLUh<4e8Tsxs{p^`v7@i%HzWK{TEK;uxNu-Us zW>rt7EN}g|Q)fylDvLjhgwnE0(fjhmwZfB4Bc;mngm2t4Myuecw*o?8?NpTJrw6Hm zbmb6Yi{@TEVx&(as_P@RsY&|r(BW=tc-0CR zMf^%KE~~#5ZFo62z3_pU!lY`^caK*BY#&^bIWnw7DMqo6#Q$?=pl3gC6CjUw6+byj z>sUxh8H5)CHy0V)r zdOJXh5i2<1M8;0v2-Xx0kY2OX9?Jh_~H7SHx96sTJf)bW1KtwC_ zBwRW#S!hcaG-8#HlU%6;C#xfc$H|wEmCrP0T&SJ;#*7d5e|PZc_Gl&|eMRhdg~@|EQBhW799Q?J znPJ9Uc}n_fvsAHEFXS~=Qg5-RWW1t#dXqHY&mH9dXJDcD2<9+}151DR2i)HwqehcS zLTA{?Xl8D?j?)$cL^-jlzutsAmXO+w&>16MGBo$mcW>?yk$?XJ-$C-P-~I2Y)p*X= z2;OtFa*0Hfezmxn(uWuJ%$JNr7~w_D#O6sY=8}p^-zEIc_^9;%ywcG#@O*t2r9`@eMEA2Ls`scs`OX~XIZ5gt!H)|Z)rz_)yw9k{|1-S) z>lc;aw)vlh@p&CsVY9cvCD)V%KBW9V@A$7TSxEo)??7MF70UfjN&NT6YuqLN^I`t` zn(2Yxe@DFkyx1`b^WP=Je_rz|f`yjV|2~vn{&y((?>AD=fDQ2fIsX3>1Q*1ws6hA_ z`B~8^$WZ#gK@jD(J(SE$@5}jruGLRmv#9hdUccx~kju3Nz^ZFt?s)=WdXS_3>BssX z5g1+@oyIKgy zBTXJQ@twc+;z0xLp_?>Y6Qcif5;pL6hRo329MqT$nsE+IFuMH(D5^Khdt%=K=nC~# z*Pny`KffpI_jgZ!5W0^5WNEbfYK=o^)Gn`i#IuRa<)jwg_kKy(Q?T3g2!grmmO&?>|_>DUqYp2u%b8fXsn$w~g;_pg)Gn zGx|klfGz~y*NMdVGyvb{$by{s>87$_(GKyV3Fr=QU;(su)BkCgJ12d)_kX{s>DP`& zH`Xm^)X>v&u_ydt>}=f6Ttw$i`!fofjSF>sZ(Vf- z7g%LiKWvv!{Xf$|k0bmR<(7E2dOhF7D_uZx@Cwy85eIjctpgw{g9W;`6T+=Tm4D2z77AyCwqu37a_&+w>M>KHaGNEWbW^Qog@;?Q}HN{PJR2;&06r)#jR^#UG)p(kFQ=$)d#H*&|X$#Qu!Wc&Hfp`1b}}8f7YST zdQCT)LC>rIS?Hvp7`r0dk*Ehk@Qt^fp^teAct`y~pe||yG^{8AP-kpu&gHVEfj)Ftqx1aHpnvw<9W+4Qfg`HY3HUb`w<$yBZ~(g^;SCH@!q zMFT`+J9z5VRW80Ze4gw=E0#JeSzu}EV2N5+b#oN3VzOb$M+tUhz>yjfYDq7)9t%Y_ z7!?$&XY|Zsbz~#$tT9z!Y}g4frUJ|)3gNV*mhAoT#t0gkv9NN->Jvn-m;bb^{rwGu z8c^`y$OPJh{sdt4Y|uN@Y^vGm1f4>W>Mf|6LgscE8f*djN)}`f6R{uN0lZ|CH1^Nv zVfxkfZZlh){Bft{QgyI+lO-4aT+s$oI)d7!YBTb#g;;eS4X*&dt`Su?M7t{>Q?i9% zGg$d^lsfFKfuig*84}e-Qd3;pN>X24ZB8`x7_aAW@g=wY|UY06GhIZ89)F!*! z%jpPicFl1$TE~hU1(dMka+t-^4Zjpg)F_Juym6Ei*cDq73Fh0#_M1psljoQd;3@-t zCH*5n#o7U3^W)R~+6lM^(C#}xLpYCr*@;4*8&Cq!=an-S3{gw%7e(x(ANKQzZ=1+( z2ju!hT!D!63H0Avs0*tU8_3!*S~n=Wg&ybmG()ODvk`-j?Yc1RV>( zp_zXW`F>fO!E}INug>E$f@ERp5fiE$YHnj3B4mx$-fMl2Q9QV#KGep9%F1_CNwwJ3MYcY()OH0|pEq01^ENcw)ZyOKWZGvBFzX+`rx`&4=^6ssaoYBtCY+SL}r*iwW}t?e;b0?6Dq4QUj1cnC9AdS8YJ>M2}Z=l zdfXb-^Pvs9U_9F=yblOt?Zl$84tg&x7+Xj&SzkY?^_F(pTk_qS*8&`~$}Bttg=VNA zxOClmyir-~+p|{W8tb_9fZXvGG_%bu*GOkVMyD2vr&7}b0=)fAKjjmp zS~s9Hu~pj0RY;6JDsg2*QBD>fyP_Hp3o$mYYeRyy2Yi$zyTdwO_Ndi5NECKOh|rsl z;=Afw+!7O-BVqmGU&d*Knc#>CKq(?SA_i#+Q5OSe&!L~GMl%OxA6#c}q-Kl!cj45O%F<}N`B1_8G1oWlUo=uiWp zO>~?>*gRW+>H7dQt{=gq$i5y3C8+PlZ|!xw_*Y??ez>V#&?rV@Tv!jKIti>PkNWC0 z#-l!P06BgbGFozFyeqW)tGnkFz8cD-T!FU z+lsSDRn}z9)9*y&OqQZ9i?la(_S^$OUh&^whMLCkNlV>RFtx}Ue_y}}gV!`g-D>>d z=lny~u@vTG0WIQ&m7t_X!hUnfM(?+0RrM4=mH{xnt$51edYiIW5mYekN77IJ~~J{(p1 zWmnwnnZM0s`4U8k9qE{oKaj*3CW?;o0jc5YDqbspw@*!R7CB@rvg@L;(lM71Jl;{8 zV2%~oNzw$}XRvzf2cG;u-nDy)2@RuW|9BSZ664d$Y<#xM-|l5h|Iff}dTxmvv@mzF z?-$p^%%rfhZFI_6k}jI4kQAPeKNxZPg>GPQKLY7?`EX%4>Re3k%PRyfp$~h3#@b>| z$K3dPx~OqB32zwgb_U}x3Vjz8?%=j+Q|7)f9RBdb3fCXnkHCWDPfa5`pw1hcdIy;j zC5u{}s^aZ`{>el+W3{z{z7#u1W8M)gZOb*;WFE+ClgLPs-^{pPrJJrFIE?c`q3@)g zI;?K4M4FhPW;{UD4r5P<=J?h4Ub9$G74{klf0kxp@_Pm5Ig~ooa-71!%>Hw4fBq0I zQq*4smK15v?JoitDUNlj_IU!zr;RZ%RmRd7k}saO&EVbU*E8o(QWHq$Thhg;#L1$E zsn+oS{*CO+x}=F(CIE(S8N~#IJHx5gu%s#KXgWeVc~(-4&@q%j9rL2urUYiVj&m`+x zjp3T}1@v+yDrtFycX;C|sUJQY2z`#$m1CG7?_YU)q`n)tr7b!6r3nxFN1R52`fr(h zclKtfA%izPiosf$N$Fz!_8r0n3ZB6U^2NUX5$B4LAtia!oRvk|yUo*I?+jCg#O8{Q znhlG$BMC1AiF|{FWlnA6V*&#@b6~R5@gMD9fCva@Y<@SxwE&ScD{JVbbBwj|I z1|?I1H=yHk3`?nzs;97BiQk?gjwqdIcov`boU%YX=0t){s?qPXO+K?(F>%(C z;kOW;x4#RPOrG()b3_oR0?yXZ}`2>Cf}&WE#kLakst186#w+JzZi9&+6&0Xo9E864Ub-D^JNVKVque zA=QSv(2J3BQXp;Y_OZ1%>$X~nSDrR;V$QHq<`6421VxTT*5NZA=cC#q?vY^pbigVA zm{11^FmyDAik5*ko`1Acu?$eTPCzaR$=*6thjjjvoqTTq%71_0$^7@S2Jys+`nTUc zN33P)t+@jhF*&!HX1bn9;YE5RN_+$1h}LF2O9szt*}EPmkr*01I+83kvua)$S-mIe ziqQQCrYwqKPjRKUQVU1#0)BEYBNt?Sm+{u?st?5wn@RbLa7*P9-$>z=FTSDa9q$Z3 zL0xSm8b_rvN^QTRFv)rVH;ieEOkkH=r7p-RCa;+hMODjaoSe$!Th(3*ve|1!D82@f zI^*dMX}&>gXi$>m=d)E7zOlPl#(Z6C0<@WGLgnR-fWGE)FkXJ8!R>58Dc}qQj|4f~# z04tmm5YhF4q7ooAf1&;PQEHH(EV?TQjRxY3{r6&^tc*QAMj@*eHvddXKz(|zf~|;F zeW#V9>*nJ9;*NkT`Ri%^Q-^HNAMlNI6M1O(rM0|Wi;fM2aJHD|KbqtN_9LiX!_rR6 zvWieKi&ei^h-PbQg%@J;QA79+S!BZQy0dgPn&pDX&Pin>44I zi`u9rb?|;|vR)*o7vqEEMNcphf+GcPX`|TQ@>xU_O`ahg_pivL$zZk9Fn=m!a-(4p zSGL4b>`I}Ty&!(LD;oV<>`-B{D{0p8Jbtw<*rxA5qN?6`yVeh4JY*7{Yr09HijYNtmwXV!&*WVnW z7>%6V=&(v2{|e8*1DkW&undO;;c;+cvC|9~jq{2>hD(qabnE9Q8+i_b4*?9@x0#^C zCJ4A0fUyKvAT5XGUu4Olp@*LiluU^B9OOtnrU4iuyes!_Y(r~W+z$Ym~37#lKJH#vmWrhsv) zSaU#f%;hC1+nh+aMOwRzt{ukL%}(L%@3mUM&NVH~{~CeweIX(tnukb9Lm5kSY>5K0 z@MS(awNQqYvJG7qpMxP-8g$4i+=UUcy`G1s2rx0mSwIe{^U<+mAMkjsc5VCH_S%-F zQB1CoH$5VXYNuXwFP`6$XD|ktD`rM%l|_w_NyeyXm&lO-4Xf<49NI!K5h*^eRX@TT zlxSi&!%unV$}n=4pB54+*0>BhFPm~Dmi#6l`S$oKwGb@*MU@^zIr(>$M$0xy`zQwA zy2Ofd?^PjS3Zyv}%pq~jTO^j2c9N(gV4i&M6QN^rXucV!FRbv%PVIKNgl-mhEib*; z=mHL4@<{_wEY{@jp-%hd1C)OVsD|yw1Y=;w2{lWBrqU0x9e}U?f|(fVv>fYpS^_w> zfV%Mk*p!gC4F79QB=_{Q{o!Yt+&^$1hp29n#IiY!5&iGybX`b#v9*}ov#4gAg~Bwg znv+d?i*cK-S{J&ZLblJ+dp%3Op|PQ`|3sATDt?EFJt4kIxBFS)9gx4w7d%nQ@WBPC2SPLcjjxWdY&rQ=Hy(@mMDg2(O- zlZo%+h#ztIiO<70L{{NdCKVm<5Mu7_uHu&vQYz-vP-}_p!0;hHIF+EhPSF(Q!tX&v1biFcR821W z>z-kfLn?)m4~1v3?r1_e9R^OmA1;Q3Pb8`rv%Zsl9V0*TTt-FcB49LvX6{Sx4NZ>Z z^j8-P%P^sE3GHG5eA@`7cgV@}74zxO<1+%#5M}buf8rnc&7awcrrIhsz%ERplFJUx z=AQz!_JB3O%YQcGZmNL@^mY;^kRzY7#Cvtj~z1rv@8p%7_atNZ% z7VhrP!Rf-*H&QW>8exzVV8lp%$rM56MQOB;f=tqsP)tlJ zOyjVKWankvO46SU7q3dn%&Ed2o6=RU(sLwOA>g(R&o+z6jla@u zg4uUdnk%Gubkw>VKIiVpZ+|dx)5kdMkqk?gOKC~4Eqrjj|oC-`!EPIcQ zy$~|YxA>J}C{Q1jz48jqRDENRx|Ui}jbzF(mnWW!O%0 zocm)_Cjhv{l0V(_gS^?!tWZ*bOewwPjO+rYntx}$=zQ8DUQ0V6CI`J0U}*aXxKmFl{Zgru>GTLc!(}nK@^p1e*Rr6T=v0dEybQuJ3dV-Yh z;S;8R+D~cESQMm{0zj;L+YWT=T zr`p519~MPX<};r5wn||`(Zml!|MD1;VgRf zcE=8d1d&?G2L2rbhk6)Sh$R7r6-za#5HhP9^)k(eubrr8!@lH}X`Y(5v)=-Ks5&yV zgrQQ1&`w~;^y?g}5nxDjkJIoB=Bggpz=ZfZ2}<$m7$*zm|0djf%-(8zW# zz+Qe2{L$EF>v>(l0OAPZ1p&MX7XEx~ZNOCq2x`A?Wo=;R-1Ic+{Ik36&skCB3qAMVD$5zG6GqAdh}G zs6u+;iksA&10j-0q7@!Fq%0KQX}5{CDh7Xu0dD3=%7elV$Un}ufZrpc`Wb4sY|<<%w>t>O&an*~iUfW_Y$ z{kx;D8QMY?_6c?mD?3bVpJ5{ShgSf7*Qd}}diJU74veHb$^w@6?r?9! z8rKISCg3ZnN)^MY`+oP%SW&u@?$+R~`kpLFY>p^UmqrA7=RPnupvwO?OC6?WEKp*j z35*UP`68Nsb(^=DjYQa-mdM$s6+RCi;2XBs@Cy4-RYv&_L;7$y3l=wl&&A~TP#f5m ziofqQG&@j~(oZuJ+;!8@UIZvOv3xO@Maeg_Jf=*PLavM)*05soQ2QnQxX06DC~r+} z$phEZH#@oXByUck&X8Kc``KaneXPFjTQ=fQDS7mbEE419v;AA)b0^Aljs9hM86Wa~!WRH%H@^c=b3|pXAf%dUj z@6uUMKFqZkgU)@7*IUsbC*aVu)p`aj;va2GviaJX|5={rVn|_VUzB_kwN5C_t-&{< zFK!;fL3_7)lZ)Uy9A{s!?q`bpq(I|@5I-YZEN&HZlOR(jbkKFB`#{0}Zqnm|^okvm zI2IwYGqQ1^z{UZgYT#oi!O&qI_L5J&+39MhYN}W6;zk;F&!X!je1a)LdZ?V*BJw%; zyN^uuwqX|vjV7(y!mtEnr?o#B?2xQqhWJQ$y`94MIiq5Ch9oz5k;$+of0FI9HPb@U$dtZXa;1$NqGM zP*TjlxDseOqY^c{)i&GceWkP7gZRo(XxlDiM#0)(_o2de!nE!d73aWNo%nCJ4MTw+ zDLzBkkp&W6X7{NiYs+QpO*+;fJ&FUth#1wUWjerQF4_eG9dTd>^fY+z266#i_=1+` z9aIsP_a`>z-8tw)0S?9Axzh{iWCu-GkSC8Y4 zOL55RHVodcm?hn$DYM8^&0t{DG~S0Vsvo+@Zxc%Qtc=jHKAux73m8ygo8TFK|JfSH z)$r2X(IO_+1}Hy(h3$a2ft(l#VSgxOV|(TT7JvXeJLjMte*(ib2V#i z;HhcWm7qKB$MiQ)&AFt2q^dVo6PU-KS)Csovo@@euXSMo&ILs zFbc{3b`!mZ%HH^?H<^vZ)cnjSzS~*xON_awWR`ZS_8WKF^64$CHg5b zerDaLksvy=f&CjQtCLCFpVitDKSGp(>K}B`TkDtbkIX6tCY*Zqj_u%3RYdP~CXc3! zF&uNlPgH(;QsQ!sTA^?nb}}s*$JP5tS_XXy9k<^!OPW@;FEy!D&GZ*%TDBK{O&9E_ z6d!8zrjokHn7r9*cGP~PjdV6P%t!e8Rl-Y`BV;2#^socd_#aDkHB40LqCB_lSt_}$ z>yWmpa~-)hU948lo>|ge*w)s(ogl9O1-sG0$|6JAwO{TrkUWp+UBn*Lg3+g$kIz&vombC?IsO1C=(ELIWDU07ZMciG8{On>t0v+WqQv8~Aq2pA;zl zhuFjApUPCm>=rFwT{3SynX!4A(N9!AE4v@bo-BJ3%4+q=_ofvvb+33~YF_*XOWw&FN2<7ctJAyq{9rRk04iFGZ%(HGBa z3r&S338g_~vgN6a%jYV6jV!}Llzvv4WUO4eMjny819=r z|0#+VlKfK)VNsiEw7$8{Lj{E2p}_jnseF{65l3C;ORT4QU)!LibSxwdQWb+`4xZnT%f(>vm_cG3}2bUa+I>M4-E97UGa4I&Eu` z%h9+^-oRs}?+)$(?Iw_Ac>$SBiyuGKUID7cf{q1Qdm?hf+}$@&1BQR~!=(GofMJpa znb$@zKGp<~T?vhL=G;w}@BuHJU=zH5ZiP&TA$s_jjCUx@Jr-H8kcwXlvY0a=>2E+opLGflGfLjOuMPd z*)BbB)gKbmn54zmXs+XLqS3?}Bb0Xxq;QvD@tuBsUU-Mm>f9HK#3{)46?Rca=B8Q| zY1QzzRBK%~ZUH0;ar29HF$Jru6S>y!3}?P@xGn<+AtW1>h-4ml_h$20y>5gAM;r9? z)P}3Usi39OyKJNYLq3WOCejyEPYjhoxuet3YJ3dRg_} z7YN=yoe$D~0&5niQWGj|%6Ymaue|);{`Z)BRA7r1xDTY2+yScF`PW!q2gy{Cm!SZ7 zhVFP>Vp@S#&Z%(&z72DQ!x$hVv{EHK_C&C5#I9P(S0Fl+1K_w9I2Y$R!oezD;$}&|N)eW|u=X#7BVjFPGOTysN^h z`&;c)gScERIxQ(~WcRKE6=}72VF>YhaXKoGn2t-PolFvIQCFeJ+3(uWSg=#fhW|A9 znIf6wsIb<=^O-PYr4a#g3~c0FEKLl3Dc$ak)ha9x?i>OfUBL|@Z4Gq&l0+&o3=~~Q z%Lra#Oetzcyu>Q@!IBpaLY6qp9=*k~Li}(zNvv$9_Xm=c$(_|SgiQ>D%iUb{J!E%R z)%KbJkZrbQis8WDb?na^ut+?n)d6>IEbS%K%S0Ry*1iL&W8V|hEfY}GPJs39UqIiv z0G4HQ`ut>lU=GnVK8w%+E)!&b8aK}xw-c1wfEF#*Z5z6F9W@(pK>XKzTMWO(K$Px_ zTOZX^(T|UV;1Xt{ODD#El=gI$V;Whm{dmT5u!V3f3!*Z=h6;*s~)=c=rCMa&wKc^luwmSW3Avf#!oEjN9> z2Lxe20^|kuGAgkt^XD`eO%R6#m6%){@eQs>oYAG#!p0#HFK9(&YPRZK*z^nXNjZ2w zxw{aUr-{)JFqZFFG zI6@jaL-kokt00X)}lz)6DGx%JEs7L8zU z9e@lTfa>GjO}u&vpvn=iQ$UY{2HnQEEqa{+dg*)6WPg+8(wCnFD1oZP;%pfIDhfX= zmrk=W`ET>y_pr2l1dIN;R5NZ&q$ahdQj^@)c#8IjkO})EEe0y;WihwH zv<058_>GFEz*dnShOdm#1})tV_g(IE1_<)y;_-X?Or2@;pmn-Y74aC4J3i-c2rjV#>M4bYc}k7tgC9W~waw1aXM2>s!AYmr z7*aBt9(**nQ3=3!nVwh8({UR#oDz4SuoHAy1KN}Ib~C^Nxe;^{V?Uh(2HXi?fbIS= zzHSB_ThEJlV0W|vB?toP>e(wXI`B4kp!s+`mGkIMJqF#e^4ia`4AK4rH*uh9n1my{ zVH&5Lel(!sN+^tJVTF7`RXR^}=;Q7}*PiBodlI2Y@{+|fMz}gS#vE6irCc+ee8`eq z8!ucnwO0*^E2EAgFJHh+8YNi%qA8>g9ff6OL=5z753-Mn{kM zJ}kWBSQi(Kg_$mf|00~nQ?+DKIqC54U5Kmva`h}~<$(KumTG}^M2{a%Zwz}&p|E-> z+61MYgq=kBs}Ji1VLrNMHYi^2OT~2+^VJ58m2`9(V<>bf7~{W2;zG)uX1U0T@T3-r zG$kNinJ>Cc+}N9IgPsvR&YJ%5;??kSoksA9-@06qWqzpe!`0jO=~U!nSHyD+;&fT8 z{77KffZoT!)`dZXLyYqf08fc!ktP!xyy54*`fXD#MPdYp0DB4^V0bv1orvEp`&F`N zxZ#9&;mYZzQob8n5csgl*ucxil`Ri-icS893(!n^o$sEG+tsq!8ulFq`zcv3q-#i2 zYp)tsp*Z3-4_L$>0r7TNDeD`QF$@9%y_c3& z`S#+Wq%X#jEUvXbvE9JR5>(2Z75Q`E^X z$7i%q)~?V^T6Hb=>%5@z7~l#Y70RP&NzRT;9vc>*R8QzN?bFjX@oudoiicgTTK0z=UL8D~4!-JAD z5mO>EGF>)~EPpFI$uO+^1IsSv=BC2{_E$#Awv?2SVf@9$bIF`PKMxDU*=*{RlM#QI z9?HZ{TQ1HUB!3SL0L>|UdU-MdtF@AXCC&-!08#{}h398tHs86VTO6t=+c^>GUoBEQ z5?UqqjeV{C5^>?DwPx$V`Ga_=keeHsRx9ck>f?ueWqWV_c+TmAgz{xn+=QTV!tO;s zn>4A!q-F+9j*JY*8{%-#FYealto@jPXPR{b+3tI`;-cWx4fF2EO~|(rm8LisPnIw_ zyw;^SGwg*e72|nDZyx^i9BY}w86GiOKE&v-g2LMIQuFz>M7;H!ZC7n&Uz3XvZP@eV zK?5mHnK$1$F{lP-Sr;%(u;VE-<0ojMBWdswAiW{5)+odoFhwJ|y5 z3sVQ!cL}^Ncau0xa>4=Az~Az3yj>n<#ab1bgF?j$-YQZzo{TER(tUSjzs}r*&LF2X zd06=2{O0w9?RgQDC$WFp2yD$;KFgEq{FQe1h|h(xJ`}c06AjtqqloenQ>zWHYq>@oTSBl56>fe5|g&laPcu zYt#;B1sh8Tc($*3gQ5v8YmDU5gEEnurc_qC1r$67LykPO@P$5@mZ{rF2?>nY?*!I4 zr6qH|EXU$9*^G`KVj(O>xlktRC)V_-^%x;$D+%?)E#!aw5SFQG6)(-&iyT3jN{zo4 zWJEQFhz^IL**Qr$|e84c*ex4bm}`Fo1$|x5Q9GBV7{G z(jna-B`OG`ARr(p0`h<6eeb=$_ulWHHEY()TC6kY*=O&4c7FCOiD5$H54sY+KV!bz zvG>?2E=wKkY(^+M1^IqVwX3hh4(;$T*DepS_i^Y{vIz6HMxG{RZ~c%|nWqz{*EV%? zqz8&QzQAlF>n%GP-yH(-3jpGNACu_wagh6x>yVmfSbTj7xUZ;2Zxk5CSn#|cFx<1l zHbfT`0U(Z|{+f834oi-dU(G=xCXq05sna2vJAP!?yVInne*Mt>4bH4iJt=Em@5f7% zNBqhvLsIrioOpXSH2ll#e;l~5!*EV}?2Vl7`qY%<$J1U_89Yvk|f<7*g1@DI^Jef$()xL-iHava- zM*&Q`r9d4kzgNAz_DoVy(-)!M1=N(FjUNa)PS+Yg+Blq}xbK z_3Ue@c-W^R4iY*7OIxDlM21Q%B{e%WZV84p>X`3#aEcReXTm69Sw98da&f5hVDB{9 zV|UGNG}9WGLoUveyB8rnQq_q}J0c*dgV#&t`#DRG2alIs{w(2DcrM#7zTWR{Zy&v@ z?=+hGOnnGiB}71(XsMLhd~SCwaDezE7h~AXDKJsPb#@rl=uZ;%YncAZ9A&Ztm2wwA zzmDw>yQf<|Z=-*7p`dMmDtr{M3C8ww0Efg2zy(MO)%fcK(RlIlSjRZNmv&lg$x#5< zAsK9{eh*_sXf%IxeayyRG*Z4SI_nE{A+uI@--qJ~2ui-gw0xjD$TQXI+evYy~8n)GhTr{La_8 zgETlgT{INIwlbl%Q(!wu%2CEqVN^v*?CkJG2pj4-KUYuKPhV4_y3-_sAVZE%DQ8~H zFjBs->zHT~<6%Z@os4lWrlw($G}9cG14%Rzt9+qTlWx3>ggWDLbsUwd&oSuf9d|}$ zJk7?A!VTWjkvmXIhnw0$-qh9Nh&ViFt!y0Tase=4~&4{!(OE+yi)H5`Us% zeSi*yBA+o@0NtZ_|DqA-xVQ-c!XnZ9f4g1bZs0eIlb&t%Vt74LX6w8zpKpI`zoiJM zNPIk`Q#4s~?^%vY+MasQi#Kde1=4+k1P&jndzB|erI{?k6Z%X`CqVrMHfh;5jo=#U zv0p1YpHiQ{vdTA!hlv7n7BfzJQfQwaifL$Qs>FGC;7H*&zoGuTV`X^|aWxf9a^G*L zA@#TDoR22OiND_zVwvJK=V0M0bmlkHyc*br z?H!sFn+e#Bj4~nhaWk`nKLnCk)k=`>ZLHm8%GXRNZYy;UoI=FZj#=hP111vEqS-)u z1T}qD^U%S7IrHIijIwsnlg-o8*8wVmvE`@d=trwiHoRi4`{2xy?$w#3q3|z!k@Vmc za;^p;QRQUjchzU+ik8q4ai$H8bYZrbN7Y~~3vc{LP0xqmBBPR!N+z>s(o`Vf#fM}B z9~e7@%7eY84GZJY7rtCB4*(qz5gI%dVvXB~VxdQORq zcLh-9fgzia+aF60eEub8+ELRyddO1oCVz%VGEHhe*j9*7KzBN)|p`v$K5^Yw6i3` zUmSbB!|5r0;XKf-FcqAR-rxAx>|$YT0Y7Ih1tEp;bbM7hMxJMdkrK@vGqSg=ZOW7* za$x^R;XREwe8c6G#RLy~MPc>qYHz)6<#=Z})^af|olTmJ?dw2StIP4FBuqs2Wr zkAoGk8x-d%+;x}}JDnDae{e6N2PnU;kN0~`_&#urM)N9!uX_DOr5qD})vL}KbL`B< zxap*f>)D-g5x3fTDK`diFS{RR*$)DrG4a^-PCb+K!{l6QHvzSc_nZ|H*u&89jARVu zO@&X($g%d&Z=nTcgEXmh;5`v9gX(1*f#zXRwqk~wmF zbold{H5C>73p>j!eggzvZQzeS-vFUl6jm42C8mVcJ@TZKNyL%c7MwnY7hm@0xz0KLoHf~QV zq-vr|J;TY|#3cx{tjn)Na=4gG0&4Rkna7&4DhEc4LRZmTb;`8He#v$$HoW`xT<5zA z54BLGSl*=kyxV}vI^__XGMu z`;bM?fzmXfJn4~A@#Z)~79bK^68%>*qun%rQ@C<4P2f{x7tOaIJAGIS{k{h<^|g9} z_3;_?X}6@}f`+H8-q>5hm8o=!b;74gZ4qA*&m;=t)m5npS8nu->d;yCqjzRXK6eWy zkRaZLpVsZByh5il!80e)s|!w_=F!={M{CpmbKzJ{opl2ZVhApV24s9rRux{P=(JIo zsbms310{5GLRH1bq)S4UOBMmH#_?Jb+hs+zFjT2*liuG^9V&wJ&gDz@%^%R%&dCAEe&`cE z$fG)6`Q!dXpd#ixq}}4iDL>wd$gJN0$c>s{zEQGgUoOx);xNwN`XeMI(C1FV{oc)a zT{KEB*aWlr&4uouX_7$Hk+F7JM~qGJhoCHzDd9uxEs8I1PV<*(3|4WfW3mQbh6Sf9 zw`dxs(=`>#J^y(BaPml|3f@ut#_o0V<1fyJ1N31Yl)+UMw)hI0LN)2H@-bFq*v(8Z zSw>@zO!xr)S?5n~`1Z^HSG_`&!{c6}YDtOInUa+z^o8iJ5nk2Ndm>D;=sU;(6&k?A z-0Z*IRK1z^dU||`s{QBNc|f)RRJ#2JLJd!M=U{*da}RJJ!Y*CH+E1(gu6483%&qi4 zwR_+3gk)&LEHiX)pC`tFNj2cl5Z+ zu}tA?5tdV%3TJ|zn83ZI&pK5?t>=ZG@x$xQE&222KYr$-QKXf&U1FFfpJQd5nPrCF zW*DfS9z1^PJ6g%R^Yl!U*`iD_= zh+6(fg#m4)zL1O6ajak)?$L#uAg*?>s~M&`y~Z$oPQ+csKBB6gEVj4qva=yeaFI_- zIlBAV4?)BEGP;sal}tBbgog7ibhdv?^q=-`Jmzh)j*@%maWYZzNEG`K1>dasK7>h} zf1zaTjMDP+=|rMnJu5FJbcRVJw$0_MmgjwfYCFji9ZwU%dYp0ki5Q~z8G)=x`{@)* zai&y)A6+p`fPZ)oz$#06J`0pOxcNMiRQ-MX2WXELnoV~B=0}$ca5(r}bfG2@0HZt$ z&YMs<;5#7oE{L>41rO|iOu_d-T!FEQzn@-@!+AZp&<33M%5EHSx<~jbLZ|5$)i%Lm zVTU;0Ji&p|H(@Uv8!hr7CpUsgtF5rQQ$COW3a9*451>LoQ73ebqGk?XylulQd(6|u zq~lU!jwqNWMrDWWmt@-4|Fa5T9`F+dG9(Bz@>Zqa%#pZAwmZ zrd^^~-WI*A8#PQ~CG@apjSLiz+A|E+O#W0UE#dX?uJx%`bBmMl)m@(uvR0}vd<=(D zCv9rDScOR~f%-dkSu`$K?+e&XF+n^w)iy~%TYYKVI)#iaoH-UO%3-gaMx!KU%My#5 zK7vbzsTid{g4<{V4tgc__Luzh6r!&764Exee*LX>Y4=^v&zh%)M0LBIid znBU;V@A*;@*7qO{*No^SL(X-UFRwd49p8Tr4)Fg>C69lABhcP^sKv!mRdr)_+sosX z^a#Q~D@^$SgnJiaSno<1CmTxQ`^yoSAa5g z4j_&@Md1^W1b@W5em!k6g#y3Bd-QBb?&!l=cE`<<{OuBvXTh#yD(b~R_0G(D4GPop zy|}2dbpG^}yMPOWB$mnQcHL4K0O3wQm*fuSs6Rb zo-h^|M(jRNd4`4dEkx;!anwf#v?n36T_EChd0b!yJ7edmMBr)C<2R35<77TF0}&L{ zJ`~z*@SKGF&x6>%Ht_d0B;!V+eD1BH$Q5K&44W-Rb>`>v;!#flSH&$#KCWM*rk|o3 zZiBBY1VX;PxLtd3bY9zk-P8a4UC*}v?I!ZyOQpUVlS;jh{T(DD*);Rq^uwnf+)+Lm z^`R?crR_L5$bL)foy{_Idjoz$^;=6&LpFHEJCCHqFnBvAbRQKf774g7=E< zAN=b;{R@1us>TU>_x!YljB2mlUx57E1-V${Qcn{)Fz9wI{@T8Ak7k6C;DuJ zmC*;$oD@*?Ym}5W^ z@x> z3e{=vvU^Kz+V>eq5KkHwwYcn}gqN#AmLA1X|Mh~61DzvQpyvlL6wE{aS=KA7A7fjf z;_EGB=wU~w``j2%e|26AAf6xH?V|{I>3xG@le@A1TIMe)=LOD`dT19Yu#E$7O8}nw z+4BL#5UePR<60tgDSz9qQX*^&0B1#EWX_GDz^N}#g-0$yVgL8xz`(;L**_w9e{#Eh z5&}@V^GiyX9B_d=1#mKp*IQ5BZO##IWsiMaZsSGFzl46x3H@4sa`NBL2e4J?C*bh( zzvX>BMfFaH{d^ksvrJ-N>KBUgK=J>#^AwM3D`ah$vHj6hN8d#Q5Hh&t4|+a3nRn28d+B}GKJL$`gA#Q? z!sp_lfC9Nd3F~jU@&6~Ce;;Il^1uZIS;lm=nA7JsHwNhT{$Rz?6&nDL2Gtt~Ijq|T z5=Eg0hQ%ALr>LsN+vnXgD3yTn4z4@o4K-x`Fa3y6%<);fb_E8UCcvula&I5qp%gO! zF@F2#&*w)bbt`!`zfcSd9E0gk?=TL3&j+ZdMvZ$pS^tH?GC^I~^fxO1XLWvpzg9MkXGmRVE-eU zUfiE;fc( zBKR{a@?znCq=|a;0?)f~4bmZc+9EN~bb~zW zhkyLidxOgWL?c`PKnGBMNpITkUTBT3}Y7*cfKoEiehq>rlT;|y4 zMGwF~fV9m5JwWHhcQ;+$vHz{)X4lsyRVV($eGcf;OrI&Xj`^bA{&W4*ClywJYMWy# z0xFq8cYw=z@czx<)OOnY0vr!&?56l1j+TR~^OP9v>Dd0NUQ~fdEPdDT=jwZX}p^17A z;CgfdEsy!hY}?=DoM(6HAsI-3-^>|6CA0t1U5lATex=1{As>E*gnh>i`;JO@ptQoP z#c$e9NVGQ>4c>r)-1PQXYMjaA_5047^WQADC7Nx&rj&g+Ii9ZL@4k^AAFmDvv#Z38 zci!NoGbgH~Gp!gsVAfMwI6$c6$g5&~PVM$fg?&d|WyJD7Ve&swlGu9^a)J^LSlD@$ z%pMntt|(LBcFg+*=am#SK$}|>$NmgM-u(pTy`XLr`t1Sta}-NI2s?Z5)B7$RkWK*L z{>!N6i~nIMuir-5sxk!A5bjpt2c*rlKErwyoe{%KK52Go%$f`|xWt$aAJe=ElSzg8 z+co_lvinw*v)B-%Swc=P`fL;eg?|Hp-~^$R11cqivhRV~=qIcvmnapD>Rdw^Bk~9O zFE-BE=bnv;%>E#yE?jRxqG)Ld6yM!$%Ae=WX>(bx?fM9y?PL6rnk52##}h#up=xs>##G6VcNlat>D{syFAKsrPj3{VExU zH}m!j=@|;Gs@NIDIjY_h5PN4}i*Hc_!hrhA`*)~o8!*6lOug@{KU`P?raw6&&{+c% zbOQs#`T&FZdf%h=UqrKFVc7|L`!9PhXSlwytp^KTGkXt>id46S$@G5JXH8!=MM3(H zwFNmsmXHmC>DAPqy>*B>AYQ~2BG;OGQ;+|g z2@;Z+pP}}U)0^@yHiF>+tnfEgtmt%#^IJ8=#Ts3G!KG3jks>Dup7lx|Y2p8fHb&ouPV1>{7rDH~jVj@^6VRa2Wdc7bID=(uQyqdy-IU z`S`ay0t_{rbg+T}OOzl!rwZwJg^1;E*C^A&ypS;FFQEvx2G%2doF zcc`7s-!gv5vl*tR-@7j;S>52zkM<7vKVr{-WLbZh*`s+ql?DBcfE7wUWOnx97X&HS zPg*&G;7rZisp({C71B6rk!+aL3h2B&Uy^Zj^m5s?@DN5Tm3Rmbd_;y@3p{yZ(G?N> zT178K6C2HN#v3Pmv+WCiQY&$Ip@7fm@#=r5`ELRP`)E`>Dab4+G)VLBt1%pott|K7^5BD3ya8n3O|#GG^rBOoDZF#A>*g*bp9?*%`VCQX7&RqBU$<_Pf<& zOyo=D32L7c1G=F1(BeyA%aB<`zzY0t8BX0T;OV(B*b0VLRn>km`jYbaw&%Sz`O56; zY_|1^8aO9=mXjlh0cT7R&^z|YWG_b|R2!4XSX!2JmC}DER+~V_(4C$2vdSwb7U5YC zE!HYW!`I*+Y|JU*33x)fYIN$_)1*`UQfTYy4~9&VQr}<^Z1}Q0xjwCX&VDT%)KEeH z)e8^ck@<7qLUCIc?JetiC;}_JgLII}R}RZO{28wrHY8Q)MuVMdf$M@px1(yhhkwdgv9e=>-*s^mdg zCKyAvnoF3#xg@tF7YSt}k`M~Ip7h)hGAe{XRno=&oEVH|ZY92k?olOHfu`pqajh4`2!({n)*D#7Zo>p!eaW zpxHQVzo^TS=Kp6WvFlAU%}bv&XT)dc#7ez1G+fL^XxwS(*X2WCqWRfdzOx?Yz7_@; z?5nlD=|YoG_F;=yMU%sw0?P)+w(M^emJKwl!vm##dYXw^XpkwV3GW*7ms;wHT;>SB zT;3FMT?QOk$fsvRU~PHCEXqO8;o%5cO$lvH`PlG5AI#)Wq z{5B<{Xs6L4O>D{%_pzf710E(H!v6bClT3vC823S9U^ zwMt9EHa2?gV@EA8&K(9$+kjc@*l}=|g;S;#>F2Ze7g@d%`)^ry6YbK#q?k00Ok4`p zcp~TnqJyB^N-V8h4y^>Pug@YSeHdt2AF(JegPAe#nE2xC%bhowU#o|ha4*q%B^D$! z2sOoT^UgMZg>8x9Oa?f+t=9jT^mRnsOVb&KMZI6x`EL<{EpDqlbWGQn+AEwDve9IW~DctLD&$lrJ6ip!?nWu zjzV!ZQSa%wwvn+hOH79QEcRHZ-u@1E{VPW&{`z;q^_@0xb~B|W#z_4S zlK*o_{J#qnIruHU_JCl*swt(**HRam8zVI(5zebrZJzmI+Nc$Cnm0m8%)L)4eckOT zd3R=E7^J}o96JDJQz4Ebj3VSo;Gtc$>&@md9r~SE4}98z^40N(z26(483xnhXqJF9 zqSGoMbqCI*1x7lQwSejvfqNimqIAiX!K6gdda8?j__-wJZ$~8~djp)uk6; z&DwvOdlbBrZ5|-=o;|I2ePJQ+^x{SW=?rT)-hFLzmie7rxJwN(=fOiTiV}u zkyM;;J!JBs6ML}8@JKr{HXN*jVTP$c6YY#IV$4cdxr{{`uRvrZ9Wo^QaNkZZ%O;}#zjd{Bja{_b&9Jz=8w~POaU##V_GZuR2BH%z(&kqlB~!ktfIz@ z;Sf4!yGnx1jwfc=R$>&x5j_q!;t+NPrG@kBxQ4V1E>D+0s9hsD#^T+CNpqcPr)|-Y zuhWq1P%n}wh&>x-Of_ankD?xE|M6Z!qK1K;rid24G5!oi0#hkI8luDUT#L%#BN|EO zG?6xT2ed-=zm;o2{&Wp=Ix4E}ixEi?(?>*hf@NlVU+~3-R>$PQxmP12R_jYRTaE!$ zdG0VLx}s*AIH_Qg<+=`g8-@938q6Yl(Yb0YqFVwQpWmswq##Wntz~btZ#+!KNy()1BuP`*Nj&OF%eNP^?;)dC;CGNx|~I976fSsU~1V`xoI>@r169=WPiT;J9hk8Jeb$m+t3bL;t-uifrGAGcnUIB);Y?}s6psv zX9$@gv`2G|@HjZy(3aL!pB7o0{Ti4Lf;ZT*=-W@XFvJ=A%o5c#*~Ia1w2kz?a9R~#duy7+z>0>4UrZSL z*BE9c$YF#CctRLbfkLRUeoF_mDnd-So}W0vn)|&S?kDc<^P#S7q<)$x856fuMUNBRaiJp7zuw^T%>toO{NglIEySGD*LkXT*8G&N zvi5VHg=R(ubcceLjPJpOk>Fm)a)x5juF)dicXs;~*R{n6oLGi9aYhXG&uO!6`slfX z%eJ{0qmFJvaBU8)A-9o{11_!XM;W3~9-4lVgxKyi-FyOu<6?AEp8S?xIaXjm279t53|>=hx_k`q_Nzil*SywYQRPM8BB|-31((vNdpTe3F9dk z?{lOq0M+&&9M*KT49-4H)@f~YbaGBT*;g&l+xfyJ^~AJDFc@PqL7NXHa{SXNsGSZphHP^+4 zaL{LHv+khjIBP^!C%Q4soPBTQAKgpg?7RJS#9~5j8e6-dHkLcg?(C0&}cdi@|uoDJ6Lb( z#7p|!DND(a3bfn&pDSzyJU>EnnO$=-@WwX? zi4fDv85^SOqCz@U4`fNl9Wm(96VWxvMP@2CjZ&!1qwD<|5>r_KU`EG4t~wt(vk55s zx{O5qkkp61Av@c6VfY^w$2%*NCXi+!UQia#TZ~#YR-Nf-mKuC^^-qq1wnUiA;LHZV zWfoP;te7y#)QK7$+)*Jah|~z@9M#Seb<8>rjx$_xjTv8bxSSq^BI^(Nf@^D?-QaHXq7xLNiG}LKg&1o1CAoqG=HZp+U#GAal3)-AZoxUcwIY1pB-m>f zwfk?4o~ZrPGn*}3Du78-@99(7JYB0u_0#`z@cu)vUo(F{ZS<~nj=Ukbkqmtcf7sUN zg!>`7uEt&*Ytq0s?oalzsaTHbo6v}?m5w)3=7#<=9NZ=jGoQwyk4$?vFM79J1E-s$ zF<$GYX9RVK+sZ>2pjb@|mJiBEp@l0t}XuF_B^#tY|+=ccL2enpVZE?unXPtc&_c zEePpFDp`0vx`$_&t*os1vEH#uCf=dE7(Y9cxo&5({DYLY$;+!T$LefPc(#B^bze86 z;xIxR`(4aGytCP>mS(xl4{o}V?CozxUs642EYoTm=Vi6+IrhN$B+1&;*#UQ?Qd~1L z`|7)XuTD3x!R|@hOSCG^Hr(x7>mkBA4haGg12(A=%e>J_QPmi-`E{`svg>=@3M*@1 zB!c&W7_rvwvPZs1wSUOF%it>)IXdE_&b90Z=l#7f-Ry<6v@Zz4rK4NkRbSeO;jexq z$OZY6tw33ejiC_B{Nv@CgL|SPhEO>M-!bKib~+&YZ37_`fLFNcn|#)HEJ_QBu1~ZM zQ7uH*w#65V5=M3#@lm!P_}(x(cGO)zXC9tA)8>1J1qqYL%Mbl@#-_b&7$*!wZ-Rh*VR&ZO`090W)S4l;btC{q7I44lcGNH!bDW2 zRyqYNraY;D$1P~Uk*GHk&6YwBJ4wNEISlFEy{Auu5yxrAiP#Fd7b|8u>c=@eV3$Q+ z?DYla%)M03wJv!;L(8Z~nx>Y%!;Pu;10ph0MUoLGexLUR>1Wy!=a-HA(lSgEPuaab{B2RtI9c-8(6#P$hZ$C5*|gp1 z$5?~sT0RXgOX^#_4}9NF7!>dXY1^dD$Z^;3v=S_7`|Hpqrx{q{nq*!VM$LVyTo`%a zmuU1rN66@I%b;|+Ulo0bqINtKR7TJEmJBFWD1f!xtgj;LKfARW-DAe*o-W}WE}3}! zaIhY_JY-D~`-bfv=cuZYMOi(s$drhYNW}tNH23p#s8}6+svT|j<8<#cM-x{zL0pw` zL^ay7Zk{3~v_vU`s|Jc@gzk2Dma|mjDa9;4MLf>MzKeZ3TWTxp6>=h=cDgIGm- zIP;XRqNdzj#P9&KCLu_XO%Fp!zorK>4Eb(oHT|Z`;xkXh5Y`|TC=Cu~DYKw+)YHz? zS%|F`5`I9PGFX}WLK3TV2&6=~;`7Yr;ZiW3l5rvXXM7loKboV?u_hy?siT-WDH124 zc@3I$u2Tx-@}c8Xi!E^wGe@|cX4TXRJLbE|*-TeMK7GCNJ9v}(BHe>$T6am0TPzzF zo8BkHX>+fPV){{S^V z3-3;o8T_F?_DVNh2sm}_;dn#7<%VOIT#$17LHBB%-@{JiBch>|7Z`q{Mu}pYNRz$v zs1)qYS22<~)OPH=(X=%Z^JNQUznxSy?V>Se_>D~HNv=58a&M&q4T9Xbr%7|$c#N#M zf66{M(?nyAUd>t!G?AGGdCX6Et0IWrqk$~Rtg9AGPK-2^aU06CiDTfcW6YbJF3MSg zNiZeN5-gmFD?(U8%K-~US{1s8aV-L>>pT8tHw%r(;spVkBo^j+B+<1kL8R!r62a&# z9~|Z>j1W&ZG~YIe78Fqe)pSC*J>K1rfQ-?Aqk9if<`*6GjGiN&<&f*l=`)D8fP9)#fZ4$y5Ga`s?k*d{KuU zqU9>6sOAb&ow3;~r#52K#_^`?i&|%?d%h#Si|-1y7CFT79T9#~=`@R&%$9eFS@TAd zY;K6;4Dg7ppxx|qd}$Z@z4gczcWztLlIB^=YQvpH1M{j#aq>hacGpPZ=Z8tQ-r?#H zEk-^dSM{IJ9CGNAa^4a#Qu(;tAt2U|F@3cAP?3L#UsAPbOV7zUq0!r!9pmU{D#MG1 z*wlB^YKb~;3HQA=XLn$6kDt`JSmq7ePq)&O<>O*Z`5st3aqsDWE5hU~qL{g-L|R?` zRIl`SRW2nJiNh>^oT2u#$;Fx_31(cTJf5f; z@M7YIqf!T<#G<$hC#P0Qe7hl)aQvj^;Zw0J>3fT5ObT%CW>WnPMPt`w|<7`3c5 zw@G@{V)ztnWl%I{9`D(04VkMVu zWubu*|In>HkALpt^+~RvS=Wlq_4OGy>6ESkIaVnSG`jL^!La7X7r7r zzR47~|1no0u`Ew|ju_<@+JCWzrht~;*9NMC<$^&RC+i~P62$$7=22s$ysF3cT(cL4 z7gy_Dtqmvj3m+LGehX%I5RoW3_~o8I<{<16C6ylw>4vLOG~F;`jW=a};= zOfdn2DDxNVG?;OdV-Y00TfSt8Qtaqt&k06lzO^c-#&D#mW8|F~pS7LTFw(_QCGpiz zdcJ5)QqJ8SfYK{nPjtzWiw=@eoP*N8QH@7Eq|O{>UQB?kW$!`3L9Ettm--hF9ovH> zlA)HQ`lDNhPd1gK=-7^Or_GJ;-Z6MVj_jJXV+_jbJ>-ePb+~bKmL1DU)zf$lIqITR z^Nj-C{^Tw(H%XpqWR(GJc2;S;MoY#z*LiUfdFa_03Axz$91b(9ru*YYt9v9&b1o9d zEMikg0F`}fQmW6W9s4l1boJ1IxcRG28yJmJGNJM*DF)#6R71HeK&p$j3;QKdDRa$i zGBIHt#}c;BiLsLqNr{b)s~-FX%_w7uC*pBVBwy;~u@e9|0>LB$v2?C|+82>5Z9ZHR z{vQY>`Uou;kE5$;K|&&niFjh#^Lia@nuK&L4z~4W&p@0Oor2*B?8Y9e2e$97bXq_B z>b^Tq2_EmDD9Lczs9$a`zK|kxH*e^&nGzI?wST~eROWjQ(dJdP?6*!dtaNmXei?+k5rGQ1SOP-Y0CVbX!;hC=O5&p^%UeUIV#0X*i~6nJK>msaLAvO&Zl!5 zCsyzAKtGEGD*pZxc?_T}TgglT5%-Oyt0 zZET87D=K6f5scg@Tu;W)S&vJ2Tk%EWcJ@M@VaQJh7p6La2`8j>Ni@!r$kaU_)%0WE z-IsaH&yfs=p)Wu_RPHW$R8BYfk%8|B;NT%Lv}(8y21Bj3rQ6$QGyA4~onX>P!t_!n zvFRN}pRsvDDhPXi#@A~9T3(k}E5F3C^F>>JI`MNWTg!BTKee1fg3F8UWQc49V*)6U z*@08EXrqPi+Z&03()C*G?~TlIH&WwH!H?+`hbQtP3jCHJj`Hi8Fp!Q#!-Mh zm%9%4pv%IDzA33NfX`MA4!H`ksA?-l=~b)^ZHS>GE6phG)q$#wmslp42qT%qcAe4( zThLfXxix1s8KDPCe$?DY_blrs_#=Gr)2zaEnb9-Am{k#W_9Y`Fs!S9(&d;-%=fo?< zEQRuB9>4ZtQ*SeDRj_pDF`&V)PXn_PlHrUoC&7g4olqfJM=`-hPJMe@g7n&&N>Otf zMIkahAljO*rWK__uIxxd%UU&|qBqjjGVDxr?F$=NP4Z84@iN&TVo`<0kD6)cb##GN z#?KXWf;ySK>9pOug&I?a4 zuh?F@XgfcrTUKmPJF;*-YH%23rDQ@6MDM8oG`6mi>rcJ1lg65k#!aKZgAq_CwW@7| zOBqWW@cz{Sh17k!R0ZqtbQ8=oHXTZur@>bMU=@8t-9| zvf)3B8B7L}8RWSOwtP*ulh+kI0iN=gIZPBnRrRyVnv-woBJB8;RWk(J3YKRV%WcI| z6Dc_j1ndQ;+Pg7N*IYi~V-c}M2R1r@GV0p){t!ov@GcNp5eX9lM@AzR!vCOMheJb4 zO+B~fHMiB$IJ2*Xk9L|666dwX0n(e&h$p|}=h_-yzSylsys#D5P;S;c$#;f?L!1LJSvj^WU`qE7Ma&@f! zv4)Wo|J``~a@i`Y2W*!)w)F7%6c%TMP6I6tS4>ZIGQ6N#$5WGKuu>==fKw2uRf2Pu z&{i;T;TEkl1oT^cahyfcVhq=gMkG@xGNrq_vsaesqH{Ro$8e7_L2H@Zu-stgnmonv z`D7_JkDwxuG^7+hZ8j|vEiFr2fnT#`>oAk@&9YN|`?gJb;IQ&ZYRB7xRpj5TRZJRW!1S*ZDlTdqyu3-;{DK1M5kZP@^ zUwhJopgMzNb;eOs2AEWN30X3xp)=;2r^G*5ovA|}H{MLOmN_nHV%Ku6hwr1%)qJRE zupePx^Qy+nv^CsXHV3ViNtRYs0}_Eq$g6D+w3X{J+qfARE}(og_|GVB@KD08f@VZJ&(*e>ESvU+o)v4WN%{=GYHrP> zPR%GK4bGSzhb3s;?a(J~h@f8LEi>W)0ITd^S za&%}t0gG+3Jf;S#J9jxuEFCpycL%c2(Yvd$auv6Car_Z=W zonr;hMfhav>V^><84os~3-?rceP!2rwzVP`cCB9QLNiorDTHjK5RV}&!D7h?oievs62Fy5XxXU42s0gCxg1dj6qX4mj=1V; zt0>KTFu>1lx$k>l*tbu~$o#Gezh$R`@LoLu>1$RDSy>%A z{f{`qTc-i02>0?yBOz%wNjRBbnnL2B>)43QZ9*PS1vgtRTf9O=ZnSo5$`=~5AWr2H zEOU^*dBJ!nZ|=TGsPZ31pZZ}kb@!HJooWbnU zoA+FyQQq>meF>*B_dy?sCjTLLddER$TCc6Eg;eInVh~c5UZ%C!MrBflsWcHD_&O6! zJBx`EqgoY#&}`STm~FD)e zY_CW=T|aXvn!4aD>0BmKXc}7Ux+snjD`K_gEc|yr?$r9LlxUMk{y1!WeVP)8F#eb@ z8dZ*@)mrUuh(W4PNl;AUi}Jk>hpGs09v`ct?h{RgWg}5!ee2~};Du~I&rzDwhD}wy zbTh=w431})JYBKDR(yt$OK7VHeGT6iPN_df@=~m{vhlAQP6ZqYH`Sb(SM&5^MSu)i zL$c}o%_khXX0}LJ2C)=*HE8L{&TfRxL;o`hN)8%CJOpf_Gc&vGzLQ+{wU1#PSA&7?Yr<}-chk%_a5pFY%`GP!#aLFXi$@`|-OEBPr3T)N7{VP~ zT(Xblq-T5vS6j>)d_jCT&~ty`={iSXUHWsebkC7z z!n*Y;YIT&ze_CDV=$U$ByrDVtyQlo@0+`16JvOJ-okH_ushUQ`=GTk8)|8g}=s7ZU z*&80KJl&<6Wb+AdrweFJdYb7%X0g|?0ZhqG2gqH3_EQAN|ZEA6ns>At7# zDoB0B1vb+wg=aL88J)oFX+;DJ4XO85Z-xl4D>kn(J@wkPoZG*)I!`|w-1wQ`t5!`n zzu%!V;j(%DYkJA;z6tT-oBZ+w=L>r8DxDu#QMI*vI-h1=WuLW`?qQx> zTZ&hUb@Kk!;U-sX4$W?fsZMOuQ}V$=7({Y2*>hvL6R%te#027)1#jqwy20Hg{^L0M z6wpUVdkf@z^I;=MeTUhR4Q~4KYXxyntja&GD&UfeKw+28;X3=9_|x|u1WUb+`QAS& zXn#JNbFjBr;NMZ|6G`H4j~|Gw4m{LOe$V)L+?QVVNLBi{h&xra1nNoJ7c!H`sn5!PiZxSBhDlSqDbh^u-vB8GVt2((e`bNyvB7u56!{##TvBm1mqK;q196N3rWj=@C8eVjGN=Y{ioq{0UIYYs@QFy|=++ z28UAGZOnfD#D5oPLUF1y+%L3y1m-$gtNX82)=%&^h+1v6EF8*__=BTXt><1wYvK&- zN&Lhj-z%IbAJM)1nwy;YC>82qUuHvoSo1wT3KqNiv2C#1sO`B)tESl`E31nhTjb}w z+A|X~;Dm;cnt{g58!wtcUL@}dq3(Rr_vwk)&@sh9!g8xR6VbGo7<;5M=Sc)Pfva2@ zD*K2k-iT%I>s2=!&&MSNXd2qB#vgP%SB%0na1u+6V9EBAW+^L>ik+lGWeo4~*utXo8$E$s$g3@e;$)KwyP^eWlng!vxl(e; zRLV_;;ztE$4qNXWtj(2Q53Sw`b!fiSZ7Hz6!eZ*Ci%GqzwQOy5i^~vsqBUv1Us28L zwH-mYu&SLY%6hjTrX}2g_)m>1lq|iWV(or`HO%*#2j1Wk(o*m~%|D23nG97EfB4J& z^+`*%5v}H&h=xJ1nOIEmxR9kciJe`rYr1VH&VuU=`&?2*+M!6M10TplQQa~k&&Xfo zG8?9aX#Lny&dfw{F0J==WK{|3y}$lk=-Ht0vl2sNW-f!vRu*%v&d1q2v?-5^MI(BB znkM)#YR8vdy8R3?j=l5QLDT_DEUZk~uI#VzGvi20Kl7D~Sy06&>WcL9Ty;fWxj|4K z<$C@ir9#@f_SO(~@iX?!x_W|9_Z@x7oGDz}7*9c&M~#LWFCS!#reRrgn~3OrZRxCZ z_3o_VzEciy#F>30G_yuGZCY+qlDy|Y_a2F!A~C;z;PF~ezUGqBW-y3y^VO!Y+FBdf z&5o+x6QlWP4R_Q+)aemBi-a~7Z0qHKw`aSa0sF8=eYRt})5ndWmFmy1Q{HSb(;+fY zc?!{Z@XOa}V#d*N$?pXaHT$Q(D?dJpm)G`nsjJzvhQ}^_RjK8qF)|0i;8sJed7nG1 zh1fBk=^a!EKgTDnC*v$BkXBJcOmIJXbNDWk)jXgfA@FqBo!qs|km%iiRRfQuczL?A zNxhMaqD`oIJ?eMo``-BJD$#Qclg&e-bVfzikFhD4Pl<=rD~CoRX7tc&WWa^p^+{gp zcIK1H`_p!)TjuuLz16~KkpBAXKEZFsWRwlBpX_|<#OgJDYdI2ea2I0@-sDqyjP|2Y z)k;mZ<$Ti89W$uqMcXON&z#8@ES1O^?5u)aEJ>#U1S^(DQmw(7?p50EFS|4eX5IwT z$&OiT<6I_HX}^?!hd=~;D#!xlFou1Isx%3!aelzv;s`chmN3GR;Hag_cA!OW9S9uP ziV=3ioMvU4>=Dqd8?;`q{@MBnBq4=QtQv@{%3@ed(fl)A94PL}HKma&mcr0*hbz50 z6?a1V!NzNL1TnV;>FkHnO`}?sVA9DA(!l8C*{eNy{}fgLMc^d~QG==yL@dRKK~+@+ z-eMk`ckYhog;B)EN@BGd{<>@*5S%5onMC0kV8<|Fs|r0qtcP?@7^ zOs#d0hU1f9Fl^$W%zHS2wc?fZ}_(W#fBpL~7g*R&K zO0Us0hzo^@wc*_B%C-4ReKEPU@8JQiy8Njhb0=d|)$kvwD$vVGfcrGHv5;!Qb^Kd) zSh&CTrbG_!FGu}%#Vu4^-Ib9|6MvPJt+wVytwlqlI3nh-^60yESUY|*0U}biHL!Iy zP21pTwUUr@>12qoC|3&0L?}2O4d)F(Qs36>Rg|*r=Wke((td@iXW-kSs`f}o(&mTF zDzTPmiIucn`@kw>xkZs_D-XNs8*REEFINejVh5AjPUVd+M93Tr zUzq23DH+A-EdvXv)bPOGDF)tEbzrv33|;9!S_aCiMv5C63L{>%0#4m{akW7Ot%Be+ zBR>C;&9fuM#Jtwbui+JR19Z*#Qt@%%1X+7LaU8Y4AUFaH%=IxVz@w;_=%iUnFWiLZ z*-QmA%XZ5O2GwhDP=K07`6{Dy1)jkIR5eDbAUe751Hqa-kZ{?NR&$7XvgIT5f=}uf z{LC;#K}A7^!m#PE;G*v+SfQ_~URwn~Vv}Da&&*8aPg_n~T1`34fbh&%nS7F!KdY?< zg`H-RyvX=cc2%k;Vo0yfWe9aQyPK7t%|^C_1dZOAxqZYJOn#f|>k{WXq(Q1HBNumd zN>jp~QTbvSWj?Ck&Ql=Rm4ic0$9+DLMgfjpI9Z_5j}#u84Fj13fi-JSe-GBAXQnSC z@<;JT1K&|*UNh>ENdwTgB3iuSXO5|~DsS<2%WeG!YN>)`B{^_x2&F)asq%PMR0rxNegZ5s^5ohOcbEqr71w2y5YpG-x^UzCm zYwW&QrJlX)H*b>79jw6KMZ>oI7^$9^(sQu_qDFi&A}otxyXZuJ7#=iN*?fZvVSoN8 zdp6%KHR>ZgTdh!&PMus@AhC*aW1}V{geCgU?JI$2J9;2v(F7_5-5Uu{JF6^odql5N zt;&TKJIOV>Wq&)cq(+@f!@36dRXlAcKVdP_ODbg4u6PY8>l*Gw^`|A)B;smYC&;Q~ z0=Pq_d=ZJYzU+Dislc2=xB?gyPyH&9aaJUGGjgzijNGV; z2Evgxv&Kz0U35b5U;(IQaQ(W&*sKqZl0cgQP)H?ox1`xO9+9X?kzZywQ&b zeb1K#S|Pc{xi}f3I>8(@AC@?4>LL1cWUqK!xm2pgK1pYqxmJ#Ok>8rcjK3PR zz+Zv_dR7Dqc~p`gi==ImMkgXLX*H98BSPp(a{fcI5ZqsVwYQ~SU0eO)?pO{+-cp=x z2DT$0e64Uwk&?(vqwtvU=`#(SoL8T6ZOk+LNO)Y|mL4B`llSNQ@ZYJxr^@&y>}(7c zX1S}GdKZXKONd1|oY`M@Pwi{ftCg4bI(k;7qgWlhB=Olq&a@A-&#iN+;3JNuR+S?e z4P73{XDtVPtAa#j(-kd|S+(4ydLi9T!Cis@izOkpbLOgcpBOcU4r=z!o8xym4uN&| zdL*yX&|LTZ%4?Eav+4(PCy68lqZ$NMYJw}2o-dV4lG`Gq4ZlgW8(YmX3aat4C`W0u z)w1RGwb$Q)rV$;Ugz}HDXER#3a=;gvlFy%D=SRO9wPHoc;#$L@zYv3cs8-C4YJqc+ zo(lQQSNIk8y*#@SEbLT=G1G@5LPu-)>N^@ES!P{jcBe% z+8Qw<*1Em$bS_$^jg_zr}0wPk0mP>VbQveo2!5ZiuGm70srn z2aGmToA5QNn&P#Etf%cv^G14WvV(TDXFVy$aY}lXOCX~Qr^3uu9B1fMe#v=8s1%$~ z*1)xYwz~-J^h!~L#F}xiIXO)MF+YOr@m|QZie_C8M8L`BTq@%(!4CgxQzi!@GiFgRIiv9WTVT_HNza#%pTkN2<&O4T(GWCvS=I`WNmz zqp&549v6Ev&co4W`Fj^B5NayBs`V2*Fl!EM3|>0d9*ae*_ZarIh1pi6r(^ z*7u?3jT-2rE!{sHsgaP*jmDSLo;D*VdA?pislAH)qUl*`_PcDYQvgL{Q{<=dgivgq zWo)0RM7|_voC)1CH7VK1^EU&C-|Dl>He9?4f>Dc@`GPRI{CKffOIzU|%?z0MG~)Y$ zB$0+VBDf;McvI;VM_47Mk=2uEY31^)ySVEi>WJ4l=PAEQq+iusynTWP(m^o?Pt#1B z0z=V*JB9n}%ad$m_RUJ!lH^264isgz?{Hn!3_2l!$z4e$vIz%&p^Wr=rx~Xiz}-v% zG-ONAR4TLcq;%$`WP!fP?3nW0+WN?d>YCa_L3U&=lbjlByw=N!D~tLgfwfyDI4(8$=7SUAUl2eaZgXpGtG+ZO8a31sc8>-fW2#84RgeRXXP7OX06pgLXbayrp zxZLG72eD;wK#zGVU4@YM@OX8qkOz^e?MP-LH>o}`OeZF^KV}H?spaHTfYVLqtKHix z(;D08$j1=>*BXIz;{L68=Qb#cT@t-mU;J@&hG@#X>G0t_+unO%>xl1(hNnLHkwdX=%Igzbgn7vc=?MBv<+(i&@ zHeW~!c5YRWIsuc;)0>kL^CUUFqw0$p=h3@~7W;@`cWtdeMZ6l4XUeI}=go>a2d6&C zxG75tD9?FCG&%C3?|g)rY&b=>G^~`toUO5u`07s*)QQdBbt#$)T0wZD=8#iO2#b$6 zm}MlEKfTQc3pMYNy{W-n7+&j4R6>d~kAEQ^i3~GTNnTCB|8As~W9>J*mt})0##Wi< zX_#_2w(g57U2B4$Pm@8Xi1Qg zfhgcNkyl>{y54BFi552PFzTPxyI1jg}g1F z$Yt>-#&3V~%u#!>TZi-FHQdVM30-UtM+O|(_Ge;or<(_RZ>RQFh}nB)8a-@-4Cv8~ z*F)2I^LU$uza?#X{=2K2VmZyyJEyV5TAQrX)%}*#c+KiW+bRw_M!v2{w%B`K*Lt4>a^X<)n-Cd=WZQV53`~xx3Q}KP6B7x+qRn1 zV#0zXPes0>rVP3Rn3D~h{N}$bBn;w#S=$1oewbgzZoCi9Ndsxmr2Zt ztK%eJQtCvxI=5Sz#?zS7^)mC=jOLgq?XoA*NnyMt{ltmVOvzo$So$V}3>B1QN(nAv zNPUmhe*r%CIJx}va(ltoLFkcuon4+4G{4?6MtMet+X&&piD3IWA#aJUFeF!&C!MWQ zqnFX4kZ#voRv(rJMxP6RD=ty92+1@B)w2cfk{fBtR;!1%Df4>ZdYEpF!w}Os7(7gB z(pGLy3^8Rm$}fB|-eTl_4X>a6l~7WP8TmefpZuxh(KZsQF)vn}iP><0j<>EQZ9a`q zKqW=Vwz2?}!S0v-9(j~2>eau`jZ>G}{Sa>VklWH$B*SG_|BzDo@q<+>h zh+oyqjjN;!$%4qXpJoz_&E7*&Hu~y3M_ERjy;Xh&7BnCU2z~NSBTD&v@k?r3xFvKQ z{U9^b{JcBuuT&x&y1WfcOjC)MI%8qr)#4+l+W3fKDN~~r)*RH zeJfxIlmqo{oBIv2O4Eh)NLVEh55$JTgw4&rSy9p!#|8~7Kc>qLm(Ez6)iMuH6>!Qz z=DpZ0FnanF2ewpGM4#gc3N|gTS64+Pg;0h)$q5d9yvyLLBEb}gYL;pYQsBfAVclnl zLeb#%WB$2Vp38Md-2I{3qepjYKj!2j%E>;N|JKMn2Zpd3AWY#}@~UU2!GpU+EMW$1 zqq4nKMJ2R8j7wHUR|QwfQ-L|bTk##J(@D@VS{zX;y3t8iEXHyPP96>8qOLcvMO$&B zYdh(;!|JjZ9g5Il4?Vn!<5WKNnV~dTFluh12?z|rc*{Zyjv1h*L zn>m!UHif^n0J_$roYyVVd)@2?LcO{+cs1cC0^1JKxt}x4_O!g}V#oj4sP9XU(*|Lh z3f>qI2tEXyxFIRC6=_hdLg-0PDA4@f<*T&IWbcNJd*H#d!Pab}r_+z@GHS3g22E`t z7N4pIi|iw%{CM9-h^d-Emj)4gvOXtT{Pm(L(&OM$rEqUg6@w>@mS6R8m0>R}`6SwG zLw?iZ{IWL+M0{R-!_S2>sN=gm>%Qzq*D5~hfE>O4eG9iXQuH@06_?8<2ayQRRdlBU zQH2xXS$mr-AyX>4s*ln`Kb;1Q>#Ef}m7t1A4W22(Lp5IVu8DGxugDgtNDA}vhC8x~ zQX5A7EFQAWAwVzJMpq34$D+8NDW<46Sd_!?N|l3jV5ly1)IqqaXz>Ll;i)=Ir0^+j z#M`X9FC%2{#R~JZ&+B|5<*aO{?epwQeL z@!#z~iTG{5Qc&zx(}T`ZkY-5x zD!?;)Y;XC>SlWY-GUJ#jHOhAHA@EHpdkGBP$E-#F)1%UfNCfwJz2_sA*@af>`!2Lv zb}Ukueljf>ls~8PEB(g!wJ%0fR?y`0$NQ$wEDrtEavl$C|Kp^xbiAIGU|+_JE55aDjAdb$^RJm>Vr z#2U&eNZ43#=vK72mTBg=hR=Rg$Gv?A3$k-$+&HbisH;&lNu?jn`$$J&am)O>CT4@YYk;GEM&!hQHIcGby zS9Dqm8k}NA1`{4WaxznxT2h2^M~==2&)V~+bQH+v*HQEa%0mb`#(AvFM^!=A6I3r2 zhePG*toP}^v%<{JlS}~W@Z-z8@jy;-Hkv%L*#;r+FL~UNAHJHAzWY6FCZAPteGe8) z$6U5%ax`;Lo{*YK(oGmMe0<|gILXfZM9EviBVGc8kZ(_<8F;Zq4GyHKX20U*l0sOF zt)q;P(DJ*L!NHb^R0$)nH>@vQcJ?VI+?cbDp|4U8`@ofQy@HY6@8LDWG&6+rnLStQ z0uiEZd5YzxQ<7NLU{y)lZo#EUN~T_za!|m4Y0z0Z(GY)D#Ha0Gk|4&j#n0F6<2jXg zu^L|jSXiRqB3n{1N9cls=`oML*;AE+AzaDvp|Z{=Diq%_yMKspIHffdTb+5n_NACv z3HhQ;nf1>ZnIFR9AapBSzoqOUgW&j<5k#5lVx$5ykjJ*<)j1O70kLI{Mlh|c6Lb(L zjIVxR5EvFnb{CJ!vPtpn6|mn0;}T3lrbzJOP+(+>(upo?@~n=+$(-%)UOwl#;@>?W z+H|90%$%lbQ4ReZWrV{YeWShf(2@kLZ+(jPUKjH06?_;DPIVU46W)$Ck)Ord>aUwr zn_1EoQnf&^D3D0G&}EyMQ`X^2I2(vsT>Si9RyReL5n7rTf2fQewJ+T>LRInFoCRzd zPlH?g!%{ts0$ak;szJ8~OCBngptS7B3KLHy7gzXJ5c)C1QeiaShKkEt^%aPUQs-3& zE?yrf`Ijt9+#m`YH9|%;{$~&j(kCDO{iHbdEso?<5@bPj!LxdD$=$?5F2cJo$Y}ga z&UTjX_cyd)Ul(D2MNaR68z;RS-tvYO zXsQHmCQoSWcGIxM^4`3y>;FU(qzOR-+#RqRNbuB5wk#;)mt=wXP-JXiKCa3W(@tB+ zMr!>A1$%1%#^YK86(*GgIf&^#rw1TdbSo9ERxGNn+Sj92;ZN=kbcaw;BYO zPIFh6ZC_nFgz%3?lY4W{mxlkxcjSXyf9h90pUqP@=TRzR`D&^q`Y0)83dI9}tFQN8&5;WlA zqsO;^x~1rlR>^I!30 z-Bf@KCO}kQEpdC!3Wn3wC&NByNSg|Ox{WO|+UV>ZVXU zz)kxyNaM+y3afIZF4u{jfCtea7EZN8%4kAzE)s!bfCh`$sRKXwioxr*z-nal3NkWO z;)EuhWE{1L@!Ih}e%qi?`IR;;C%_o?tOwwAQ$uxUFLZ*JM&6%>%vM!CG2H%?Bv$ss zaFL2jO_87vt6++5IpVv2xR`NCBio_`$6T3qtW1w&c-~-|uD7mrIj64kOjbfy)66kzqo^q; zGMmP+l}UcnQ$J5tpO`z5>D>8`&F|I740p9@|6GncdN&wlqki&kPsJhNjBVcqUD*Jc zrzA@G2cK&TR{Co8*TTtzXXiE7Pb-_g=F5j4G|MHZ^cp6NGv(A;$ek*ES&T0hT)@_! z_^Kt86+}|*S0}!KZhx5AtP`(}7X%%YE*c%}9F#FEsV%LV4N~{vtLmh|8{OkS2B$w1 zEBs2M5MUaWG{3->0lE6{igk#Q2^D50Fp|@!4?lPmwMBictNpG{ngf#}kgByEGLuYP z7jloek4cD%^_OnHm=oxe&(}$ajn_$_L&M{!E__-`Go_2JRDBVIX7&}8jbb+rmkO>( z(2K1ikT_QmMFTS1fHj%3M#>e$_kEhI)pGytxKhWp3n}42x7rE_hImtY2A~MX{R%#- z+eTGF0}Y&RG$1^`)l#rG z1nStZB$^u8d;0?QLbtQiYd-y9woT7 zm%SH7t8;8MXeD5NAo(X|v;2{s&%aNenY0SwzZj)XMMOg;?=l*ydpBlfZGYh>bxfpitF%CH((qNhG_ z?2@+Y=&TT@BOv| zZ14DuiloK85>DDN4Leqvqhc(+F8U#Ac`$uYA`OB9JQeYvYGjhIV>3m`Gz!-Vwk=pJ zj;)_^!_5M3t;|*O&^~lmea-u@u#){V4Q;T}PTcN(9lbt%ItKgXpR;_BQu&{vMtQHI zxWmZ8?E)ajoOX_Jvuw@D)6VBnIHuH&)uChQ$`QoEs?}UhKQh&}K1V5&Hzy{ME34>3 zEGwN9b?{r=JEe=#Y^Rd4MCtS>?km~9H;1pyu0BpA|Jpo1&)$m0+Z?93xcE}mv3T`E z#+{g7(e182PsdY+hndeJY!0PTd2K>zL*&#B)>JOi~#l!*TVdWWd zGc8K`Wrvz<2M8z4mSEDT>2zj~8Ks=~36kVF5;Rqga!S|Nr%=^{beN~$L8b^<6#N_v z#H&D&VyQZOX5_)osZpUo6k@`XxDko3E61`Zzj~T0gtpygL`?`~o6}_)Wu(EYQ60i$ z`04t!l%l`8+uB2W+j&U;XUMsu;tjABLv+dhhR?9QJg~cpUIt4N`E(y~WS!|0GdNXM z5Cc|kq$JByN1w>~jRci4e`NXj04QrTVxw;EmwmRnzV$9{@@$uWU*c`YuY~HvhHB`_ zX47x%LQypSt0~CIS67U`AvqF~d?0^*HRxR&@vB+CYffpmbEYu!!cA#eT6u)TkW~wo z?XeI&(R5Md~6cnJmqQQhN zb5}RBx@0&Qv>1yA8H_9pmLZH0Rtm~|6(~#@WQ1RkuPK>iE)S6rA#>%nkNWtbTluNz zO+k{G;>#N^5ABx#^Z?;bw{Gy{4_?w5*fhSB4o-*K;w69D5U&X+437i7eDVF9swHg@ zJzz0GgFI@jk8DiL@qgim=0a4L)E`M#r@NI5?QPnrHl@=(Fv^CU;nd5jq5TzAQ88-$mxL=R=Y}u`#L^rQDv#vVaeVR&ac3C=fAEkH) zpTWO!1NaT!k+C$CfhV&3w#}AMe((A^zJ!{ap!rrWTb5Z%hB)BVz)WqFCJ3K7G!@wf zKVG{AH($X*jxZ$}uQ-E~t5Li4Vb&?&oxU!&_&cRl{1j8Bo!oF%YzRZAY7LseE8QOI zGkI=$5UW~*OuLoTOC3#!IcNGGvX_WE^?nys&6%F2*~(5`xIZ6{2~nS`Y=h_cdhg4; z->b_;lDX#ev6nUv9^5|m9;ra)@X6uL;0SLaLD35hTn_j2}yCRF|} z(LzG{BFT`f61VH7eNBdEsCSCiH3;GDs#|)TD0(SqqRh9dDOTC0_YL)|tEa-n$}GbH z+5Lrvj~VPs%>bLVCwjhtS(-@dr`SMQOKkHairTpbYe;N)#*jb5+z1>jX{O3sJ>9@Z zoz|bV^OMu_N!ow{aKaouGcITl*AiP5g|slzyoRdQoN7uwT!(3n9>}jPGM7HX@&E#0 zkIY0ePq)Wep}EAT2#mRWTyzrz{|gviNcdaC$2TDX7a@z}0CxHS)TTZwf^PifcJ2x* z3jYh&*KqIZ!N%PCJ6;KEbghq|B;a0H?x5M= z5t8Dn>0t=8Ld6d^v8kb9-YN0_#RCMg>R?s>g1k2oq4_ikL)Au9l#<+jY_nT4JzP%L z@rgdoPoW8m;}gOui3P*733MP?RP3B$OjAl1bsK`02Jbb?U`fEHz`8|}4FO3T@^1{J zpXCgA=%U4E#%BIRjw96XH{IYGH6`-qti=#e7gQ$+D)Yg3ym_-kJ}3SvY2xMf+0QCF zui`EWxG=#d-H@K}ks{t{oOn8pRVPMJ7_yZ(jW#)h8rO@e+p~Jr%M$me!7eTmc0r$OI;rM0yw<(8GNs+SQn550 z1T%ct8lEzG;LOb84^+Oyc3U%0=cLuP!S2D;h;&+G5WS%o%ah_!cVhgm_-*4>feVXx zsQ6i46GbE#9e)HkkdrnQ7d7W8*hE1>dXxmloj}E@VWV4(5p4aHeFhf!Q~1!#v_RZL z0tLVl*=gbgWDc9oF7;JdSEt8Gx+=*t5@aXTjPdqLiS@dAQ~%Vwf|N(kMMDCt?7@t% zg%sQFunOMyihM2#&y?VE8B|2#I@WxSmyb-s@detR$oGk|W2GCU47w(*+bsRpn>Z3~d-NWicJv((dt;4^v6h|GiF=34ErJNHiwm=FGiFN@Kf~O^KQ)U%?{JIJ-{yrzwP$P`>?u7K~E&W(X&FzS0c9(3sib z{OS67Z^B|dW^en`6VeyZKT)?|c2Xsf2V^G_EgbzRpa>bYE#S!^W|{4cQ<%&z_l~Hb z+L&s6w5-=x`|L0&0dGRF&P#s_^@9(_-+@Cy%7&m}!x{291Qg=sabIr7-_XYJYE8r^7(TpL`mZGe^j|qzdc9kML7n&uabZIOBAxHIA$EM!YhI={ zySXVU{A?Cskqti|6^b)Izxp2wk*8VyH6@!1$L3@8@< zQpQR@yL-Z=cE-5q;}_gLm}zu974IlZp^1qhVeqiBIiD=#$l7So*o5NW*@S;dt0*&2 zK<|j6ap*X+{rrav&lrWE#Wz?zz7x7MY03uIl zB;+zEJDC#;l*8mdCDA`t@(2L3ebl2P0?A}xa3(H|EJ|VY2=1hnDqP`Zx@yla3PCkW zf=^`<2no$_^)C=;kv5ZNcwKmx!1P>vA$K}7X~0INT9~Km$(z}~f!4pC;R9L^o+~&{ z4iO5gqcKm;G)iVPW$QLzNTlxqO!1p170!+_+HTxgJ}wR76&WfOWsn`(T%E;y-&AhzG>3~lP(-p+;8IhV)f{QR>Ko^MmMvV`+k8Gf{$z)2TsHXO@BK_sNqFsrd>vb4wTr5olW@7b{zXAg+(d_)1Nw<3UVwvjTeAe zKvA}{FaB2>od)(@!9C$YMrvT09*J1oeA!qdN`|5D@}V;M49D$GF~dbc(W(3x-wt90 za@9qjE4%*>&U|5j$&cyFU$)d~p;A%d_c-y(&?Vu>H*_GN1edC+ulrTZ2@H3m;cu1X ziq!sR^o-7I$;`dq^=YAPeEt#j6rE1-r1^(9WD9yRnLfiZwml_I5-(81_rLd$q-V3X z1cs(c2^X`L1aNhbY9uC~jnwhD(Apy@YyIz&1%dE#P*r{mbrDkseB4F(Z^J-Rf9ps2 z&X@qAa@HqBZq-wWRAli#9c7n<7g!#pwm6C6zR5^XYx|eJ{pW-J)y%(VRQ~m+FFkS^ zT^juU7)F8;|4|~Gl7-^NxNj@KwB8q&5C#lIqyKb}9X?dxw3*lP8R`zg%_ZIUP4?dFqhiO^Vs5V7M@^d} z;CG^_(sQ<=x83W0rmPN2T)z$c*~N8w^F=1}JB6zK>^#0NloVpTxQ2%sEIkZHKk4`P z?Iu)fQ~vviNYl@+^&TT7)?RF`af;1-$9WXF(`na6m(Im|NVxK|uNtKP?-SRMkUm&F zJBLn>`s&4e_M*7Fi2L^O$+w(<2?McKTCPIcTE{K0#psAZM^k;Rh)^&(s)n%_<^6qV zN8>!-yi0=x!MMoPo|v%N=C&dT+TSbYWnrN-cAr`z%3SyJc)dZ%c*VQ7u)}><9;&la z?cF1FxM!))f0{o&ZdZ3nl%~F#KbfBN#QjdZo~*p8Z&(3@=3kZz_RbZIFZoj16MDm? zp6htIFH})r`tKtqfj2Cm{Q;fsk5al6zPg@N-9E#785HtNB4Ox^1l{SrrJ{U!?egPj z$m;Fv!t(y{;dz$N-45-kxp0fKy@u+7P1bJ;ys=d;ryI-n4H+WMzz@Bl*fl2G<^3#y zjPK>iGv7Az({CJgfh%ZwK02E z2)Fhs{IX~?r8B13_{0}2(rDpmb2)J+XKr#&^>;Yr9R{`wKE8AyY5l$~Dj&?i{_fpP z&vT`Azq(sHU;x4MACJpAyzH+eTCmC&efZDyhw*tT--&vUV0u_me7zMr z%6uw!b*}d$Z*&__zI^tt%zFWqqm+V{@wH|VaPr?v>DA08EFxI)%`B^LFYN_t>TeR@ z06ZQKykknp+ybMZGW06jZLI z_snULPco%#Bsn{0=@9nlOz-2+ngldY+ZtKd!n=VnXELFZT*SuQ?FF6#;^1J=ZkJ4# zv!nQ=^>(j=kuj4xckJSz(t-cI{4kFoAUO6-KmQ(g8(N!bYAsiJU4&~exy@hsAWc65 zx_0mH@YQ}j_Kwq`uM0-+`|T{@kf-vq`|Tlhw~)89?8)AI%b{1vZ_T*csptSrA*|l_toqQw0d*@Q-c9k6C z;ov;4cqp}=?NXB?i#pfe3sHIEV=s3(A$KEIyiwbo@3})vO(ShF+T#e!1poW1$aP?Q zZy7!sqGrb;v|Nj6IIHqnePO_KbgTPGcj(UElvu|}_04+=?~7&)i-j)-1rbYsAOZNd z*zKXr1#jBLi|B@`k*0I4=W4BU*QMLh7f#6|4xH{+!(!Jnc>C+FHWWHT_% z-(lp>)Xchx``s>n9K?+3?0$mIt)`5(YylP5Hb<+@rgh`>fPSl=wjKLl)%}XB;i`7p z8)_6KI_~KS9^1pb_5z+6Z_Q~slV(J3Ws-la_dlH?A-%@?0X=7#EjyXIQg~0s`;6zP z`Sy$L;vx&eVE^H&-_w`6(JiJT?)|rgWajPv zh@TU@`2SY3Uz|0jYr<>aqv|{OwDf0KUzCqGia2XL$2vSbfA}RPy0#=@I^4ht<9iPv zYFAO8yy)^|0#~&RR1<(p{5G?pK)$J?)OcJM=7UAbA)J*V8;CwQ@PF#cH7 z?{9~`A0KmNqbPG{M-xGiN-KVcDeXZF2qc8ce?aF&5P^Ml((bavSzv8 zaM^aU_A5iA9}^>pcNMruXCbrvM}`4)gA1let+$cQrLr~4R-Gwp#`@VgR8N|x%kc$f z{coA$CUTdGnFyP0{(_^8F?apitDnifZ{13E4w6SUqWkc+GgV8T?f{m<_wc#@eqna9 z>3cHQXv<3L=D^&M(lg)H^rX^`1;5M9TF7eCOv}Pkrj^kaK_WJwn-t(|nHeoKRiV%O zTUnP);|9MC{xfVv?f|4Em9OH>Vrp!(m&@EA@sT6;PXD+gKrVkkO6h*lViY#l)^`8! z`?4DVyP3Ad>`HV8SPdX%O#6LbEB{|p75{-?uTjs!`-8Ns;yDN*Ur~^tce{C)rJg$OYt#Lxv0=nTzI=!ZQLw187+USDmASf6vDvuJQ-#Uat}4)i=&R%zb)Nd^wpD@N;lJ#@@^33o^^rkn8e|U)7bP z_0-y$fj^Ly%EiEM4{GEWSqHY{)nDBLnH86!LjR==>A z;BBOLTq8A#>0_sOl~NFn$zx8u#_u-FhFM^c2!K+7$Vb5X?@yV?wc0-d%II-8+;)-Y zH!&~L-zOT)ac=qPO88-MU&h9V{-1ghK*p)^I}X5J%wxiXcTNtKhF2&RoB$p0xq*@w zj8*FbXgGg+<_o=oXSHXcQK~lH0&gYd?|Rc9(050U8DfN}atod2g_q6c?}eFyP{t zZj*`~8C_-eC*Cye6+e+8&%rMPUi#|&aT23G)NYB(V=$(5xJ(w0+gWB`=?HGwL zajWW*{2Xg#uD6%xSN9L~Dd?CrhGm$_owrN=b0#Vv80Goc@!7^>AIjDKgOHB9oh5qF zq8}r+ppa*QGry}P0Lj?!lDA)kVM+@t-2sCpFQJ@iH3(?EcIvY~eXq|^Q;SM$qA8JU zy*8Y!RCgH3{lnM->^v)P_ShPk(jP&Oy zt4XW-{FYse>+5L$8^<1*RG-bac@kzO`HZ47scrM*x2q1)eh8qg3q4%3+e!lIYQaqa z3!L;)^oO!59ZHR~kpG$;KUVt09n0m#_2p-eo;rJ*u_3wb@h0EFk?TEZld0b{&vpj9 zWQL>sutMN?MkslV#&HI(M zon@VD3iJb*DV(UD$E0{YbVSz>@c66#!Zw4q1pr!n7rIv4R0iPs;lAhj7Lm6c<6MZN zg;K>#de>H&zMI?dtiK0jud$Sm%$aKyxE>&`^2*?i1zUic7+bmkFtX&jxV$e`d$WMk zA++a5m--sSV_(jQX~vdc&>F|3P$hg*;=6xpr^Rf|tMtz0IoEnF!zpV+!l~eLE#E^U z?`c=m)$psWMS#@ZZ{+fx#u>PTtSlzfzNI1u$hu+@6OSL}-vKdd5p+5gm?j$g^yXvx zTHPP+N55a0sb~1a+3#X>oJ1HupQ>6MAeh$cd6gFcnH2ssQ7yoyb9PwmH*Oc&>(o6MKsE2bG8N*9By|9z3N0Pm@%l;XtvN40SfZKNd;CuD zbfJp($^@VrnU#JmtP+cI+kDO2cYJMt(9`}1g0}7LyFVClNpcNv-$>7cKk)_lDJE-e zXXy>T9>CK|^Cs=nQ!e~ft%sY+71#jpx?yk$cx`{*x15a*!0vUY`u2g&mK4lMAyJ26 zfM@`8*uNEs(t5Bl_?GBdT7X}?!;-bkrQIZ)GLrUdXQ)}KaeHaA_W7s!R@F+f*Ui7a zn?mrO7Km1c7S)Q}w8>fc9Z$iuL@z4RsE!W?5jA~)`g^>%Zuwa}is9CqyQQH->bO4e zuznn!Jh(j)?KJqAP_IK|-9R97^T2jov~ll6YiTO!$=Teende5HAOJ%dAJKdh1h|=Z6xgL*hSfLU2Leqg4QUR(;pFh zU1#3#9`n7aB$$&UxjBcP@(yHIMntDBO^I+X@&1AOra?nsU?!8G{h!m#^r36T09)k1 zBEB8EZQCS&`2M|X*>;!A`TcO^U1tz?BTX*A-=4Sg7XG{j3|9U;$j390!)d_27vxt z!bzV?+cyG%?sNo$^8pLw7UcI&)SF5cenF2A-FrKGWTL z@=a;2i1+R-;StYw5}zhp6}d@z-4=?wt(JlwDwa%d=c~fkzkGGeOXk9U{)r`G-jPOa z>d~#;uAlhXpmCJmD}OgFIfLA}9iA)}D~)YmCJ%2z4qv}_1XL99cv#)Xpghd|FCrkh z9wHw)T-{J_Dkim`tGFuWE7qj3TDJmnCeAwYJUPz`rhoI?*B9{R?M)5?hfZ2EU9d$} z3)}5F)0Ns0pldUG7{qsI8E_*v)b~f2AUc6Ocjr7-zY_Mh^>YH)l;6h@a6$e}ylNhQWpw)lK&Kkfv+;5jd2u46x z8`0BanJWM)EZ%+l3D|Av@2cT3`)aivjF&+G8AV{@y00zQNa1J*%Salg^X1J{nm2N* z{|ak&^!nvucV9Tgwt`2yXQSjmdVW&2yf*Kz-*z77Z%M(=|EfLXLC4FH`}jJnf%-x2 zb#Y?R#3gHn=MB`ezL4#JnDaOgFn#{0-!s6=9$6@kS36hA;>oZl4|?@ir9DFWQm6bg zX+gDP=q75qiLRNU!09dVAL)L;c~c!tWagKqPfCvRgOqflPTt2ngovj=kM3 zt4T$gvEE*)AWm>t>e*9VqlHgLNKIwOo@)U{o<4SWhLY7%th9S_qm8%yC926tvv?JV zunOW-2h(K@UyZQ{xPBNN99?WX<`%&C*DM*oacC~V^mCMpo$Q|zAJ8yS3cwKez%8i7 zD6>|-qS*!P)1jSV`>p)06-@YOg zMVtY||IXk41pSE9v^7yqTcr$gA{2*v&+*#UnALXEt@tYiK(FqMPK~{9y6}Vh{$P@B zg8MSChqlBha9g*Q>+SYUUfNHWX(_uuasQ-NLPNqIt_#rj@%Ag2iiqM6&^h zN|#M$aW;lWZGKKR^(AKja5&?{0iy?yql?bfM@E2CQt8z;5w^B_VACf{n9+>SR)UAL zs~pu1`!zTKE^<%ny~pI}za)5)wAdexzpEIiyL|WvDSIiL%sb+YvXSBTzKp^dqZby& zFMundLszlk>3{D9Ql>ZUBw| zZuotRRq|}7)b|gA0!1!>Ls4ognMu;TcFt+E3keO8k_sGi?kJ6GgFEjbS)Qw|OUj@m() z03PUY)y^_H0p5KV0QYNvS+5&#?d<#C#>A8^Y@Q6&>aA=xVzwi^2g(_)m&oOfpu@YS z|FHQ848{FDOWUy_z()4?Y1Pi!gq?;k{c5kHORYuT4$Zu+9mmh~vL72F zzZ*<1*3Cyf8PFgN-kx~aO=>S5+6Hu7j%5V!%uHh3g6~SfT$$# z*)lr-X-V+rh7iX8arM>#QFhz=H;RR*2ndLDOLv#jNJ%#+Al*aPh|)-RH%Lo2sC15W zcXu~4^KOpMbI$ko`j^TubKiUKwbpfg*53DgD;X$l1T=OP_Y>l(_)Zk8&TQ4;@dC{h zr?BTd+y@kk%i!QCrfti2o({71LJzZ)DwUZ4BKE;N6CU6s=i9+$9hmFDe@gS_BhvAvEJkFi0_)$)l zi1%fl4!Vgkx;9S<2*q`0m`?R~UPn)e+8jWuoh_U*Pl_s@>DE}-YKYxD6^z?SpfKSpHW!1O1F$t62sTwLMi;=qI@~;+62jKn9vpF{c z9!9Nf0gxbGf5chu|9!hGY*ECzn!^e}+qB%dAnzF(Q zcG(p!ue`!1ud3qT90JBrG#!`8IcKx9?(DZdZ*HzIs%8xp~cR%?nAE`6PR?k-zVe(NA=JK7!;OgO}~A z-Sb*UABI2+`D)(MV|pInDCe0=cjQ(GPAA9gA{I+UHGB$hK@iqT}hf( zUpjd?@tD<-f`}KY$pdHA0t3Vd>musjxW?GZibdYTW;IJWDG7YMNr_1v{~vogLQ9iO zkq|eOL$?(Wuyn=C<&Vm@Rr!$#TP{!jf(PQ98NZ>EZNC>;nXFR&W%$g$NEt_>C1qEY z;J{e0QP5^)K_4-gC*gPwQtm#F?F`XGbp?&{E;h*xfFk(Fa@CrQPZ=Yo-Nmz_Ut6V9 z2Z5z!Ue#B&{=l=3opowdy3r5Zw2>072?L-vv$GeVGqEaKbzMztC!CU4F%+w;ewfj* zIf}a4Y+PpTo|YtmJ@_eQV(|{j2m8rpjzIx5{7(A;-<2+%@aKa!ypSEa#CYk)2FKj_ z#~4_#Vua`Q8THl%pp$W0ST$%NsJXX~zQcJu^(wZ*N}brf7#b9dj*=c*7X#U|^A=RH zbzi{7r1B=DH+XB*{4+0bX@9qleJg&e=AqD0-Idd3SObYJuX9^aVN;WrXS*)oC2H-d zK{U|-TZ{|7ZLw55e);%_SeKWZGhh$syi`s|qoToLZ=$)gCJx{{2FG=xs2SR&yah@k z-`h$N79Um!t_bR4srOeQrR+KYCf9vfKX;*Z-tB_%}J z8oVzr^G)}mMJE5CPkp)=dbrZI-qu-+u#`nsjl-{Ru2z%mQ?EkT9FTTZ+aLDYJ#kWc z6UWDFeyuB7LT=hU(@xeUOy4Li{+_c+S=O}m)+GG#!sS_JX zQ#Kq}pZPQ~j~A_VbhzUmGhb6Rm^7|tq$UEDulr?`-;n&s<~hpi!i5deQhWKx=afx6 zX;GBRL3|KGO!&4QHg?%1a}+KmhZ*@8E| zR8>WhLb-NMXbCUXVs3hS{?LX*e&e|0|A$T;nw}7GEUM)z9|MK14Ry2~PJQbx2T>C` zsNxm3(MiP@a|R4*U+)rNAqNC#7}ylvHa$Q-QdUP?E^Rr>-L@h?(XLv*+VurC#4Ud* za5?5Y6t6=Oa2qG(LM;eLM#KZ9G}nb8{!{X2L3^#hGFlg)9_-3QKCYt^idJ;#Jn~NC z+}NfcYq_2&V1F$95D=|sw>hMqu&y-TKwmw&z5^5$bZy2r@4E3b2-2JpP|tB-Pp=cvD!NwC9{7F4e$wQjxuS^$>S`e zES4_#?i3nWDW7ONL6bjCOhDCuF4FY!(-wnF?%Ja%SF z9?narh}sPF44w%T0skpYXF0%WV`qL1Dsh7Wya8KFz!{LT+t$UJ$~AzZqskAfN2Ki* z(Xah*5%6gME!;sZ268T;9jum8V9`LLasbPhU;uLvquG8cK5sJNtwTON0JtA2nfn1S z1wncH(W(kLqDvM7^!RjP!{r8!_Tr^JN=lGkV$Uj@s$&Ht;5h)j-W=4B$X(fo4Cs5Z z+K>>RoPuWO41jK+zcxv~57OLW*wB88jb}4s6-4bYBdFfF7xVi4+Ecx)*uU0s*9mtP zbU9_9Gw?SEv@gJ6Up>tRj_JGZ+hga+5hlQIRQibcO%QFJ(?b-@qHLn&-i;5A*j5tI zCojG<2@#`{5S6&wof8D*{X#{Nto zeyy2a{%0#aZPA2vEl;~^_HoZ@*;DO&>8FOq3M8lNBZy}6)H*l2`9Bpl5^yuH0E&3> zy70g8x3Km4K;XQNm#skRf zWshihpLa!$ze%w4_!chr?0Fx_qBxbab?XhE@S8XAnUk`b@lqCkd<9E(F90hAuFUPX zTy08QPBP#?f5b+uOiOxb?>YeAimj3uwbcdSh=~)%hzuSNxtkf1CdM*Bo7tLKnA}3l z_v?mi>eZP(GXtbF2|5QkhPg|48~~Y)yjc!>jPV}}qc5!GvU*Q&w$cHb z^P(jVzx1D8rwz13C*?2uh+r)r=4sO4}Ta5jS)%c;u;&Qu&wuQN8OZg?j>RJVnPIf$kBrYD+2j)r-UUWm?e?rxEIeezO@jXtf=ug>-R=1w>NOkwRlt$9LKM3372Ro zoGHPojvGv-ydpb>?#nsrIVJWNI-Ra~9j^T>`C2z7G3i!b13co(yV)Rb#6UIs%~J=* z4lXRab-XY9)vz7sDjGdc(~=AQtjpvz{)Ww|Hp5%51+3T;s{}zIvHz4GC*T zt_~L{@hS5JSOW{NYPgR8K&RvQ$E|Pbo@rwRE}|QJr&%Is4e$khuy||Y?sSSbt&C&; zwb`l0{4e0b1Gylo*&*wo!p19?m%ca8v;+`DncX>6kJse&)^^)3g_aQe%pO~FjFJ*J zbRD;s(Nww1i8u-qI_)|W`j(E0@?Df1o&_hiuzxEdIXe{GLPjs`BBsX%4eeX3ktCGJ znxEuqiWZZ#^iyg>-5TnBKi|26@_t#1pb2N4S}_VOWbdY@NR{B3?54e3q<$ms!UNvo zkhy{VR5Ou8ob}3yPyiNEa1|g z;pUk7#8UbKb9bv-_s`(O)CPC~{eW3MrLIg>dE?@5HFnFefIWSohhUpb^e2L;BbkR0My9gRmDs;eiEqJ`X;m1|N3}D_2ynY2S_KU>M*N;rFlxg41Lkov(5CE z#fdB}^1zEsO0A?i<2)ax!kJdOri3gn;CFlX&9)tGTUd<(#E%=?}T4v z#pryx5|k?j=T&v)47EsM>AUCH5d_id%jLqKGbK5@LeGk&)UFqHb#ksOMmWeaGLg75 z2C}SIan_bTLV05+LM{1lPyJjVOhCGvjPG6-KL-!3kdrCtMsi2Xt%~bLo^A<~&59ETK3^twK%elsd zyz~uxui_X?3rgkO08HW59jOL8jE>#?0hdWGC?*^o&}{c-OF9Q(>2$JU8Dp+4X{g@W ztxx9}Jw2Ivi9BLgHHyNTfjh>Vo(F5-LGHHIMfqHg58Fxk{(_`Jr5CJI;DFL&AJ?kX7I5I?Sx` zgS;VBet-X1S@r#LSugLSR}}T5Vu`^w!Y1XsPlRo;a(l5UfgR)(0A{ED@fG1 zKSdRszH$7!UMR>Fjkd!AkkXbW%dvSOXl>PC!*Th>n|*I9lDJ8=oK(-vFZhRS=iqV% zcXa6otTd}Ws)W6`7RJf_cQ3$Yds62Pg13}W+nMY&iY$4J%fMz?M7D#|4-62Q;p@IJl-cr!e@ zoGWzM2d?1JwfoHjkJvTgyS+RY2qHF7HknjB?y?BVcqy@EDxPc>QliMmQVHln`Qer1 zL|F7gi5gLAhl=&O-yU^m)g9uwl)-AM#2yK~p@z zHs>4YUu!iKp9Artu0Nx;ubBPy14~j`Su=@^dNmP&{e5*dlh@OLWV%|D#h5sbe!v5HLoEB z+rONF_~{4M-$)w=XsQO<_6ql@fbH$Q4yP8nEA58WS^5TI50gqVauiv513a zOnVD)z~&Zieh5>~ZXfg$;Zy>1cTi(07Z`g4f}yT1!LQb3 zZ<70=?7N7{+HWRqA@88_s(Q|mvj7YRyuopO!lnHP_DiL9Vawp0EBW z)67s+5N9>F$Z`h6qekzB_IMeTqfoA7icH}U=g9f?gaj))SNPJ{pwO#TZqC_x?%%c! zC@AFb9t@&Q8@3WsJc^7IsEHk*pzRJ0!}M{m=a2B9_@lNwDtuD&x3T842hAxdKzHpU!kB|VsedCgpxpwQ5zc%1vpUTAlrU{PmMYWL>ghRin$=Kx)wJ8b z8-n6kr@Fe^D9F%S%G9r8vVimYB8#)M_*aKzOJVvSb9I>Ad2_=vI`Ui5JZ06C`ye7f zO@F5OYRSpDxN`xdMBiO$Py;#|6K}&3ZTP8~)v345j%WF`2P{2_24PTVxR_@gS^U>xQQn8zHdjg>a$n9skD~-% zceP1ACf_rrm1dEaW{6_G*;JaJD>VFfuV{L`((=ZVMP@qFv&G0He#xD|5cIHmMb?KX zQRbUpa?#f~lvG~FRw@8FX-Lgkce-?@A|xr6Ki~yYo@Hv8llJ~g@>nG;g;-sT=H&;J zQ1?lOs0V`AYoDk22SdtdFs);1kIRRx!8eVLMdi5OAd>61FT);>Hw{raCkmdCknmpF z)AUuPCeNH#hxpI!qR-HAL=EAbtrUgVxo`DgCh5i6ud7GKE!(6CTb4q>AR3mQCi(36 zp-<1~YlbzALBV1&KBve8Z)~;visu0YAmC(LL9mnF?vFXzV{`{LAki49|&ij5x*hE>j z*9{_+FYG7m`>2jhUNn_X?(BsbYNseQv{fA5opGHsTP4W=hUy{}a=jk0J|oon>B0@I9>NAgjuSP4Hl2+=U;T4*^gEaP%K_OwMG_fm zaPE>Ci?WY#DLqu&0Z(4=ovs%)F3Og!MV1KxZ*F-A@BouXSx2j;PGUMx`Pu5j_f>q_ zufkTDV+e2&o4k1p-LOT=`eC=DjpqFMG-Y^aRdI6G_tupzIoe*~+$KvtNbEYbsxK-G zl*QsH+XZi{rW(B&YyEPtteS0)8-MF%ttO~*Ij&A<@#uj%WnIR`?4QyaXFyj6J`nQ)#G^W-v^{daG%?}eHZb!rNtq0kVnHKv}mwI#*}a72)wB~#od z%Cp;B6ur?8#h?!&AspEzL;d&6>~~_wie+f)?6TFd|GhHm$ZSqD={~W2mMQ+DX!La)tnRp6Q{1L{X(m>cAV~N(kY_VxkmbJ0$2n{` zHO)O8D!vlhexIV^|0x2+A*uLjXE~17k~OuVh-K-Pp5yfw7~gfQ{nqEkKPlG(xyflo z#vDIf>$N(g$a(DHAqlNA|2-p={7Ek~Q!RPeVA`_8ZR;odc#6Cjh%z<2?L4iHQ@m<% z^x=(!hqMJA9hUqJXX_^jA*Ay|2yBn|yY{zH)^c4FhBkfWBpf~<00ObP#aaOR>c)FxfZKilek-g0T@ zl%#Eo2s#SOqGk14MY!6bP;72r2{;ipKK28#RkvpE*g2s3J0(`Vz4K;B^cpN+S#quV z^i8msXEB|#hSHR~Zz_iFC%C(LA z7??E5jDY?hywo4xZS(NG{&UDG;_x{=MHDs2T66us`{j99%Sa8d&gjd?2UaQ}_n0yl zcZ;OEj2eB4(}F`=In7eC9R}5{w-E;fC|m7~)qXgK2X#u`;f!LyIMT+z>I~Px;*BBU zAD`l>_lwG@CF=oIOSMTSEZ}xg&yHEVYXAuy0W7kiSFFjrfblgw?o*yyMX=M?sIP(x z5o*qwzo%tvIUW7NeRoT`y6-!A2{6h1MrQvhEPCWx*Y^9T!P@;dEvNMBY8rJet-i%h zc#JI=VHa2^DBt7-c$qE}62x@aO`bj1z?2!HOJ(U4lO5va3wZW;=g!?uE3Tzd)ZCU1 z_0oTD`ps$lem9$B=bTpf0ayiZ88VCHjikPBm?kGwGG*Ix3Tk6hA&t6&w>Ci&h#juDYl!)7Z&s@7m|J;rqzBWf zy-K_n08d8Ty{OYdT>N?G_6tngsb|`fdY*8 zJ}{mtNgLvxpRJUEu z@AaUM-F=>Hft&#rrRy9>)_AtF!lD(2Vpa;OyG!9JQhWiTJZTPZ=tyz&ZI~<|1nUP$ zOHAC>>wP0pcTrSg`nmLG&ZYBv@;a9VTBAuOx7Cw}LB}SIEw1-l%Je8?W^Q)OA3w)< zHe_RR7WI%KS*{E|{0I_CBQmz}5QY<%9*?N!)UNgbe_n zjTc13^mRUmr65~GYQ04Hy!B(ggdLe`GELL!#SSBU#N&_JLxU53B<+7V=v|z{?*G77 zd1LEh1LgFerxCI|*j(d0#H>vqeXAHOqa-FK5}tl>T>+kALYDlDD>0gUtb4KMS=4?9 zwIQ9QXi~SfGz!lka7}~8B4I6K>RZMQ&(?OHS8X#}0stJhR0g5`HaU;iXVHsGtzqml z5qyn+p3MpJgl_d+3(vB@nw6-L;)*1+?2V;x+PyC$oJ4+KgFI0AS9trg8Y*o9%SYLB zDvzA#4z43Y?)Y2Ys7Wz8NqwS!`D3z}ArNz7@}e9kf)q_BgQe7n&{h9MZ!C)jmcavE z$+)_cOSm2Ql40K2;bF+|H;H+Lx(~Ud!=6_sym{9G^Tz@(j(Pi)w33@36xrsEUm6;%1weJ z&@*pleB{N!U6Mw_$<}f8RQzQ4hdy<+^y(ry+|f4h(;`3KZHvd%2&-=P8Z{C@?#sD9 z3=p-3+}sb_Q&>!XyTYetOXCR~OS>CL==hE+JSq#x&dQZ_IIXB>YH@RUzZp3|K-X%p zLLy07(?V1nH}CR+3T<>WbgY*;Z?3*N+Vf@7(|Ds6?53AB2&;URR^YQZL`Zu+c*qct zy&AkLrW$}d<*$^fB;q^y)+Hq^&6MN&q9P-?94FdpCboRlbMOBiJR)_i)yccRJY?$g zos#36TI^|mWuSp&|=2}VqHRCcEd1WsKgeu=?xMa>dQ zbqal6UQQm?AR73s|G_hOT3Oj73T%~{Paad`EX6*!yS0d&WvgNHCyKh=iHSlC`(%ls zsE$}=DyAF^+>Bwx9VX1cp4a}W>Xn3Ek;j=`&B}ZCmOkc&dn?X^pEEE~H4qIEVS3VQ ztKmRudci2=hjC4M4Yqz7+(jx}B9yJ8p6AYd{nK_Q+UEv6q2s14F7^#LO`3f_)+5x^ zr+`d1B>c0;$e$yATrB;;ZHNJG{hxc@`3}4cs>m*qvF>)IbAK6@RJLBI%2bFG-|HG8w1K>2I3l`dh%FefEe zjhsMKQ(ONMTm0usSSaD|E_U8{L}5s_VDvwmU?6?Y%lrt5PL+l(kF8-T`wG+8&|Bg8 zi$rWTioYTjg#k-NwLjN28S4`2`<$v4MNP34slwUFT8oNS8cyXAVQH$$kmg=Ni9O>R zO1&a=jy%m?QM!W?e5XX|TfZsmePYF-g%YVXDSt-hA7$a+P_y)K*@qjS+$gLK*fJId zsjIE9f?bY(b|z$hw#~i^i+LAbBIr#aWeJ~-JQC3cLwD(zf}dPj&9-o zR;?ZjLur<1xeM;te?#D>z(LD6rpOm;RMEiD0J@Y~R)>4 zwzlYeAVvQB5uaa(pgqlvr4U1>O33n|6B~S__((N?;ZH<*8X=79ExB+->pvU2abN0) z4}bBpmo+woJSgyGhXo2}hy|39)eifHC7@TJZv2yeQBWei`$J;V89GfC9&29}JkNX{ z@?4xu1?@qJqQg~kd~Ik>D25k~d|CK-$KSX9VVeGH@(w2TBOhAGyA2ZUpLzF?^dxVR zU-`uDVMek?FyjCD=NWz|DA@rElryHkWi_J-xX{Lt;dybvvJ@nUI+uU0@~6n>(na*< zsp0fi!c{wFQTF?rCMv@Gq;-@7nE$)NAruVFR)O`(g#wLtBIw4B*TK&qOTRV#uB)G4 z{3U18caUU*11!xsJj`8_lXp)3F&HQ)-J-3>TL+S_p6F1>*O_g=f(Gl=J%86FYY_b1 zi}}-l+}`PpOM360SIy}ZDJ=W}7mR3dTCFzs%U+Rt|DD>;(dkJ(*sK&OJcpq=0t9yV z3^>lYF8(=W-(~Q-Ugsc*+`6Xx8S{_{Q%xK1UC)0H;J?`l__ONsl!KgIU{U5%jy}Fe zZ%7^Y-%Ah1`uLdiu{~x5D%Pfmi4Z@_T;>1Cj~`OIBk(!@zLFG&p#$yi|89$xCXphg zk@rg0gH?X;Z!+mpbXa7<77*D(`QK+>1z=TmxY(yyC|m!1pU-b9Ro!vVVYDN%U=zy! z`{%Rxy_^3|TpvbV;}y=|7d)p4cKZU}^|P2u{qU&Qpy;E?`sx$!5S#?2XDFMQRVGU@ zEwWGKE)vyoguxeA;&BUc+UQ4YirmQ*aFV}y zcV5!3B8`hS$+dP~*ThY7lMdUZ@^QczAcjJZRzh*mRWfB<4UONKk1y;TWT#o>mpbgg zdvKhK=;e;ONDF_mJo@LpNguvJ@S-dk&ud#pm7=UX5f)xGULsr;+gGuGi9P5=eIo(x z0*MDjLMlZ|4<}N}J~A!|wX<>wg3K#2Y#D zWgVgZP~XFuqa;n>ylTqw5iG{~Su(NbLDk!1&*W#0axtx&@5fbE^=lgc7*quW`69Lr zB(z}GQifJ{$g<0-4+^M4tt@2lzQZI90`z#E1g9t#Of-}*UGEB}aU#2iQ%WQiy!u-B zh_+TzTLSZb_`IyzP3C|&pSXu6C+*Y=u&<8RKQw6<<{ZQ2Au=B>|4pEV`wMA27-?mQ zSV`7FfycJ{ZS4GwgWD_rLHZd<*!8kl5c|A^uXfF39V0nZL0^`vj}W=`^CWmbROn*r zLpL_^JXVy%;l#ZUT9xz5?8woZ&88;H-L~4@_N?7ml0@+GFfk1rbt*)-TBG1wx}0-? zrJ0xl(}p2QvD)-`h+&nZoDC;YTFxc}feycD-!z+Y7AhqX6HMQ@Y@~ zPi2RHpikc>|Mzt8&);_u5K`0`_>p7^oS5}SC6;A%RQ^OhWg}&3Xki99nrK?0d)=ZX z+Vv-Jj#lK+G{h@7!GO)3e~vKT3-aM=ehzW+kegAu2CCFSpy!#B)jUM@Aa|SiTXAL6 z91VJhr?pMmw8fs=Ycm(Q-p9+dX5o2U%tXltBtrZ`tD~(9 zo^s_oZJ}S=G-l+#ysz+Xy~oR`CS>RKFcQHBaJ9c#kr8sL)D%ArMD6jcix|7AukZhN z>8C%u(kW5Y-*le_(Qoz*g3?>ZFfDBa`Bb zVZqSXuR9C+!9AM8I=|op?J0EBO=VSa(6FwqeCL3_E_2!*N%?1r{o_yv-<6=BBh_RU z6vw#_nvFCds0?Y-a2Icut}ln~zcTU0Zr>F~4AbOXnm@bt z75GuM$-xK>2X!D?M)Ctr8l24 zDM|3P?`WVfFvEHsF#kI~C>xRUdgR$2NT z+nHzdrs1=rSCr<$SIac$OnL@79Amfv`u#E0rqEHzA1@*rrMVdB1FF|EGqdI^_8Ds` z%gU-3CeBt{n$|0**=~ro-$C7BCg-zSDQkECDYpN)1YC4bogYEKhZ|1E$zbW8FYI^^ zXC=@nSu5*r@*v@ykz(Adqi59(Y^vF=Z;p)F72b|d3ik`!nArwFBfn@Q60-ZlF_#V0 zv)9SVjAXu6Y-fzxhW~H+QBeBW18mTwKnmCXPwMughR;xR-X=O;u2CH5`f=KzJC8KT z`kVCv$LewFSC@$eH~h$w==MGESgc=o^PFmR#2!`$zs4V2j$d=BG}^i|(oYy7;dW`$ z+PVhFwT_@mX_+w{^_$Wfwcu4UpBu^X_?LN#^|*Q;LXReR59wzcVxJGKpY2&WgE%*; z+uN(J->EtGP~&r#Y+3KB>mKmJP;kW=4+($9#<98Xda7Yiyr$X?^3=FGA66~V<}xTE zA;K-Bk7@j2p#-Uwuw6lW>!sP=igjIxf!dF&cb)46gxce=Tdii)kQ^d->iD%lkwrj| zx}nvt!~%~f+kiDoZS8WT%0#e5PocQX+wm!OGBuguauL>h7T4lo83)we-H{D*VBD8f zxyNuF1S=Dt4-M{Ln)w%2K;3!@d5N}w2}_%5|OaM&z$9pGaPARE^jXU5RfT=x%3MO!N^_%DcbAu>@{q^OCn6Wgsw~@ zhZY}d@Y|+IMVFJxN$8FEwbgJ1UR#gro<&uon{!QwgU$)4qUDBaaT7`A=t`W_lw51r zBA{Xkx>+6D?Xr`IbO=@W0Vd^>TW4i2dB==?X z^M%_V4ejtq+?X3>2Cc9{+OnD=IxhXO@B`&JLV6qJZ>C?_2SZRP5qu_`6u1;~cNmkj z{}oIq;ev0{z^Sg;O}D)i#n#uZ%6i3rtBdb&R-clMi_feT1uTttl{_4=4kjp=T4fvD zs#BwVLV}|o4g5GEb_Zw^^XRgVUmYi~p8l8M;uR6lhrp|R%-nHr%TAgxU5YQOSW4%}Pt8yoo!&$;${#RW& zkXaV8H(L(hVKg)Xc|YX#1h{T~Zz{GfYmi9odtGL=nsl}^dYoMJyD!r?7qKvl9xW@# z-l~j7D=}mvX~vctIE|zcl7KVHOVqYu=aamVay4EvGvAOgbfxVdlh=rr@0K(a zhAQ^`IKDryG&r(47PwfuGo#c8yOpdMqwyS$waXhGV z=Ori{Kmvf@4XADfL#U0R%Z3pfUnMX`KK+I&VzUH!b0D@Kox!V`4HU`RR}#v!=Dh)Z zsHu3gpoi1UN@zT(8PGs|ju?J5+@rY9MXYr++iJj=@Lvh8wUGPFklwU~kENn1Cd<%+ zL=FAy{p%h>s(? zJfxv^Zn+u!TKcrZLr>PV3WP9t-IsQ~8;88@+l#TEPzgb^`$Z?9#c!gZdM5i(@BLs8 z?96gE@KraL>I@-1Kldf|;1s*va_N6+CZAK7--hj3eB%HssMIr;+Zg@)G9a}V`!k6**zwNT|66`OI{+c?V^+lY0a+kp z)Y$_TAQjaD(5db3-M?MEldPm@>s?8<t%@&n)!{oU{E635 zhd@;_n2Eg?y^D_YtDdsO42MeD$An~{%;ro8GkbG&UwIuR)-ReC3CDfZ>Wuy-%*q;v zamu2|xMm&K;c;6!S)I2b?)z6TjN&vwSHvX-1uM?4&S?oCBjzjKrwO}KoN-dOdJ@pJ zDUx4Oe^e|=JTk|CkH5G76c%>RZDz4zS&fqKIIocxto8cq)v%&yZ;Mu}9Y&EUQ=Z&1 z0+S8HxI)Ii`}DyGOJN7|HRr_Bwr#%W9>o!y(uv8IY!?AVQaR0h6{y*LA_CmyS@_Ye zq8_iOb*YhMlijC5Wqm!mF=82$_F{QZ^?OBQr31B}h;n&m|5uR8J`nuk*=ro z!EGJs&W%w|N|Uw>cwmQ=zNayv4@-+b;9)7-d3-RuU&=>IrBSJ(0E&kwl9Td`!tBqV z`OdV)L+OGq@j&k;eN*Nfq4`72>kQA^DOhs&?yRfp1o;}J>ykEotu#Jycuv!gbAPRv zeC`)W+Izy^q=a5ul155--2`?6+YouP;PCz2rf4Ci155DTFF06#*ZM>p7!KWVq5bLi zzvzpz=8yMvmm?o4fUwxB#>AbpA(jw6^H^a_o|WvDaRfny!3=Gy4)A>Yn5*|O38Q!p zahc7la@rx@e4zSO?T_R{m=($?%RLx8>xWN|+?QuFDm?E&%HUY*HbTRHPDhii>;5_=v?N>z^J-;T3#$o{iW>&HphsIRQJW z$cUGzC+cw%^EYZlrJ4L=G3DmMVBv7YfZ~5@mZ_15Yds2LAyRk8PPeuIIETP!L%)AN2KRjS6!gdt8c>@LWp)MKS|RPdZ9W)@nsnE0n<;c z)p%!(hT?UXYlloLc0Z<3zA8n z#_>B^n&9~{vuCqfj2(Vh@DHF+NTGM1E)nJd>k0HRS4pG^8f-?(iV00>FJ>PasZyU0 zJVKJG3JB#}pRPSuYx>+|C^?>XLu^-WrcU}1XRg9q8Z<@yg0NkxHHYuab7g%Z5!jMP zn5?|Z%KD+E^^&k0s_s8P;ICjr8YOuM#hO!7To*29k+WU!~9?gljqIjw8*yL>hL)-li% zscvy1K82OA7V;dV47D?kuyczDkRO_BW|m^Fxz(gXv%9&`%+Lb5iG-UyeT80HUWGFi zmsP)sEaw3W(DNo(ri~G}_`Y_D!7VzbNY^BT-n&nbmB>O)B^PNsc;~G~*YfU|x+|T$ z+=!B)|NqEUA>4eVj${jsRxJy>d`YK0h)nv3EV#o}eFh;K90Yz`z z*)}~9HT8v@W4q2Gh`i&+$bR?wKN{#dy&ROr9KVl5rae;Sl`yg11gX%5NCDlEx#Ko_ z^n*YLxQ0Q;g;Sw04Xu1}&A=s>@zGD4dyUMFA%W!mv4!t4Q_AW}G@dZhx99ZHfzVKJ zuBX)W+*dD=x?r+2vwek|3w~@rOql#Aje`nELrPXyV;Sc-j!j*GShRgui?G>ioCCHb zfg)fP;XqiHElQ_Qd0LvWST6#oPSuiTibL*r9Q{KkKvV9UQG7H4*{N_Y)AOb zs}^_9PnqpHQYUpyHaaIdua2L}U%(pHz%&{a5Tu^u95?_2*HYiOE|i*-JPddl^*TrX z6mv+sCm*d+PV|?(;;xqt2Cyq7&oWao9HM;y|HQ7{`xauq*>L0tR%ERZneL6!Frl;9 zP4{yxnMdvGgeV`0jm($i_kmNg16SBcjnN8grF@epk-mg>{)MtMa2nLo6$ z=-(TpNGfQPP2Lo(uiV0N?;DQ+>&oDmJwe;)kay~-@N&UtQIEu95t#lj;lPs=N1I`(}=+VHrQk7GCqZSpgaRyG;T zpKBR4?`oET_ivwxJyz&DGfZil$~M}r`JOGYoU5w1pB|WHa_exib65Z)ix1faR!?a< z+XZyG{q~t!8RPYuLfZmjAmVG3KZJena-A4G2sr7wXtfKkH0#Y z$>C;F=+!j7Z}d3U4*!vwcq^k-Sx@XeWtIhHusWhU2Q=yG-+)m=dGqS?+=Kr}=r{4= z1u)Nnk#M`w$oqtOWz9o}rWKQ~%m*Xg&wEJ%Qy<%vf!qOg^d!x91EvAEGmJE%79jJ4 zIb)bjo%y<7#>on_nx2M#D|4}=e&b-R&C2L7`AvmD$#QfoHP9bnVaj7R+D{HD+*#AA zd`6IAz>DAVMRycc{seOaX6HI?NV^eFt8A}aftBa%RenL4#gt9qWc|(|(P%20Qln!e zp-;NVE0P3iV86Eu(yNR zD3x8W*;);0LQRTnlU!LU4tr@TS`_3L-~VIfV#N9GjG3%10@#M&jmj9pjc*5rxY>|buk6;@5o6jj!?G<=fc@sXy zGt-{RcZy?>WoOM(j77RWNbf5LgnkyjS%I4t^Z7atwDW8$uCVe!WWfj7bS4F?>sO+h8mbd);P9j}Ss9^C6Ks@k45{g7P@Po4T zdOal4lORi=UAU{}>Ceb*Pe%)dQn}BLnv}|ZAnf|T5-p}p*?biRs3bh=QlhK3sIv`D z_ON*{{RR;MklR{r=F+Q5)m_?EQtBX3<+=&f*)4P%8l)_yzjg&wDe`bYAV>eV6z?up z*Ft%t!0(xv^)rcJNw65L2_lxBdMW=PWnX6*Fl5|(Dm*<&B7dW%zbRGojk4~UgKjEl zCj@YpiJJ&~3fQv)w5-ZRMCCO3nd*^mKZ6q2TUy3Rf%p2GwX8}%08VixCXnAT?XHpT zj*vNtPv*WI2Bq_a02A++JMWY*lb?;Q)DbF=3y`?hx98rO8Gp67+CAqgd!}$nL2FDp zX%v~(?mM!at&-v=)jE=R`4}~PVp>J^@VY4@!2Q>%1bkcnE;rTxl}Bp0RYEnU5knzT zemL@J(@Ae)b02c4CC`+J2&_e`^V_=py5~AQ%SA38SHHbg&Vg1F$u%sGEl_hoGQ_Q6*``F&ndJ;t8g;ufd~6$*E{F_p-B8~Ka9(7GLHBFLkguh-1w)1O zo}JB8S!c8+PC4QUdU8P(l(&<6vJsIxTteQN^gh4)|t@Imsgm zxWCw?H}A@V#jj@cXu0*SY7KSsRetE^5+Q$XD(0J6X=_KuMkZh#TfJ*rF(%>>q+_hX z60FEE%JGNwWN;cr0$unovXY{&GfTK@Hz;WOd_&r178|6jPt9i@uZJu$GXGF}#q7x0 z=(B}#j83tafu3bd=fLPqHa9Cb-ZqGx{7dOlqeDBs5ffxH_2ohuwN%G8-h4>L9RMWV zZVR?{wZxUW9QgY4Qk_uhK|ngP)3AGMD5}8Er%rZ!Nsyb^(#hA0c(L&W5l zyge_m_G40=@m(l^s0@Rjce27h5lMSLM=iBtbDViGN}azske3ErkH=x*qDv-sD0F|~ zM_qVhgB9iCMbseA%_;qYtea+z~b{&q1_$!{x$5zcC~L#QT$X|Evf5(5!&guSaAcJN;Qvx0nZh)eOGvE1VzB zG;0?E@H$gr4m>efj7o{6LAW{h!uzBF!261gI4-H%`=tqQ2Tg8(F>?3=$FurXQ{cl( zlhB(y1wJOXW_QmVmn|?L06)ad6YLWw$MjucSQfc7!6jFz`K2kkx#X{}k;&aT9zD_ETiR#Y zQ(6dmY}XhR1~0j{U+S4T2ByS?B+#a{G{tHgV&EIx4j`-opmuZoW6^l>G6%2? zlcM>5&|3SJq?^F1ZSNi4_Q98|v3F3=zZTn%xFzUA-vm9yHNJy_`*JOXD9jP`f8#MM zNKrhkS%2$ri_L$*Zc?UkgRyI)&FkqJIP6h;1BA7%Hl6XkWWbx7 zxh`C$rQ`y2ixL0=YgZ8~(%Fpli^Q9MY7qq3L@xvttei*>)oAmgW$&+q+zE|<#%+=p}a zp1JnSHP_4$`TKt9#wo~NX1d@K6`$B}polqxVf&-}1O?(9pGJtTv&Q5{iJr9h`>WJZ zi5Jl^gZIy|Z%E@Li!cq9OAhFk$ZC|o_)9UhDRTc#Bkj><2y zUu>%o#JN`+ome4#NHyC6L=U+ffErHooX_e@yW*%;3dw-915D>Y5k4P}CBfZkPmJ)l z67?qx=B+7JN>FA5ju-UD)>OO<19iQy%J>t74=DC`2w+l>W|?nL0h9y{LKdpvGcQyHX3&(77|WU@t7dmSuc+MRky#x%*es>UCmB!oj9V3_ACI(94m z!SS`m%x;8F7DEdWNI=^p>2n_A*<5+9;jn#*m9@tm3aErmzTu2%PMAZk`OGY1DpSdEZopUVv16Wtbfa2y~bN&}Ri{=iGp zVIXN~So9>A2^3z==%yitbA>y{bY?o1U6?gV?W;*1Dysxs$bn zF2EV471=V8a%${6&U$)+oB%v!TSEm2e&Htr3-8GcKrXd4v~yW~-9&WJHRyhUOPhLZ z5A?hMNZ-wjR0+PPr(LYA?b7W1wstk&t6r$Rk8@DJ%XM;fb|3AXg#w>|B<pmX|vY1 zIk)TeNb!(o1p^?E_d!$x!g!z{Ax-sLy6u_oc-f*gNewssReIyRC&R{i4qY0$(Bk0$H8h9JaZ+>^*PL%ocX1iP2ff5=noXbP#W4 z!Iy=3`2yyu4b=iWW!9C}()E9IBVkz9oZHTQ|36X$&rd#akL4MrjH|Dto=dMcDrk%- z;hk(BTtEJ11Ri727$XU(i~?)geP0O@el6sZ5nUGn5m}{o=kN+^4sU6Th8irK$GP=Ktgp;1`#t|05|T zT6h~Zs@6aB-^jV|Bf1}zL@xY4ipy}E^yk-h^f^B6zoBHFudOBXA^+za|HW6Y%?gvo z=l^?QKa1LU@<#!GlXwu!s`1~qS_n|AG1&KYJ$EE}`62ux6g6ZAjOw4!B%+c3T&Y}D z4qcd7=xwV>InzIv6^DL$#vrv}V3-86su6qg|9lw&VL?ROq%vy6GtyWUh7mZ1;!uP9 zT8I3f+7|p&p9gxTFB_LC7#;|o@7KSQ>3=?7NuWfoNXmw_A)mpvD=4~Z_(I_G+cna` zj~pQiv)DHx-zRTG(!f)lcI~6i*K!ij!m2`sZ$* z|Ka+PDRf^|iL3h8yrloY(#EllbEUpj^(oKiho8pRGZy*sVrZqem_S96UznK>dL&S* zO{pbJ`}+1WvUp`&&I-SSA3PW|PhfzLw*n<%6{WQj0YoP|6y)ZuV!Ahy5FVACs=V{| zo(_aqdP$~Y5+)g4M*&#x|LMZ*_1NNmzHj-dN-*jfsC1r-XHG{q3O}=3>y!(`p8Z8{ zN~sZXGm7uY^x~;qhA?f=>KL7vl2)&_em2(jdz;bY=UEn1)+x?e71fr}$*aBlabwyv z$j?-Aw3iYU98JBQM`H^oT!Xex>5C!(J4L0Y`=UGIYe+VIF|`NNr_oTDc`c8q*aka? zM>ND6-|!N4mmfz8ru5Vw`DGELzMAyBcriI&nmQ*H$1p_Fd>33OPvbg5O*T7+DH0%q zyyX(yP)bp|Y%=}MT<4fbT`45`UoC(ySkEF(=JqfqenBhAn1^NB^Xa&`?8nPx->c2L zxrsJ}BG~LWG7}j1%^)lM|0%}he=uFg<}J!${2DMb{yW0Ofh@S zx-2VUH(c>ESEGt5piyte`izo!*MmKTWNV2?$jkAs+h!9|59ILe8h!39O&~mOVe!9A zqemU3yJHw)zEf!$OhZaJ)HTSU8u@Xvn{eu7S~f&5q4lmIAkjp{{C(zpU?GaIoG(>e z1M|0P#X02n7{F#PafT!bUu*PbNG{WvjRJd=(N-E;TK|3b%9v}*WdL^uGlT>Nm#T7_ zEzQq~|0w^xa0PWR7_kYir9>W_c&>LWkK~lS5tihlYZP(YbJ?BA*}EjalLxVhh@gRf z`-i}SMYPXsmH*$=o+xK`rpNZx=Q^MEvYcHdE}UTsd?qJuv=<6eP*wz3=i|UO*-GSB zS{K_bj=?&YrmBs{4(nUw+-Ji@O&9iwqbF(QC}u^=;%4~L_)^Ua;*mJokCw;wFJ^${ zAC7syY(En>w_M*^U*RrsFWxj*c5m6tE7Omf2P6yAiM-okNgsqO`$nd2GHy0*( zD3Q>CzgiZJFu0mwEI-(1&6*3zQB9#s1c|_AC;}W{IG&AVaU%;75m6rjrtap~4i@VH zvt@3v4MT5d01IviDSnXdE-Qm{>HD_}QPPmxf0a;Pe$t!2)rljtZPwcMjaSWmED2Iw zqyDgsAD;oggCkyuEoMzDzKg^*GR@^&1CU7d?1ms>&mIPnVj z%wE~tTpNbc&HzFrrwJa{r+CKlod(pK82MWIUSpX5A#HX*U{IZ)ShN^>^c#hva-p~v z3db%RL(X2}mdItng`+l6Y%6nk+taMuS;{ZTYFUX5b=4&&M!J!WPJff|W-TO~3P5)U z9z@>AOjyhqFeo1iZNHK{jHOSvcKM>6Dngy3n1Pe?-VXq{l2WGG+fTh6n?ug4xe5dX zm_On6gZk?zhFK~^A_YvOrZOhRySF{liO$)TRSP!j2ihvdYFPeeClnI>kMDNdSt zp-98iZ)6B{Ci>qHRcJ8giXB>0o>>Q7Hy?)XS*A^kG97HyJz9^llhtvVIieiyOF0`n z<@nNf>3z{08Akf&9Q_CR#hBlw+DV8dkP0zNQM^qwpIn6*z~93N|0HF6vs9j@c0PC> zeKd$4z~GXD5A4xg+_h=&X;}z?6$M^{k|K-?SlkrrS5tnPQH4~~zGo|!wxugKtoErw z&^WX+xI^bqjoYCH(7&Q=VWQH0z2c-dvT2HXZ>qmmqPrED6A3V^p-w?-VKY7qc<-Ff z^~K1ZRr_>eGeFWmX<;{})rBad&KfmZ#TuR8)#&(rW~!p`OhvDyFt{-#sNOf}!&51e z{?0Xx{z4;HAV_WGGt^-+Bnqx_YHyjza|rwbxy%+t@O5rY(r1^yGTBGPa?CuHXH7P( zNo(%lZY*U^k~2(y%NuzbYkB|12KE&%NjMnZ`vkspvZC3+XXR?5uIL7qA(rM2c*{fv zlGfSrUQE@81f7ssAA}^@6}=(RoZWO(1HOSbXmOMkm)zAJ#PSZzsm+C6^m(h8s7J?k zLnkWDTa>=Vj8#jnMyYInnVdRr-l8hH5+Ir?!lC$0$#|gv%Lo*~tViWf`xD!FoD!2( z?r3`w6tJb5 zrvG10PJvfIF4^mKyd+oJw+o+SC8%=DTT9H$Tj+EM26y%v-gZ&AyHo?}Elu4Gc}@)T zYS|Ciw;N2Vxm2H`s#B4i#@NfjXE$#3Y1@JiB30;^LJsQHME5yN-?rO z)b30T`-=Pq0TleI?y(fLGx8n6J0dte0tkup9%Fw&$|cnnbmGCJY#eH)w7EBA0hF!Q z7yT+H1gk-rP}t29xR8|+hf?X@U~p&K`wT&O@tWXrgjJDfg5bcCDH}ay$USjK6%BAZ zgjSQ1qiqz{bTEAoB+%X(OG}|wAo30T@L_(b&bDoxo`29Q-%1Re;SW7*8Jk;g$~YR&sV!^)=q6IF*iA6;!U+51=W0&~JBbK>GvC8Nj1E zw%BU(`a7@NaM+ldX=m+6@C-4)=jSNb*R5NhZdHr z55QYVl9$LA^l7z^{_^&~no8;Kp%>j@I4=C^<-h$HzrG8}!OEd4DJlAaD6dcm1j9;- z8h>^E6yYRiE+`5kHgsUU?tCe?8mnpPu^zocy@=b%djfy%OG?b23LLDqg?0F7b+IIX z6eoLL>+wU2o$I%qS!w_cVb`dtr;e9HmFKpkt{_+$yDc_Z@hcboEE$ustW1G@a?$15 zf3Gt1lj64Zunmi@e2so}o7XNVy28$ZkLT6fLcbY{4luWwcYS1>m$V9{J2h|qTro+j zVoq?wAE$bEYfQG?N~hT}-G8`}SBA^h;w>xl`Q1oMdOVamZ(nb zB~$D`1V<7xp^*lN`Nl&T5^LxFmKT{QqmS0Z6n1}IJJPXY4l4=?YMcR~)-guj4J?`! zK7}1I2q&697iaHPg59LyUr0dY17V?C`q&*$?z^PU4DK`ZCasEvsb<|cN)RKzk<%Ui z3|~zCr@r@4ZHN#>YN*y0Z`|N8!QqwitqSxG{is6xO44itxUVoK-dv6V{>1L3A<1sn zU0|y`ahvBjDmMw~Be$6F@53V=**ya#um55&j1;^9nLuU2cN(cT-=7ryEaZYRsS0t{ zsM0>6z5M%suSyi7wv~Vrj1Ir=F`mirdfy(6%7)b|GCq5z8bgIc{BaaZ5aX(F*2QNo zDgK?5`uk;hqa90f3dyQDc_+tw{3X^!HaGjenWc429b<#Fq`wh$)0;Lv_2^v%IRlR% zDzb@5OE?j}`#nyqn(<-(B1XcL(`7-eZ9lh zw;I2?Yvc9E)OZE161rC-H8uXu{ByW%T}v(|pj4i8EOz`!8yw7n;y+!O)y<@$Ar>O0 zQKHJ=LS2u9)l=U%Q_^<|htt*PFhRm2L&u5WAea_YEix^rtTOIjWVk`%X*B8jWV005 z?dN({2t6&Uw#{3>3(lVNPvRc7cZ}Z}T&F?64^Wd{doXpGYkGCAkEm8}xqBoOM&FTH z`JG#+{_6{H?ueb~={uNi30~=3oWVy_A_NB;4=sjcoTzAS>F5}5WO;M`1t|F8=JD|U zW^@jpMlL_o^i4)I4GsBwY2x=1L78;%(FuFoLH@etA`!lEnoL#>sQcr^^fgWQ;2;&P zi$)z3XI{|qaDV!iNxZ^zX4kQ!1(q3^Z!f+L_sZ@>bx&oHR83P`S01<7PFznn&5d7B zEv)DsfY#qZFRWqP)H{`JIrPlRJ&1-$7VOja<;r$+dqv{JTq+uJ9Jt)1AFx#sFX+5x z82`=g=6<50FGl}S;VRDQP;6W!nH%G;WqQBKkNNhs0;~@jpE(kJyfjxEE|Pix`3k#b z+XfF-2WHYq+iuML`}(FJEbFk7QH;=#JJvx(O+OB)uU!wi#XP8pQeQ!eG%*Koif~E3 z&`BxiI+AOs#8+c_7RE-r^@7%e1UT@?(2>(E0yprKdsv$o9p1851bF?eR{bhk$0Yqa zYDZV=r51g7jSE{Nb1ck8GtKRJcg*c8t#G@bQxTHAl}rx$m4L8!;Ny|Ao$u8d5%{;q ziPL@)cE2T6H&^QX`gZGRp^#J)>F?f9D;P%7))6M62bwG*3Q zV02{ojZx*Oe>=ft`6KyHEINZCzHkSYoh8rlBF{-(;_rx_-EHc7n(MH&06xClG;fe8 zc(_{)tgXiS2A=#0Ms=GqIfwQOm|H!Ez6t}7>c4o{QROo>PFC0a3jTlRE;YG11M+}v z`&01biLN=f24_8aS}Z);-&Y@2Qa$U{z}dR?e&y2W{We#7=4irwGLH&adysSf4bzzG zN~T>5wlGDhGbNujSDTS5YY~nZZ9D|PwWD3sBaYJ*SZ1%Yaz(S*|r+y zt82QK=UEGRjeuE2r)5`}N4a!qlR*aguu;YRGZ&|8f?ocBHv+NFLit;TN@3fy|NK^vlIX}~?J zibX$+up)Z#EoiWa0*4ND!(z&@f~@}+o&*35zUF~;=^+?g_=Yu}Wyx41%X-!*vAJgm zWj@f;jJPgVF^Aq-nPMGenP67Q0JI6ncaPe3wNOUvd{MxutAsw4i{j@_Q9BkG@#&8S zhSliA3FGp^F@{Msp{@JKrmPKd>dYTmDSBg*(4r9h#!dH3;xu*-g2)IbW?H?23m9Je z{-zp=6L8Qpzy7JI@q3o9JC5g1iovv=CT2&aK`=2xw{8scrCi80?eZCG9wCNDl;|V) zP*S!bc0Km7NtMW<16YCDC+BlacOlq`vdASd8}L)K zVH4icxfwwMj#!ZxM@|WF3RG*7Hs9BZ?M%Ky{^8vI*(ft2&OxNSXjtKTgtA!jH}INh zX9*jQb=DBv__cK(7=?L|a!Ab%y&4YY@E49x5NC@eFFP)0zm#FytjgIn;QMCGYi-{b z=a!Hl0XlsBt@PfX){kktmPSawjk&WCUY4}zmgwc>-i(4aKGsZGX=@8j zHAc^-L7kcR4Zsp>zwkKG8otto99%0YRTKzeNeTM+MzYF3f>`0cl@gC^Ivj8WqIXfP zes+0Ln?a8Gz7CA;mGy&+RLl5Tyt{< zMp7^JSGVkwou0bWJ@H7S!Ue^oU6;9*4H;O$(!IO}dCck6Lvc@f9SxcO zM{O#X;ttjczsdn>UsGb8M%k6TLx^HV1N%oX2;jfSLbOD+W@vl+t}KI#i+l~+)mnyR zsNIe_R+`%dfXSp5h=zCkQ{(+T#+?nj=RPlbx+h3m$pvE^Nh8-ugxx&zo!z7w{9;ne zN#PD<&Wv*;yw-&9HsXRRfM@Cbs>fEWdMdQOQ$H~3h6WN{d~-U(7?8RcK<(NI5i61l zkt?pPuxh0Lu52CZsfy^9!ZLex3QMuJdckwPTil;kmordml}wjUdN-tyu4XtS3L+7V zNn4|cfpdQHLte_WW#8Hw3RS^c@>1^5kgQLwAR;O+QdP@7tAWLEPy?8eaK`%1Te!iH zrUS6KE)zRokyrBy1<_;m;6`sHjHYHgeNefq z2m=3CF_tZphoYPTkIi|K_p7|k0}%?h$${~n49yXD(|-4>K|~D|$ibN}F^^tdXEj_d zQi68ee{p*gmG=dj6K#d*?#|t!GQKN4qe8~4Nhy`}z&ScC%ojGzN`J&?(TH)2R5qt-eyWqQ;e8l zt{?%qx;ssyL8m|N39v?CX6JA0>s^E*U8A_7<0GYgU8B&l@~qEx-mRq+@yg5r*m~iv zfoIXRl)2?Nb&{ZZ1GZtgEmH}<&*IPVzrD!11miH78PW1Mi=U~9+0ZXp^naL_(#J_t zYQ6~`Eb%=78+FoOwCQUIjwLhF6;GoG8VV*_z{RMk=q?R){kY3K1tW&+96Xw*ODS>A znlzFl5fftqLSAOVh&RWXza8IB75K${devg%UO|-9|`TM zslas2XhvGc30Oh$7fv9>yY7w8a6;s=?;rfee4=mTSK_*HyG}ZTA>Ij(OI2@AIqj+G zfQ8u%f6f%?=I!^6Zn>;}U`5_&X3*I!UChE<0PVZgt7ue-f(|~aL*#P>uWo-T1VVGV z-*r$SNB<=mw1V1~(VRc6f6gr6E+g;=1|D36wVOm5E5ZN0A~ZB8#`CLm>SH%W$=-3L zpRUu-!;a{-MR?D3p0U6-xHLP&uDX}~wm=}n*vd1|whS7YK+Tv{mB4!SV)wa0sqFu% zUceBUFgpK+)%52p7j)SkK%%w*|un5)lBJ5CTJ;=>@ zH}ZO!)~Dwf>;a+n7T{^`bp2!J;6|~}H}CY)QbE95KlgQK{~l0nfJhiJE8~^XgW4@l zRzWXq)JPusJ}qwqG7lbBfaU7R7<(fua7E82`jKOxufHG!fVRhTW(fQMihSr~S;9&-XKsif8qPtd< zBnget&?R-EtzmPF{N$Ep0WjRtS=~Ji?hQUakiqX#6!-A;#jgVQ(xvHs`nqmY+4jAx z*khIf9}NZ_fEf7Po)S9w8&m8VKhyHl$rrwoLLK}ZsXvQW` zAOeSoBCXnm<|Ym)Fi{yj1pN1O9$1WJK&r*VJA%qXW61u<)JO8!a!(K->wAi?xd=Po ztF};?`e}Wraw|s_Wtwj2@~s5v>s~bAloL*W)u(~>*`nHBTo1c;T_$Q+i@f$J_UP_{ z+-Mo3j`2fkL>Z)6m|7RT4=42h&r}+m^!A+cHyJ`%LOH%)6cqvssS^d!l@{smb!r{o z`*e@M)$<9h+sn*dja-0~GoCtD*bnuIW{-nT3(sjD<(UR8?Zhw7AFbHHYXJRvTQFu$ zH#)atCIspOFIzq#+et>=&#pHb9@y8vScnT~>-nm^IQ{`p&u-%_L4XPWtbX@3VP_GYv$dH_!bN;;_Ll&pZBwBp`c`x&3f+v9}NwroE@ z`prAgLT3JaH(W+>?DA+}Q`R@!F-~WI0~-_Uc_cz){}?hlu!4o<1dO}S&A>6GObydu za%-F!y9OR4((iBG#i+EDHM=E~z0zLaE5e)NI3hdeFY43sF zMnNY*cj)$~hYXM@-5(&gm2VadOGZ%_>Q#(3H7jWUz--FxT2|Hg`y;m!XgKO6G|r>6 zJ#Ho}o|`C-x8}f%jy=t7=anE&|(F^z< z5Ld@*W*I}%Zn;0HM9ze@Oc zxDpP@eo7g=_iFlP3=GlLt(MyGF0Rgn58GMbBHN$!&K69Urn7nM0QnB8U@1lC30eNLjI&E|S1aQm&YSf3 z&lB~fjlexT8zZQMOg}Wd(xmADEbDaWO@=e;^uj=iCVW5)&2qUzXfPTYC@&?_S}cxA z6{0+hgE+9B!BFM|?;85hdhng;mY4Ez+~rx7x$1Dt8A>A{n*A(On8)uu3U9=J$v{I% zPv+;EC5q(tfawzS`U2hYh@o}K$j12|S&uu12_y4xXyMK;WJ-m`;tYwlnEsZD-s|i~BY9RX zNYQjTF8;YsmZBW`!1@=aQD-G$9Ae+Ga=tvdfm~!9xW|p6TQLS{6nh(Cfl?y%9&FGF z(y(JC??EC|Iq-f0pP#hNyC&>VFB&!BJN;F2;#IL6~_5($&PlKAv}HW)JTJpJXKHctH5)8P+j#V+l~20eyA z;ymm0wWgmB7%H}&jiZ1F(yrZf?+#F)=9E@|7LO=9^_{x?NM1&T$|i=<;(`I0YgQbq zSIVaU$QOaI$ECV~rYMn0RUp4G@y9u*FtOWh3Dq-o99T$GD1k04r@rEWB2f6k5I8^+vQ;-o_8&g`jW~rSZTzIlpMG>nL*mjXf>w z)=mAF%nOeWp{BT7F&nkq5V-9Z7>ZHU8h~$V_%g3ut;pM;c7B&XT6W`}I9hb$_WNY& z9%}zufga`WrDy^J5rl|aW6+0C@Ra@?$q*8_A*IYhb8P{1CBU8LOIX9awu_m}@9D7e z&3*0mT|lg|%BL9W(XIguq+;mblz;_&wV->cvg_4LNTmSI7tp>v>WLNM7K%_oMev_d z2teH0eK0z|bLDsyu$3?EARMW0*pg2IS{FSodS!-QRlVeVd?b6))Y9MLp9R{O|I7v0 zd2Je^R1HQ!HGgGLe`nNOXy@~4914>djOTb1oxz$(#r+B&$u$o&$Schp%R5L6xS9$v zy6fMk^@DUb zxm&Aa@j3KwTXc7p0q%@Ld*HyDdPBD0_diusbX_RwGXIJi1?3`vRzVNrs{tPA-e7D=Y!0Y+=Xv5L5aerjHXb;Om!5^7zTWsYF@JIaE2zB@j^0z&}<1->OAf-QoW_(kdeK1q&c=x(1N$3p(4M;2NO6jFn15V%nPEvly zf6cx@WF|T)9T-qpW-o-M$fsh|4=-`XDeeXOoq*5`%sd5hx@sUC9tmJ&Y~x_?Bj*KE zOF-NF!B4f(G^7xD5MhG0H|l+0gv>9=iT^W#!9q*p+hie_*#op!Iw$A$SG|<8pdp)_ z;CwmP)+_*AJ65TCVLG<53MlzSN2JM=h0m3z=EOfc3WUTb2ii>*9=13E{gG-qoj*A_ z4)DlIba>uYX=_5_ULFku+^ZmOP8bMm*3HB3lPqrt;>&yyxI0YSj6wT7fqA_fYEX?E zEvD5Su{&aDyDSdZg~|ZlMwqVd_4@wzNgmV{7mV~dWRs;`>bJQdktLZBsSsl80j*F* zG>TG?bEE>XuRqpwNni-&VnN5k1iz8vU*0_8%pP^rp&gpYAVomGB zTWOu=P5)a%rX$Ug5|*vr-5vXS2dL%s*OCr`!C%5TMe9 z5HRIIyfPmQW(a%4I$Z$DxbwOhiK$1>AflWA^I5||jTITmWn&;SueDjx(+*tb5~v}; z1d!`A+w&u!um`wjT7j|CP*@d}eCUOdP=^L2DuFL-P)B@bU!wINFDWP8NJN6$`60)%h&_W9K{Ht#{O;wYS-TM;8t^=(S zd;-nVV2Q6a6^zeRAS~U`U-E&80Ksl>L(*i%u0fz{%%QOC`Rq4J{zXtqx$m8l27iu@HqJcepW2F{cq zF$1$H9R(=$f#Tom+=yU0jtoA=%Nw>dne!WQqT&IxSXWd?xd2>Q9$Z5kjI+StST@o9 z$1K6f?%z>7%BsjurXyC%@H@Z0 z7Sn^S9k+2z(66h195q)s0)-&~0~)o<(=#$0D&aU(F>Oi}OAKTYoq4*IW;eD6UsJ5(NpXb_U zXNq*wXY|%_@(|t5K?TK2hbOyklC~O^o`R7b>oZ_Fmv1;YCNjHkz@$mlx_B`6vF-19 zUdlr%nDxeL4dz&Yd#o*`XTQ#S?)bqXJ#IyO$GB*j+!RRMT>~Cu_nRsE6X*gyo&IG% zvz3q`&M#8~rp$aT!~=5(pT(&{Q5c}#H%Mo1x#sW_2zcYH!hnEZPt=b4H)x^(H!$sP z^xWihGuQ<*$z|Ar&jX_DlzsyIpjdAQqZqG3p1a8J1!v3jm8ngf7Q}jmM_uaTrmUO& zR=}K``@QisaZmRg1JIctsKO@>M{Si~kki=J3c9LC+Gu|iL6jX^EnU_^PSg?6`{%&; zi#CreYC0Tp<=;B3_;UWw5lalG6PW4}kHF}?8a_qt!hGE?rgX681DR;#$i)@fb_g3ss4$Ox6PgXv$A-?`R`o|yhe6BYl zRXG_b{Y{Z8O~qbexSTG@RXC5A=78xL1z(FdGDUUdz80Qs;9EW_J>20`B>$(E<(V)EX?VAPvzv)VK+0z^%kso01~K$>%me651vMu&t$Zp{a)MueLMqWkvK!=ma* zp1PMvDdZPb#5pJ-;^UtgHKObCr9VP)yu+>BnJ&?drrqrAAsBcWSbxb#5L!ui>(zp} z`K;12o?!R#FVTFHosU1I4YGf}5P3=7R9BFUQfbinG`f5T!|syv@D$+1ocL1g1xAOm zch9~loEHwwJe_aZ|j^*tekL89DwjNrL;r}VaN>9L8le&XC& z+!3{H8FkR2nBP~Y=dzxAY&=%sJJ7%XjCbt4Z!F_bwKMD2x^Z9(o1zh)m3K7g;8^yz zCEof=S8-cg>D{pBOSOAA0?+h{rJ31d9@Ufi%04D+7HVPD6j|3KYQ*))Y`l2Unrm!F32h6ZFD!oYBl6}%J!()+tH7tO7Jtf2pi2i zirT>HuscsiT>oC~8b_6eVFCCO^M z<*#+4V9d{Jp4Wnbi$=qX(K`Hm((A`i#zs!{Fq))M0!9ie2PVmjP=hT?0WzMV`fE<9 zhje!n{pbWb`<>@4p=_`aH1uV{HD5U;ORC-I2+Z^$D9A5K-PMt?Ujn))Jfnywm4rx^ z*g8#nP`cmIu$q0KKf&fD!OI?B!bUqou+n5*|N7gFk2l@=*Km*QQhdXATJ0TFgq+fE z1fPAwq{jh^5?rEQv`scy5YPMg>#@-?_1wp0|H0FW-{un;m8L_L$zDd0pr_Wu647Av z@Lt)w*xfHj+CApMN8MYOrAL&`FX>;0@&DQ1(c+PD3?}qf`?gC{w)J*$_SiL3uv{KC zBX9ZXIyY>=rJKvfG4)XMVcFrdk*)SF7Qc}KUDp@0Q^IaS{-;GN6 z@X`h{UJHr^ZF3{OgPTnO?e3`&&L^wLS)Cbajv3Zh1#bprwNEm8+08t+z$U7Fy`Fe~ zfnX4M(d>i}@pXS7xAlIe40i688R_~L_|*yGK@oF!$56}Q?XwoLbvmpZ!j7bWSfF5Z zpLZ?0vcT#z?T|#0ETdvc7@XS;uHmMLjU{oSl#Gg2sT*oBAouS-La zRl!@7tk{Q+v>%d1)w+P4I?S${Kc_v(i)T_^f$SMT4|zD&45NsX{m z!vsCZKILCNvlv`pj(dNe`#zu$X@Y`^a7NF4&*)=d!FV@;J%LxdmgT6F(IaF{ydFAjQ@9Cs4?UWBJxQwK>} zuf(?Ay&Oz96I#p(C}sJ|jPyI%YMi-<|4Io_Es|y2FU%W3^_b%aK$Je5l$PmHt+58- zdG)PuZ8AupM*hmgr}igb59jl|@ZbZ&O$fwSSl8Oxl=Ddw^2%Q%7qL$X4s!ZhdE4I~ znu>|jX6OQf^7L{ilN4^Z7X=RIqSwnKUV<3j1J9^58>uCq-%s>Tu}CU<@)uggt>3v1Z0 zDo%saf=}*%F^NH|E9H9-c#|U9n?B`Bn@J_q{geB6R(?x2b!1+4#5$CJPGQY8+eQys zY^2qd1+|ZjI$AOWyxWDyL?(}_Ul-h?`Qa5m*MWTVWf2i&DXZ^5%U&mCNof5k&D3?0uLhlA+X+o{1 z=2d~cS;8eI@x?a3<~l2~2RC+4_6|n;pQ%4b@gLZJ9JQ)9VLD~|(9&nmC76FFOTveA zv9|B+`twJ5kN`HnTUHn4)$0IgD7;0~WHShnHa3!$WK$0kqBjaMriN3w?@EHF)Hc|R`gjmORO{t4nb16_91e?Uq%WK8zGZ1TDLOI^ zwP*Yg`I%5NI*V~&oIk>3^)_OxI37QH)P4M0$jtArcDD(Ej@4#EU%>jeFEt~-N~(VE z&bx0-N&-=-5_39y3;>90rHBAu+J>|pX>36q6}p1MoRLB|g{xJ`~c zpRR^s_+g_#mR*@(+Y3qw6S~K!6sk7?qt_GA%Og=Kp`!Q-@I`7(CjHf5!H7S3ThK}! zG~xe}Ah2AZO3F|=%-;wB0O`{{B2VIx>C2yt$h~E<(kAI5&i&o5=x(A~FoD8PzTmXu zWq{ime<%qIE5;Xp1cM3qkj)S~0In_UOHzh?jcJ=>Fsj4z;LJyV?Ek!7-Q!3|PY=F; z2rqTfC1#RATjn$_KLB>J68B>u91lcEM`9fz*NSwcF-*jezXyMD1Nk1Dm3w9j=AZdl zc^hc=dR|dXMt4n}Xi=u!pB_p_P{YP0Yipb)iXkzXvNQ50j_@S@xlMYy&HwU~wIg^G zOS$SwXb-^_p>^rvx41o{AS`~%DnVOQ>vI87H;Bk9_K1|>(aQH{Q<*pLD-TstMw1IQ^|%Z zPecYkGN}i~S#y`OFmLN$S32Q*+pV+rstK^WMBdpK9x}wmjK7+4jMDHOl8@4p;{Ibu zaea>8Z8nP^?sjH$K;KBogq`8s&DE&5$!=ghTDOa3gJi(|HBCd8L_{WxUm&TQpA!>Z zk#!|AtDEbVBs3)=?t^4*fvL6U9W7>-CJRa)N~k(G2ip;YO~<}CH<5f|pVE{UqM&B# z+>9QkB`8A7lW&@~6TSb))K0twC;29Z3%x(Lmg}L~D&b?1i>ngGNzN57U&-HL<+H0< zKfZ6DsWf73?jw_~;>a27J$ZR>h<>L}9Y&zc*^(gt>Tyh~ACu&bQvC0RtvL`9R|8|R zYV!G6)`~8II@!Yh5!Y)v?C%z9ablEaqzTcAh^Y>nw(qpE)Kudh$&UK)e59I4qez}c z^Lo_LGU4Lz2i*7!c{`R*yzph(ADt{Ts<TW>|WyC4H*c z=w58M*j#hiwiDVTiUgBlYH|`8wujPi{ltCYgE>)YjVrdnX$W#2yw=MF>>PADiu_r* z-nY%zgC2Esq=qZYE|pPyMfM&-`ssdB*}iXO26B|~J_oq!-UZ>Zdb+yo^nb?A6Rj@5 z9<2h)kukVl0=G$zJ?PuQU!UX=QZ;r|c91LD>;H;k>Vp-OUTWTb+pCt&hVM zg*Lu{EcUeCM*(1JY`EUEWXyK8C{ph1XZcltTOTT|DHsp5BaSxWCO8*X7nwGXcUzKCnGa>`n;mRz}wCdoyZ}>%hXCKHntGbaxF1F$~{RCBPe)qSUO2N$mz}PC7e}RpuZq` ztRv1Jqz>%_2e-~c^MQ|Diy#!7OOU++tKEiyas{7w$cII2;HR7Hb_#u*-3<0tavz_H zjY}@XMYz$dA#XB3;K5pd;L*4H^9BR|1Hah)x*`Y{k3irNv`EFy>6+X}&k`!Gmt$R2 zwuS(y+!v`!Bg z2-+J*#ZD#|2+`++^jppQQ@wJ6s@tY}jH9P-M1Hk8Gp8=XObrD>hqChJi8Ucrd8oZO zVf?=~9Ts#j(f7Ylr?u3BL!nbm<&#ERbiBce;iq>s%PVhhbu%q)WwI~HEysDWHQ z^_QrpW$GH?5*k|g$KD_*sX9omn514o(2g$3d$EFlV-n8SE7ZK%xWs$;*k-VfC{pAP zx(SqT6U^i-?7&lJAftM{oHaaVSl_`!1E=YriKP68siS-Yyrjt|p>8FS*C;X?eX<|P z-+>b~Vh}1;PikG-BoGAvwQyydcII&V=}pG^fWRu zhi~}dMjAF-TSZvG5mO!W&Z%AHu-=YS5Vw;5V?d*UEHHZXP(&l%8HkWyUAo`*?S#3$ z0T(9jj3-0&wl){UjRP~huDvc0_j(yFn3Yv}RwTJX3*v0L(@C`%MvM)<*tqL6DOXK% zszq5P=qDHJxml8Y3|M?`bBR3AN?j0r*d!VNd^5=3)q!VUHMg?h5{JB9*y0D z^rNZdlYE>rgGw0P3ZBeTn^307#SyB7%$3ohYMB(I<3U=T!kHlin9F^2uo(yj^0~}r z#`A_g${W@zYe&R@d4HEOqp$TH?lZJRxPx{%3Tvu2M)(3WRi6}pSNvfEhMN`R+0S7u zHMq^TKY?j{5Xe5%AU6a8B9W;b6u|&bH{`OxZrrB?>>`qWe~yBr3p+pP=>os0V)}ko zl)<>p4+6zUTbB&;bbmJi;n2h3_WSd~AcFdEVgt3zCN|c=)8F8hT0W_nOlSR2C&ik4 z^L^S66Fsv{4Nzp=E_oRNtsOFi@>L5=bKPAAHGUT}#fU#K^LUYVdS2za*DTq?g=t%# zgAB?7RLp$rO_a6sj)3)+5^bKsIH#b;uzq|^6j!e)vF-yNT|D*oFifl=myX)tfmSGI zfYxVhBd1Q$GlMB;2$SV38^<(xJcKNi1{rQB;Nt1`3R=Db)sj)58{6%7GMuav&etup zzBt|%P-Kg+iaGxNB2S`t7GDsQ37H*);4Ub$@xMP(&BLHvUIb+CDKZltF7o~2eOSXh z<#_`kdeh|wq8vzm7l>lZeY%$ukN1u1IzQ-+eR*S-Ysn~q>vUrr-)FnlQS-_KD@xHR zgLY(Ua$8Q{g5Lx;IS${msr}AnE^Y(_r~99s&1!=a)E>W2$N$N2iLJ-x*l$3*qVsxb;Al_us>GNyw^75pKf z4#`Lt5fj5gEp-?RIh}z0=pghd8+ptVpHDYX!M!U>GUId(1{HP8R5VYdeHWgjmi$lh zNHc*B{Xcl+Wk^xV7l2ZkSQGIcLD+j9BS+&gf!6T#fgMPULvX9IeUs;v<3)C&Ni5$~ zw+ZTvDBhw|D9QKJ86eOXhQ>(&81F)BFe)|Qhv&E@w7c%PLUB@wT&1+lUzY17dY||7 z?1#Y-7j&k+0PB`+(v%M)dYD)(SW-~s#RJw}2ZWAr{t#EdrTn^E_XcL(KWyGi*l>6e zJraL-1G%`&yy#B+XXF8^lrL1DBv~x#sgy5q-Yzq2)|}^5586YLx7savu90*~B$iu5 zAPjmAB~_6c{5lv;A`p!=we~&I*9Q6yv7-I)dkmbiCzj6lmvwKQ)xA#W_Fu{P^-7Zd z*^AE5@RCWyDO(aqen)i^R*n25r*T!`IX{6-<38PPuQ zOp*|~5I&&iZb929AtyK+e2&OYHv~;8zL;OnB|lnmyH(FsvvK|*jcOQ9N30ATr1C-~ zNaXXgWKNpY(LRYZ-8NvsPc^;rr6p$i*kXMU6d&KK8(k}A|C_mwk^d;WshYPN_7#tt z5DDovn#=1(y>1=^13!~*2EOaoEU28Mp&3~04y{ISKE%c#R0FTPeY>e)PssO>GPqRN zf7xc*Lhc?3*Z&gRt2w_i+FVmN(~jiZJye7^=bOZ+eRMYw8# z?Pbq7g-3ei?%wH0VOrU*F6Muubr*dO#XDf&pZhi;BAia1EUnz(ym$&}o%(XLrN(u9 zE%3w8Gi-QCJ%vCs1vj%=>UJ4T{$~jd8vrji-7}XCp<+Uw;1OCm_H3k|FR%tseX!R> z#`pS_#W5U!-UH8v+jV@y?d0ut=v+k!5f@4MTEYQ09LYyZ0Md;D8bb>;2C+kdg4r>~ z6>TtYTj$cj5T&TAEzK$v>@t3(7UsuDAvQ%t`|%t(7Xnn z%;(>;$?c8Il8o*s)JeR14><8)qC{nXaBQ#LS)&z zOMFU3cKR02{1a2OA@>)klDsyj4je{sKCv#<1B((PD~PZ41n5Q95l!@`Oiv76{4pUI zKV2nzE#LPLy(x_IjT-Q%O|H3jULfk#RsF*xmaPEkQ2CO3XC1rTOYY$076t9xW2=*# zugQ6;3ZeV}w!&P_9{&QwJZbkh=~M@=rWg@k!tgSiTerg}q48_)nZ7dZ_2($~Syvdb zFd>OT-qsL#=2@V!V`>X8t~uH=@jNW&ny~Rzd8j%ncv}!n$mgw7Eq8HTs63jkK{ z5v}imQ;FyA<+$j`%PlRE^rT?t!~iaIvKc=TKH*5-TO+jawti4f&61ofe&%h_x{Ilc zASH67*eug*W%E-1ZAuw)iUdpfHcBA2OU*>F2;UnD`Mt{L3ZV^D-=BL@RCJRyWL?!* z->%H~T~Po;2$Y6_M6b+_aW1=04e9MbqM2R?b*Z%Cs% zjU_0t&fTN5lWVQ&4?3N*cVD^(2OAN1hWuUh&!7Aj;0)CrTqE;V@l?%=sHIbtw|Wii zwS*2kundNl_buG_Pp%nIId->HMv118Kfo>o7Hf?jik!;rG!yady8fx0kjG6$FGUkN zzC&12g0%fDDxoz=AE;k=TM8529@be|{aabGZcjf8Tpd0(Tgv8*Fe2LnGI|S`Dqx^5 zB&z!}i1<>}1Cy%_B3vv4A8isTjSV|xjM<&ME$$$7Ha^8V-3r!w_?L==(2!nE+9@>K zoW8PHXrk8;M%|iWSXqLnEj)()MUYYU$I2Lb=KVcxX@$UwL!GXK6dxL z46#czgu{vtKd)oFlFqC%eOpQHD>eOTk2k~7+q?Hv8lBuP-eM*YvV3vercXL9bcKqg zgrCcn8FLKdPSu)O4{P9zIkgpP+BR_Q`aZ-SG7rDObsP*k*<&^obxzaq5^xyqGzdYJ+THK)sS%@U9Q}a zx>bSjLF^s;aQ#I-u-wieeu=+Og|6~07YGg(oGu8Hg#%zpe)d_8e_G$YH+|XNb{O09 zgyu(H5?6j>l1%8QK3#xJwuj^!>|ztb`AK{~`{GJ5Z~ftIiPs-6&Ct`DIP@HrP}KZ${3cn zd&M6moJc&$23giAekpWd1>dIFL&TdtkML2`KK|t}^h=%p*#{1TQ?}LKU6A zG`fJ6vC*w4JRb+xmn}Ey@SYx4HnSJ``%mp#qXo8cn|3q_rGyWfbcC_;a93WwF%e{i+Xeqs-}J zI`?I?RJ;cuudu?0z2-B6+^L+eyc1Neu2mmpwTvwU-l_}S;v(sB_O2zn=;B|73I1YUQH;EW6l>hG*C(1FiMBVZ}*JA~#!|&u{R?inemo zlw}F#|2dp5nE!E6wp)L%lCgI9q_Zj{m+ks@MF*z zlXa7+>TEmZ}smgD604s8~UMp!& zzd>F)Z^h}|tFKb-xQ)664E7%puB6k1lnq33FO~92vY6x7T6YcO_}t&LtraNSMS9xR zO1s{`Dl?VatCyKtlILmF#s|bwa6sloR;hQnH`E{A;0sLGFC)ZPy6y%^10_t;y)X_)2a<_cL7Fl8Rqx>@#-9eLbctEa7n$|&eQT%g8) zjyZ`U`I$m*ZxG4mRbuR}J_f~1hTHC}y^cU(>A)~#3lI{*F?vg2R~a0)q0}r&NNQF_ zu@=!f?F7jq;Ur-YU+LD4y>xhG_d_!x@?j0BO6=^zDHOyzE$qe|nm7r^3<=pUbi7t2f=3L~F;Dj5TzGLlj-v}vCx(BmZaV5JKyt;okK=3`EL+!dd8tHM{lH#aYw6|`cCIAzDSd&v^i!A z*hT zr2U~N57DPIv>`D+{63?pv(00$Kd7K)W5m?|bN}g=`rqLvI?fyH6^F2~8V9-fvY2E8 zfZ{M{GuIP2qf*Swg}i#capv@OXh2v#?8KC9s3M{L69m1?2T7VT178+NiRn>=WM4bU zxhmKN%wjTU|G2dBvsbBzKy7C)mVWQ?IARSRzwu4eC# z&n>k*Gkn9PhCr4&c6zK9_n)haK!b2)C08K*Bvk_MLvjT?s)sONwSu%C@q!z!F zF$~eivkdd99Po8VkV`3}e06YUb|~$NWjRzKoX+$J2zYo5@37bj9-fCj2)mlVTK^C0 zgz)V)!aw)hfjx0hhSzfePya241MhCwdgh-B0Ojo^R0vPn-~T($VBY>a zvF43Tn`$PB3CLWqU$&JznF~Jt9X_9vJ0Dy;E;fINz5L-j1p?wtumAQD!D-VVB898| zokjW1UvZ}$J5tficza_M=Pp7!>k~DE0AqH76JlJFt19}Dzf=_b$vdQPS4o)b7{RpH zcLo2?7oPk(e%z=3n;`QX3&EXc7wV&;LbG|8Km`zkWsL`hUH0|9TkV$F2Ws zi2m2({v`i{D$I+Yo z)f?@926VPlLuF(o>!j!AsT+C;R5vW}_F9pD9YO>f-p=z!KrBL0%X^6+v=TI;`OxA5 z7Kpz?It>%jGo}G6zoY(8Wl01w-*3+m+HXQBBuiQqRrLo)yJi1)@E%sZO;+iY_;kY` z${t{kR${C+bU|RvsW!DcMHhnld_HaxHq3V-|LrA&m&d$+-TbELauJLfWET$-xPj6u zw$Bk4HSO;fW|Nt9XKdtx>!Tyjd$$$p?Bema*TjDHJ=nqbApg)0 z#j~?n?75qaLzVJdn&k)L7|W8;s!Fe%Z|Dt#dE#S>Bf>k!nEyJBIYRi@etwHx-HL#^cr(@F)V3ZtLu~B`J+=b z5@hrD3?_eXy5E9LB14l0Y9n*6|@=Xw|u9clw5C(yJtPFLbsQdvwt ztdtS_$)WsC@lExu^f=ypJ|%BkeUDc&yxF|SwUUflBkO;W4eFo_4DkCHtx@On3U0i4 z5gTs6Z8=)5ui-X>P?^50Y0u1`OtOwapEa^IrLl;nuvGo+uICs8)aJ>=L!OWLPez=< zAMXX0&8=y6E~(M1F8*5ysFBu`c?!9@c zRjHmQy1LrG>|{o`o>{5>!Jr6&c!qc&jKYv%D9H&^-UHpFgZ)gwT z9&X>h>uXbXSMdyCZYR(Vf=@qo7q$d%A#;$GV2!5Pl+o$T2zEfb^T=m=VA=oo7g^bLvcU|}sH@tLrrp=_LpzK0(Q^pH-X$9`9P1ZmU+zI^{vW9Hi z8pA;2zdm~iH<%YW*=isiGyg4tOdt>HOWeKQ>kt~cYml#QmRw+Be!NSPTrz+2;;4^Q zy_%fU=40h`{qlKcmgF1hT(mR=IS#Pdmq)5hdye1`X^O!)cl}%AFiTECt^S}a+g~fN zWMAJ1z)#9V*T9ZAN19be8C zd2Q24ELw2g{m00ODLY`SsQPg2gJQ; zSMX)Y{gwfwm->p1kk?HclG~SN|0HAS3|=sEk$nAKRPF|^s*k0Q7sVPPNq*+_f*71} z=fAa>nIiwGN_q=~%2($dYaI`~Grg}<%iBGui_tNbTitGkbY|*@vl>0J`U$|F11>ug zlx_{|g~8KmX2)Ckn?>a_dndR4qnwo%SkA;wlP6$OR^a%9t&GR5u(9-O8otw-oY$Ox z!JWx0ZYq71&zQM$TQ8IIXH+_)%2eAk+JiZS^y=h|@uWyoCDfBd=IZ{P47+c*Th1x| z;}|5?+bnd&EnY1g*)pi-tHjN+k?kEEd-dz0+ekZD30)}XA6Mkt2B-h{12fPY|B-9Z z*eKeU4knT>*Q71cl{SE?vieyUkBKe>xjb4#>rNBa@|uZzQR$SC-Kpq(&xJJigNf$k z+}@_OO4Po(hkOT`02X3dzox_C4%zydHLP|oW0-cXjfM+6kSycU99gH3g-JR9VcP6d z&mndltdb_LV~0Hgwe^>3rOo=>|IY^5z5i@v>}xt|AW!{#=PXH%<}nR{<#Z3hgQ!{{dI2AWH-WzNC2YQZX~jdo7P38qZo(ET z^L3A9ef|qAX8|c)%b1ZZ)VQ`{>f-PuRR#}2_W72 zVJJJ;Z1^GZ1s>LyS9bESCxY@}9Ia$xGr3y3#KOJ;H@b$QTGB#UgwYJQosjaOZXSp=-5qD|y2^ zR&8|BuK&#Ok1=zxM)m38kGW)9tRsuWaAHv{TCM%Z;@PrUn#W-JD8n*oATH*3>Utv}>+Kny^~|kpRg3#ik&0LR?rPH85DX!u z58NxZaew&~jcjG1c0Qq@FQs8iC;K5`Pvaea=J@U@>l>qN@EQ4Qf$$McV)DSm&3&=F z2)jJW1l8*y=@+)P=ahmlr{4Yrb3k-2>)=xPIF8`GTtL4K&b}-GjwuRN?s(OCvmolN z-EV(B9!}3Flv6ua-dL2hPU~Ih-TiQ-{(E%O(!qA&>eAhzdB$704~Q`yk{^JT6;*$& z*_QOrsxVD^?J4{Ws=TuOTZLw@13)kellV@#x9U|j-I?o5($}ROT+WU>|-ojGxOgI?ibx4WfRp225jtyu2(X z-e+}~D?Cmb;_WvoQy8r>5pwMGFS?^hRwbOwJur{D0fom;?UiQj2PMAIyy`9M9^Dqt zT{_iJ=4_|kExMsbD^1(lB_ANrH&4*uNARull)2D zU%sN~K;UARdw>(`e1BUVJ_}e z^W9TO^;VZHq7y&B#tc%*g4ol~Y3R~ml9}2+VIOY@+sBry+Pe03y33*$`vbl|m=Ida z&Z|Og0Zy*64OABmB*Q(%+N|VM!a9XhXOrTa2VTzmq)hF`#&)MQD$QVovwI*D zrhZuM%!M@Ox9rw-2O(Lj!*s;*G!<#)x?=oeLOYM7FtuSQSHXSp{pyPs)2$&=bGa)Z zcUHvr!B43>mK|h!03`R5uUn9+)}*)KEjVI*ZW}?6A_^Y)&dC>H^i3JuC|6bKagyXC zqg#4Q_k4jLQWoxyfAdw1m)CNsg}w zm@$asGQUp>f5`W0*N`pR^m_bpQ0?0q*h|I9YL*h(8RiFRk7Z_S15%ld6=`E3vUVoL9MdU-w#F9f3H zpW;{Ok3NYEyUGj_hSy|9Yd|*p@p)jbitGMF0)NUg+aS9`h?ThSPi_6g^4I0ycrL|J zSEn&nJRdfMa%)9cMGsEys@A({rv{e1tt@?`jMLz6a!j4x(y2F>Vzi(fn4aBsChHeA zVW*=RTe{Ws^4#^30mmF13NGgUv9KAfng9r}jtpv|>tPHIC!ZS&fDbjx!y_I!nIf%ExibPGjL{Y-<4u`x8>R<1S zj*jb!sl`U*pvyTJ!tE9sx4Ekag@X4)xlh4JEPi=LVo2w1EyvJH;E@R8P)}ogO`-Km zbwF8;e{k6nXjiOJ!G;O8+y#8Cyz7J0$H1qm5mqu+Rj;e8md&-uGvhPWyeTwVET3talog31lj z|E#bbKlzf)%ygkxMJD8$c(o@v%DHn|d|TWygR#JQXY88mIOA^LapL1Se^~j` z!RR>m3B}5j9A#Xei`z=c`BvwDzSfb*E-MG42{~WgU@jBK5mswS;Foqe%nXRkzT1wB z^!7@h^l8Hlca-Pgw>GB4O7RJO`Uk%vk?%f*<~5ngSiioU92N!z{prL1>fmW)zkziSl}O z5MEuOp+#50N70B8+W3K7#Pmqs7PyAqOwcA=P3(h8y$y+^sjD!Id+MAvVSiF{58GiN4}J$9LB_dY@GEgAF}k^Gbsshkjj z{GiSNU+C5jkgC1qmkY0W8)r?t+h3aFdx_o;(6FO9+8xD_Fpo&jPs11zNLG={;|fRL9)}r41jv?sSa`d zx=&YpkPltDWsS}IfK?MI91d)SD%onftF3GBzB9O1D@;`dk+e%jZmzE1?)aT#=A*r5 z#7pft5JMH{gdJ^)bbLiwmE+VP8C5QL49z}s{NcW`o|=fmyy+u$){3YwG{*-IZDb_5 zS$39Lz(JN)Zy8zMcWLa(r|d)Zl1Eji#NNJ?^+N^w?B@cvC)chf+lPIC2{**6A)YDU z(+PIzDGt7?Bf(~)gN@-Aj<*d5xqd}CTF*9TLnXs4ryi0SvXS8`6>dk*5eInjlF8so z2KFcgnonio_o!ns+)yt5{4m-|$4()c`8|?{a?Yb1*Xh1x?>VjUlYGL+&E~86f4u-2 zlG-mfXz>cL1~=a=50OJQkvh znIRj0A8G&H8ZJQI*O0A_3bO5VGuGnDJ|j;pZPjszD^)$H5XT|crsmWa(V10=Uql4- zKKMv2KtTNmaJ8=$SRM_J71y5|yNX~E*fhzR2zfGs>%3%Iv}%w(Np+#d>NNFrI3LhG z!;@G%QaZN|weY&Y0*@K?t7_FP8{k%W74U=@_?>qP#q?!v=%$Y(OSbS{d?uAZ=dlF` z0P$U=w^Y~o>}(aF{_H1V5c1=u5Vm|pK$01_*g@9EJ|B=Qd^TML5N!2Av?*C$rgW81h!#OTGh>8jIgz zPXm?JWt=(3Iz43e2ueCQ{jsAL%R{naKKNlT%eBew%KFI%JS8hmaTz-tWmoip-nmIr zDJ+F0gEdFKryER;E9)fGr9?5ApA==j=c>i{pvwKEE^-?X=_qZrMc-mvCy|-DP^UH7 zfn`U33n%uCA};nBl28tpYU6sT{TzZ51o$mzNJRLoiL-lFL7$Mg@X*L1pZTU8WunS2 zVw$DRy|a*3RW&-IB8bd)rARP}7{U=+)V`V!)D2v0^(ZK1f@i#JNdkI}vhD=D4U%ge zj3Xc-*;I`C_=jVL;v`G9wbk*J#y}z85pb=y#9G6@)a4n zY1#@9OtF7GJWt;a>+H10T$^%Q@#{v|YHQgFccWRQYbEHA(d7lRk;By67|QMO*vCRG zp&5fmr&m*8rqyiW@Z{En{G^)eN?v_XkKSqlnj2NZd$9+}$PiOnT3W40oKiY$u40{v zHda(~or>{n9F>xn?HuTdY$oxanYX@)G-4mmxmG@ZvHSp++C3@gg&&cUnV`#8!1~8v z%!>mi4I~y&seX8Fq7}YYd$Moc`NNdM3>prHFN{mLCDeiv19L`cv=-c*ZoWfA zb2VZ>2o}fdhG-`WP{>z zf(7bd4XThVIvK6(K8#JVS=u;@wWP4zKRUT9`KSR=)cnCbOP9rS^TlO{N7Ui(Joqk5 z4L?SY_G`g5>@VXCfuxh=jq}^*8&h7ja^vbwn@X}$zG6J=tYv!dlB(Z_9!rNMG6wZl zBSSSl^)`2vD#KD+{}_`UgCf3qH%6s0MrF@mk*`5;`sG`RvEf!ScP=)ADD4a5jQZU7 z@fhb8X%}0}O%9c`^VHY2X>gq#*qrsyK6_wyAmA?1ko5c7DdRcUGopD= z=?*qP>7hr}?%crS^=wME@w)HJOp3{A@ z0q8~Qw(!oTm{{9E#b_ad( zTTv$WYznbjoW*pi$<<1b=kY?)ifSn}sQf3BC0~Vm=|BnsB=Cy_c{!X zAqz^QU+Kl#0O7e5F!{*}oGp8dP?x>kKBtQ>6ij{QDV=D7g+55!?8wIxqGl!ic*0wY z+Ek++@?rCz42X~szW7H|YqGSs5-AzzdClYZ{BB{_?|=DC^TfpgxwfbC?pULi{pU4y zwZ|I#6V+cHu`FOJcsP+uJ)VF+E;*L5DdL}C4{$+)1Gb84pM9hkvO&+{r>S}9{5u%) z?7&>dcxjWj)sxQlJ*jXdo3Q*n28e_{;v1xS7MuA93clI=KX=_uZ&6O5 zjP~hrO*5w+LrIw&m)p{>yEp0YvQ=cNjW~ORDMBBLm=2YJR2m7AR5lG02Fzw*%_EhK z=Hx)5#PFW^v6u8M%qN}P8v~7i;KC&Q`IVG}zgHUaZvk8@JX$kwfCZdmmfg|p(;&Zs zX}C!u8%yL0(qgs>NhPpLc3Ir>bGzmp=QuSVEY|u$>t996rnB`;JR14Is4Es%YngHJ zm<>UYT$UO=7Qf*eRjZp;=SE^3vA9rl7^v$$V!3bErkge#io1VZvv+CvpygQ}nV--R zNTnkWhdBG*qxm)V(zk4`fl;l*FWaxuvcnsZx>zv+3Th~H-u&yAMs5};wSPOaSKKg1qRAVgq zQ(n18S8V$$Y(q|bi46wnF26EYKXV$g{jm+SJA`lMJ?&!&ntvE&&o{E#aDCF^rOFcb zB^H<1qGqrp*A+k|_kYkvt~-05{N<2sklLfiD$)+FWb_}ak+SfN{RDnq6b2YwCpy_4 z#`KnjpApZs4y^tA^>C+JbIty240xE_-)2JDRQj?d_%z!!e;tKs4FVA3t1iX+$h%as zoXkoc&q%tSV4)kwP)_$l$`Pb6H`k$KQ<;wSY1!2)<-*T)Do1f-n6Y>s({I@}tK=T9TA{4^F z?3uJiSNb|Hs{mXX55?>C`L8?vRNI18Y5bAm*B4zt(c6yD3%S0?)bg*=W%x(@e_mf& zP?Edr=)mF(-ngPPD}a6WW5004MY~?<4$D%*b$vd^Nw$T^hQ#|l9pOoLF-ek2>7yX; zDvI_QmxS7(B(W?mSIrAsx#%ZvP(_<+<649KK6P+bpOw$PWA8B{tYTo-qb}&+#8a_< zw$bH>cADLSI~bq~biSH{S~l;OAhG!0)H}Z&^YFv3U>aBWo9tfSHuugD{od4nZdJ63ushY1{d-IHz zNDV&(vd!5l+#G@9F=}` zZmat>;7{@{(;NIzJ^i;WD$rkt(i`!pJ%(aR3J1{+ zv=JPY@ZpVrq?Cn@aXTvA#3#o76e-?W72x1juz04Kr^xbxiK!jX#a#u`k1gH~ksNJl zDreK2nkxF*Ni-AC%oVsl`4X?>QD5)tMh~f>l|(~Y8qX$)&_lL@bLn2oK<8D{m2XaC z=%o~1M)oIv7@e#C3nSsktWB0WvR70xYReqbVnBuxb1ZnUwQ z-+vVoQ@*Vk;k7CepeU2|wE^K+#21*~gOi(Z3$ql%R8etWbkStLK)cUx^37o5ptm4Y zAw~iW)8_LhyG7jmn1gW|P|4fl#ba7&7oNQ>raOvGI)9?hm9d?0@WUisZoN?@Vc=$? zNA7Y)rXP4DUY~*w{1R&8n5(POQw4hKqozchVu=Q^UJy*sZQPG@St@#DC9`&drR+8` zp6Ltq6)F=ucjio!Nv%nP{4hP8JR0q}uv!A45YqeE?-6&&muRn?FTTPQmiM}VZR7~O zKm{c)20(P${gfJ)Hn)aFD$+yr=09!xz&ZX|3S$VSdf`rAe+V-Km|&32G)5Y?>y;b` zU0q*rQjT=HebLIeFEpD2iiMPnNFDA~Bz~D4XQ_!@&%|9={umizV>>{UL-i->(Sqiu zY)MRd)9Qq8LK+^-_spJ9u&Q;gyj}j#C>qh~1}%jjAG+cwp59_pY=60NhaArWl)^gj z=|32xi(F+akVtyepWx5z@+mUd9$m2?;=9h{78F`Q}|(spMSV;M|TUbW4re6+oBCS` z>ucHtDI0gIE%TN_bH8MzXehI-^^^H{myendnB z&W_?|6cNbj0%^IkIwejEs-?DMbjFO%Dy3z3Xa>7l=x}o=M{VBAIC-p-f zNWtBrO0>y>ApzfxKo&};q`a5u7ZClF$KToQi72*YIaX0Oo#h|cr?@|CAI2*WJcc%* zAZghSuePL{d8kh=$n_-xK~uB0!>=F^J0eMf-l5*%;lI5HrruOMOFBnMADkkVVQgq} z0M{aK6RrSTCEe=8U_h5fmqb{wn8fS)Z_1y@9@o2!LjTcj2D(s?SY;|NyZe2+PX~8j zHss{`2U^@jVEGVb9eNqAnxN-X>;2=RlDEO>+ijnXltWlA^vpCC2$+u5T@_xUd;8;Uq&#?(mC*-00b5p9HV> z8Z)5st3d1_yj0oAvJaKJR(R%?s9nLc5TIiZ>|IGK?{u)QcUF_+35XYa(L&VFi}Si0 z4av-THf=+37&mE%d1~*m?SkKOixN*hbB$*pGiI+U~;WDEO zaRLSy?HV;9*7|Ti!pp=bZ9r_2DMTdy+m-!O14(`cTx8JFZ7`$JxdzQ@tnKcr-pOoP z6{fn@pLyP2E`!Sx9GXDi*662ZE-I4` zf}rH64LM|qPu-wlGQFXAbw>8RJp4l|&l~@n!}Elb^#fW^YM)OWt{B()eM#;s$eYxr z5NV8veDH#NmY?PeLAaw_n@7}NVP#vWslNbSRPvdf=trRaD4Gc+RTJ{0U*@L`o>c;0!x$jwSpfB$fBZL^LB}fk-Kei6gxXwkCWm<0r zJJ%%z#ce&gc0_&<8G)0oB!Ur2SLX);nTs%S13Yr#{IlcloD0{2cfXcSRb9JN6n~C? z1wXbloyRB)U&E>LVoR08xFhl5XF*oh2VX~0VlGS2TulgarJ1Z0(cK1UOfE z2o6DHPP!G)|My-sg89cWA{3qlMkSV1(+cCJ6CXI&`nGGjPk8i8C~lYl*+S!&tnQ{9 zlq>8J6o`%{qz?w`bm1?fcM7We&tsQA1uq@ek_$v@l{w88Ukq# zQ{donFr{oz66AZJ`}SWo+Hw(H#MsmUxow>^4ADz#daBB0BxRy37J{}K^{JPT-!!a@ zv~r_?7>DK6V;9l^@NAV~Plf}1F8!fO3Xy|@32AdH49%5kbhU(vIfq$yS2Kqhn#-JN z6bFE&zEfBFv`v(*|EJ#18{`#d<^52ZNw7Rp@k6}*`935nYm;)>DDMyTKWrsB z>uW%o&am;dw?E5nsru3_h z=ZFSqM1K;M9H0FKft|pfH%d)~EZ!`G_va@Xt^hun+uQot21a@!9HfjkkxG#!!!5|- zSdC*+(*lY znrvYxgR(e7!&ny6)e$Bqz`Cq`SsCqrY#zog!qYSdpux~Lw(!9{txUHVFs$?ZHO>Iq z0euA*BmPAon82Ye4=l`mGDCw;FWQeT!Qo*!2{sfA6FPGlPqQqa^CD=-^*PXh?kX66 z)NmmZUb7FA)E6cFSS7=td+kd3BJ2vFs2ShVM9^ z1_YF#3sULnw&o!dh~tloyj$l{>n~!~!76^;k7U*j4)0aUU0rBybgwycgsEUdAh;|U z&5$}&jQ?#sma9caNw%rAx+lR6ZL%JAwfPDomo!O3jVJO)FcMDE2n=d&7r{}boq$J= z7?3VDkKiO5Hjl0j$T6AX`&Ksc>d_OmW`6OBm8 zp4o=|LIz9$p_Q=@ScbmLe1ncfEmf6d*l{j!?Cl2oKvX7&WtnLeFbNbVBne4}{nKFz zjY^UVECBy$6Zyc=2d&^@6`?oF&MNIQ8dX^Fsb?;`4U!A!2?MzG{?3bI`zql8#!VKx z-@xN=R2(v=rO0?{=bm)84Rg+9=+R_>RRFq$rruRE7edEWFBRaDHAB>d?=j4#_0H$- z`_k;qnWY|g|CrykeLICrPn$PNV+uRo;0)5QUfDl8tJ@-TV$aXN&2x+UWeT>;8cR?d z0~P5zFfgz~D=bX+e!+<}WAH0&a7V%RyGLk`?EnkCIYj9Ke~TJFjHV(zt@S&3`~*dF zkEvfekI4p@yHPHkp}DrZLsMxEb6B!m(&4RYpsB&lwV+N}u%#;J2aMYhui*C>H_Z_*!vQd0g~? zsBVS-zyWnc14uPc0KrV3a!`x`t?e7D@ zpP~u-c|QvxB}R}UB0juEVeDAXg>OWMzq@&h?p?Sc;+?zVOsw9%a|=#zPo-VozSWt7 z(sahqVaY zv5q!N{P#3~NQ#NT178+Nfzx%qNGpT7vnM+ z%^ALE*U3jH#;f63k$)$BJ=n+ccEZ*L8)EVsAJnPb(5ZG)^|I?v|E(!q)^azu6yiJ(IzTn=5)6%%}=C3{%`|v z;~^mqsJN2sNgixvKa^ajpEZM$_9h*t`$*(v3%FO>@)*b?0vtcU65*!d#xVdlWn~-0 z_r+SNNh1&T!UMSvnhlh{#F(=}&BWijHmvR-(HDQ_T>32z=@q%@dH0m?2RoY(XbIP@ zzu*5d)%(9*03vj8O5HG&!}yn$&j;6^hTZ1ob^JkW@~p1aF;*^2KF|GQMVUpasGr#> zIBZnnUY#P&`NYaRWoC!9g0H13$uRg>^yAOs>EA(8!cu@-n}FON7BN(>5w^vMva)k6>O?>N0@gast~b;&HmjF8Er~=(Hj)Q5 zRBIirX!pt3+5LU*q6--tBbuPyM-JiXp}sA~c@yPf$g~mBr7^Ilm<0dp$W^v+Qy?Jr zMia-@3w06G_;>`Sug5AzyGsIR+Md^bjQax2px=m%ySqOp^EI0tS58LZ+J%9nQs`Gzr;soo5jUTY&0$0$V= z-q9Zl3>lrUg7yVar>I4BuZ>UP4WHV(65~i^QC;Z4R&ey_(i-P_1W3u`b;kxqio&Oz z^6eRHR&@f(RBZh&r&%5_sxA2}Nzi_Evrrf7)C7|{Nx78m^Jtdqiq1P6f>MFUtLib& za}- z>i|F_c{|DzD|pZw8$O4EMoG6x6k~^S$116<&Hyuqu>vUoz^r6F0mh2$fvXK#>)r5s zrL3RVFnlQP+hVfL5+5%xn6DR@)U?GDFKQ?EfDtOdT-~u<@~q3EaP1rOCvg#~*!6q* zqK`Cpw;U87P2WOspAv5F!(Lut5E~V|l$(+hJ=>-a-ixaA(P0{5+wmC-U*1#`5p?+m5L9&$h^vZGZ7Kz^O zKlCS4$oAqXe!S~oLY@g?AAg4Km>MQ~^`q-c@@fAYS+*Wwxt zQq5jH{Hi*ttM;B!mtU{e=Qmjc{>j4AgG0o`JyqFl(wxv&|8&jX@m|S6_KT|PkQHpg zT&()@wI`&q8r3B-=F=)wUF{H?v7`uE0`gXKGcn}Adzq$_-Eo^4RDz(@+eauFh zbURikVd#Q^(3OSO>)H`JzxD7YFeEJ4yU(e}DL!VxZ>Mr;Fe7J?L$O$Ec8X){C-;v1 zF1NH5NBiTYx4+>T)b@m_jZV;2N)wxi2E8;-jMmEUv>1w;-^1!!--m#9iq`Wu8ixv02_7Uo86rB3vqlQUy*0kgL?x9^fFK6rxodmK52Pp zG!7=Cgf5p;B(q~{b;lYM#9K_NPV_yre)O_AoE`xmjF#B><*8R@elOhSXelu8)Mu0F zvy{6cpfM39bM}X$e1rxW(5gay$d+Z*TS?P)n^8NSU~05d7M`NPw&cE@N>6SUlGafD zYXch1fk{geSVeQvf+1_=gMRB-a~bV~-&9B*RR-9C*^{QY?U?We!UObCyco-u&Y8zw zO~rJ=l37L0e%{zc-5q)}s<8+@g-8jTV#c0$-9-D}!BNIm`wX?(VcA!U8A)ae)X%m8 z&Z4`wDi zn<5YBfCaOa9dQEyYV#<^O|RSv!OD$bHmzT2V4&%7>T51G!R4EKmhmD9y_k|XR*5Q` z&ZbNRk+D%g-SxH*J)_KM{eVn#B#39`QL&W_P;VLKTPND>?T4c;t;f?}Ll3vX z@wO^E#(gzopz-vBRki297m!X{QC-SUs!Ql*jqt~7@UqhP zJ(10*yH`%E`%7!P>^e_O)@YsKxKjA-vSB z1fFCI^srEWIa#5W1e(dUo>Yq!!XM$-PqmsYOna{t-n}@LN>RGJM`Q>W7HQKtaa(FQ1_fZH{w>@&=WVy`^D?vhV(Xb|Ou7S(fx6x&PzPE9pv zf)C{2?%phiVWj}uW~1exn^9(zok&G4M;2Vbi?4boUCPQU+oXAstv$Y6rF}g{4b9}* zze?NEE;NuXE0o|WOK!#KwAqr8_*7mjaYsM$`P!3yck)r|w?!D{h@Y6SQ6ldT`)MU> zz4HcdU~PPOx3YDxtfCWA`}3`DN6Iq(1`<9zcD8eDxI_>1B^VxP3YAfhz4gsA}iho6|3}qZKvmgv@54MIDj*;&(k0#9ARsB- z-AGF}DALWL8%b%T8$mh_($dl(-EgS?oX7Wj-|u%8iv{c4=RR}K?AiO;*R^NnR^HRe zU21KSIEtP;Hq}}VX~M=P%TA`N!00rski~CZKgJc!wO!cDg{=HAlm16nv)N7VtOk9V zpfaV+)(Qs;iGX+QLnihQ0uCJ=u^9(DY1c)^vBJB%(OW+){A%u=`Lt4z3E__SO}TaC zbq}XZy6DgE^KGP;2Me3m$Ac$JP;F4qrrkjcT!*%V8!MNG9~C^&%pzW^PdslZFI#pp zI%+ylw!xAAO%^4QdS2dD8u&pr!*A!ENSCgSxb^L#le%1#to*swjMB`pv&JP^ZvEz; zIUEN=&YR{9*Pgb_l1Wp2@fHK(X?%wgxq2SWzR=FJGrMY_p?riWqCwsYO+$oBd;|3GZ(c zSL|%U1pPCYvtCBjhTyq7#-IDM8IHzc+`;y%jvaqM*Mu%x4ek26<2e~BOgib7gDA>U z=O4VKr7$DcfBn9W5pvqw8EsW0w5++l@I6f~LP1F^eEG_`{d!1s`KlX9!lfsOi#}o; z0;4bBB_R|P@w0nf82u1aWNg;5(;UlRKE_kgnv?U~m`_(L54ZTJ#s9QP_1K{VHuW>b zlpnE}rw$!YYTYgu!u6ZS-_xm$C8Hk2FeM5?IUaYC;8*~ZUx6*Odq z=ssv^5IoJqrCgif0^(F{>V5^TX0dJjkBbKq*rs#)Gxz=MnH`R)=Eu9YEJE`|s8x20 zx!L}w>UV0GZOx6%^0p$#iuo2EqpZ}KY1L?!@o!2E&uruOqwd>&eXS4T=EC&#Fs>6` zAs#*XK?Uovf^F~!wA{KsiKdo z_7{gQbQOWBCvL}u^8xm$Dmy2Fe4F=p`oYt|YCDW$*dB_`+-mnxrYi3-V!qUobNMMo zA1Czbh$_7}4&o7#?!{hUzb))JZxcMMYxWz~?QXcZ@^%o@&&^T99(nBc`(^m>gVtljR!z9xfXOC;+!vRTc$BYHwS62C)O>!$R*OynfxxT9XS8JI z%5L3Yjm!hM_|sa^lySE=4_V5=Jpv+gO0gm#o6Xs+#gd)Ue%>Z!8v_1~yLo7{0X_TP zzGrfy7r0E1?$(@X9PG0zZHwvGCSe_4HPLRgW_|{~=ZJH?J?gS%O5U8*AcdWOEb+rs zc@M>lHhVvOZFl&quxSXFHlF3_m5s}0^1Jp6HB!S4bV{_N70{^D z+b1JSp}@XI@2m&GF`z|P=ng|RSx51}MUTf4aRO_xpN!of*%T6XQ)b&U>rDLuzU`vu z_PvAM$kZBUFMU9>!>xfprQmVHq2!aoox$gO^9bD^W@>!-g~vDKHuoNUh9;AqzQEjS zW%Tc3y9)^=99Td5OyECqH2}%iA!Lx2br3JlP?w^Lb5WdNp$mQR_V7_hq?SO6jTGst zE~dNLyqyi}IV}zD&ER=5+P7=Dse4=R&bV}&HrkUssmWBVh&lF?Zzfxmlnvvp$*TnC zIg~9;8ny7uFKk6iN9^C2as7#FY>zDsZnj)W?X!d`(?%;5)+eeo*sANwwLhAE-Z5O9 z&b6MrXq$1;@s6m-^PGr5D?Ih5JY7F-_>k|%>H<}My>>-GS6{B5&$$?5$5+VGe$TR5 z+L(5I!-eOS9t;h0qr$mDad*+3a)5;`z_r1bQBH0Jse!Gf+Ers}+t!$$;Eb=P z>zu9xP0cW83%Ee(bo`N!P9J<@L-m+IW}>R#)H?W%tLS9dG10nWa71#X2_I$t1c)2DDXZXv8KJH2Z=l@07ECw|_MGaL#A%F+{EBt6=4{@N)}7M~RdH8Vq_De%+8HJm z-Ut$ee^Pc#w@6UzlxPL?C+QG|pWh!h#Sc=!7P;Q>FIj9jof&bFtp(J)!BqK(SZ?U;gnf{gUkM4N5_5JKx z{qMFb{SnDAH+~^}l-%IFu(KR*vPGfaf1JzFio*ucpIH!1AKF}2qS)-%#V^Hbu4doex6ul?dNJT-z=%BpXA8a!T*IK((e8o;RIpB?9OmlbM|S&!)5H09=+&$=(>8& zZD;vp4&5O30zNBxB+J3QrtkUasfV*W_4NZPh0G^u^4?0s8Fl8Fd3BtDVrEdPh@eD3 zwC^wFu)H|N8$|sN!bh1k&0w)A#8o0%5|+8=@%PN8ODuM-N{{`sEj4!#QjGV!eO*9^ z27&fbB4MRdVmS!(R#9X$zy-Pc)vf>^=35!#2O{cp&5BuI&YS-=h&i9CS6bEtM`>|E z-AnfCngOSGB5|x|9DM<1J0z0zN@ptl_1{f%P972TBOq|Z&TCBLXBOfNOXQ<-o6>(U z$l_!7i|07CtZVSoux^676isX)usYA)FN;r*T@&?ZgNeQGUHQ%x>|O*m;t})eKn~nO zq-nJi>mpzE%aC&4FbK|H%UpXbv}N`o&d?mu7aiUM;3XRNtfzwftvnONe0|8JOL2?2 z;NeXGB{+Kq{!09u$_jPOq8z1G@UnLf2qa4)?+j&wNKlAn#G4(I28xf9lP{o^|Dlr^ z#i%!aJ6b9Izuk9e|Ix8Bjm#%|E0K}^A(0PvyI160Yywql1~wKHzJn5YsnO~b#@~xu zXY+|AkW;AmIjdmdQ#F=O#hrCGJ(mdy^fHrvf7xPm6(#!w_IKYM@N{9+RrX2JFC1AC z&?r|eXyp%Q>XQATcLPkJzXktpgd-}s=G$iZR)g;{1cB0R=`?&zBI6~G;68g6PO*XKxOa)rUK>I2hk=-&4zsmo(5Y>QI_c=T&4A^=|C$_yf$| zFf2bc`5p7D)66aQ^{-mVVolIH%rDFBg8tOzNcaEg2m}P#Ph`bilhP&uRHshR;%=-? z=K=!+hFg7CgP^;?C;v9l$bvE~1#?xU=iAmWYJ6uH-@S_9K-f#iKJW6gI-V6|r#Fpa zEa$2!6#v^^DHcrzA{eGX%JEq@&GMD8^&`6F|Fj!76!J5LY(NGDBGD~v5lV%8@T8RL zZDhQWK|B8m$5gSI>$r0YWnV;t*u>i9qkp$r;z&FAboTiMhH;U#`@Z;kRcdK!E;j8C z)<3LVwsXrJhv&)^&2HBVz;qQ|9T5<4!_R!zQiR{MOKgeIEdEUXBw0CrlpAsTO@CJ8 zKRcVG$ZQ)vHMr*^4@2dl;ZkL|4kSc~Pn|7h!sj<$&*p%P?(iVnsiOc-ihv+*Sx@w` z^{&{UZGKvB(X~&~tHGPh_q?8``RTZXOO?CrOz97AZpL7Vr-jseOPa&_*T0>d-Pe}l zF$Dc(?GA<6Pd0yE-&E?bdIljP$jiA15^{KFs8wfht~cjMY}t6Qf5`m(pDnv`7?1s) zNFO$QykRnE97UB`7~&hDVxvepVplIkrS**tl@Lv>ae}J1pAumn|MexT<~|QR4h)V8 zV?wd__YO;0T_*^v>b8E4WES`c2)$9VoL_v`&MNH*u(k>ZSnU3q+{cH-C7fSGeR7P+ zM^pIl~LJg~J9uVg!Do|SV|lit8Qc<`O(+xbxy=;jp{nm>tY2O3c`-F)J!2rqlPy;#a+~HE zBt2;^pkrO3YmjWISj9@rixcD1%$Ln-Grixw0`ZJlKFhjF1{UByn$>V-Zx@c?e>#dn zXWL`6*rKa3>Co2hi87?B@w|i5i6oV8;c7x;pBJ1|Q|+a_UvVB$R3c~&1xY3gDXGBR z12k+!e)+>&QcaGP@m}pi2l>SX;SQArHmPw6ct4_aUZy)=rd%gbnRKQk#j!N-{KuLT z!!Ev_;_b63ym=Ad-I!dMhJ>x=jqYY&&u6>`*%IL)rmzFM1O$Wra`3Hgn7p{`YYy&u zn(b%a$g3Xwy7U*GY^{?2;q2)&KxaOMzV{xG1Aqan9iu}vSW1W7EQ)tqHopJvXgzb| zmMnMRo#4sf`z3Gz1_S^;fA^i1*Hlxn?R(-KBwB4{7g_32K^GzE^IN*G1v!VTA?@RBT0dG-dkTK1mvqVV-FUwhmx3zD$lxp@?tlh1 zgOTx*f`dhzr0&&e8gF+Z{>*os;~eCe=UD@4;Y96T+IB~$rEVywhzKco{U0TYjHdne zN=+~S(<|nYF5-F+%;PLAva~j{?kTI``0dDKITUG^{5pP=FlXH7tGWt`ZbbZx1ne~m zj3PISJ4c;;7?MQ8*)p_lf-FP=-Z!dk`xqVdr$?!)6-R3oqNv@3Fgdm5gHJAf7~b~& zB|Jqf>8nPEzxEZ+-EOP;B2Rey+(-$uYwkwg=vLQ}n_s69?TwVz3eDK@m|lwVTe>o$ zRr-$Y-3~r0#4XjaZg)O!N&f?pm{d!?g-#tZd&P+r)L8j<>^v@fDH9Rpt*GE<45t0w zmGS7fKR@jIX%VYxT~S_4yP%e^WEw3AZ1PR{K)Hd&&Cen{1v^g!goyy9 z{K1Sb4ExwG{APks%2>TX!7Kviqq{pN0EC5+zLioKQ&7e}xj|q#3??KDTml%`vq#;>Vfuqg!nNG_ZsmrI zqNR6xA=J#VB|5cL9h^Gbt zqmZ^lGs8T(h>oqh40sbXMWUFqKkx0N>sZnNLYCl!;7Q#qBkvmc{zwJyb>zH`i+T9@ z$GlDhDFn)QV}KEOsdjjt3B3;>f>hX+e`7b=7xZ~usA{enQGS$&m*h1A!pJuRRfHGH zm!fm8TGQ~OGxzrhdJxESy&q1?j~;tpL-^zfE%jl-{>Gi4UsRp<&Q8A}B78LYhN!oB zT;tVzcl4hSx%uUXj@Q!#tz>>WuAvkDyMUj)F2@Z%3mDFSCgJo?-z+mXgwu{X?XO;eLkeZvY{aVC8bLf6 z(oVpkv~nDRu3_Kf0gf)szyx$&9hYXhL$j5?|CgDJK$E_zk^+m}^O2_y_qkQlZp$o+ z8w7)8HI|c{mdhS8dYerulb7)4QnpdO4eRhdT;pk@8LwIbESZ<@DYpNO!G>DV4vF+K z%*P(UzJ?CtX6{CYe^@Fww))UqLW^!f}>)8 z%~DKOd-#z&f8KcT*;ydq$)Kg5VHLGIR(NLo-?&&u)f`vr@xjo5n$6U2y6_5LSpavq1dcn>j{aW8<`7ZDxaT&Sk5YJvEbnFcStPg zd)@CFN&o508!LKOx{MAVk+1KLbv36&&UXLtUC%ZoYP7#z2xF&o!^nTfmtV}>bg|lm zKS?$0?gJhWI99Pktv@DPqgiMX^X->uRw0Xy+h^0e>1&2lLa|GBSII85!(lAffisr^ znf0xg=ZboGSNXYz~X&_Wv@tP8Mi;B+?>xgQ>|eF~s}+TkrjKl56V7&+mID z>xQ;0B0e^QK)A1X%^V3rpT4`Egaqi?<(<9dWoI`kCTrE(ilJAs2}bOMe`6TGA*m35 z?6g8HxEfetkm*_Ssro-XHf<*eo<$Ss$N zDNai;vr0O@ugVdsTHw1pZ|6FaF+REfZm;@r@Js0A z7#x(?8cfvgP8r+P>=Ht6f3PSB`%QSiUS9=ded-gJ4Zenwg3S|ZX9M2NUAN_jk@=t} z=k!+JPK)ZuU4KxY%8z?nfaA}|5M(68)NFoB>wr#q=>Tg;Gyf63z~9+DJtKQFGp(l| zO8~ns#k-|_S?_Wg7xvszlc$tb{Guc&t*|*?JIj`C`{E@`OjZ8`T$GZgq>0zplDzah zuzN35MHS%dhCM<13u{Rb3mte*yS~+k@Uwa>tD{l~yaoObYNbgS5f{nRqx75x8FqoRz$TwDJ?N zWP(}baMd6Z-1bIY&R&Q$C1z{4+a&4|pfBvPVBGUU;f9tyalNH;+vRD*&vBm*z*mdq zE=s+uLJ$8n#fRE8 zCDCPKnFtV?e8Du;uSD-tbeyg&+$MzX3A0z<^!JY%^3{NltddCh*Aalb#WavVcRW(P)<{;h3IX`#$KOANY9 z!_I0T2(x2Zxnba$?)|ZISjU!i0hbyxT$nkH zT<{#so@)5~1F)c-2HdnftBfz(NM`cNECt>ydFKKH}YRNKneTLU&yTAZFRoKTqu7?mqXD=3&&p}c3O05`8Dc_Up%T^ z$9f}kU#5i<%12Oa3G#-KHBCJ&@h=OmadoVQ>glT*#|bbK?I>MB6)@?+kA_mq(=3rh zRavVNxssV+M9|Oq4=eahBiqaO>KV8#lX^9GXcIBvtyiw#>S<|Zg%GbxRe!u~wWQ>t zppCt&Ai29QnCOWz5^?YNA$ab$ePPFDio;>!qfSfFH-4V<<5P#x54>@wbtQOf^ycJR zbdn=IcuBIaDytN5fv+uXXv54d7&ienEGg|`k*y|pbX!d54=Z|eql<|I4JJV=c>5MSDC!tFmB417N9?I)NSAV zu^9-FBa9djtSu=h(K7-rx$+L(c(oT zRk6kj&n*+YRRMgu=qS@%-Ocx8*XH5OxU?Vo)X5?;U!=PiWNPUu_^Uqug(nqubbro?lehEdHaxQi?55b&*XXH0iq3N$Uisk5kZCC#w$lG+z$ zS9xzg_GXN3q^!)8AJsHP*NvnBry2m4WuNz1_EVq*dwlHGYd@_RzHKL#b0KXo>P@LQ z11Hbu4)>LZey&%+0E+suXA$0yTs&4^+yNNd+U2W63=l=3g)1QaA%qrKMs>&4(jKVt z`cI+(l-s@|eyU~+4^j)RbFGdzxJ*p|=4BM%mL^ZxYbjUii0Vr%R?85g{%-iz0bccx{eZvv=B$4iI~>wV}O1OUsMAa3{dFiyJg317a9F+YgHd`3*AbGX`ai2=w2a0Jl)f` zmOtm=&=^tyCOeBy`^J5#kLv|^w?cqfr>(5sC8*(90S?+iCBR^HoJAmUe_d>L{S7=n zZiw4?^<18zwsrj<;G66L69Vo&jYi*Pczv9{N((T(iS=+mLYh`s+rA?2$SjPlyQgw`x7=!X5R90!VRn0-8Ov*BtkCVn z1Y(~Ppa?eCK5Z##Q){=Cxq)wy#3!>v;aBzMP_*Znto(q|oOgGa(xO@aBx7EFD`gJH z-v#VCIlk~P@bRs=ECzpM)MGGNq z#Zlt7QS|{s97m$rGA%{Qf}~NQwCkr#YgCzPQ$xhsV+44z3MlthS)0efjELhW&wLOr zB(mVPIPf|LL`@Gc- zzF791#5>o45aBvjpW7vdS4{dNdiusp*5tW>2a|$jhBy%Xcb0v3c?4VF+|tbk#bSv> zd&DiJTIEA3WE3@J+U@v~Gz@9qO_J04(D=3h_6tXSts!kwSb@^#5wJ=22*}YA`tqd34|IDt>itU4J%+on+RgvlSqKwPKpL0=Z%OTLzHkVfF>ZE*r@Q`|0Gm%p`Lc;^@`q{Y1a)-w?U_4RQexTV zz~`ovgcYo7FX>OaGRg~h_=p^mNS9t^1HN_s_(#@7h+zw$oKFL3(t%k4A-+(pw#dfPl?Uam}O)cK<*BIXN{FNq!T66bg)D9 zn5E!nTj=vq8<{a2r^gv@7ZrZ+s zR-d6T$@VC4cGzprlQG~-5D=O{#C8C%2_!RiD-AJP0p>CyQp~79uu0A-b#w2*Gz$Tp z&2EMd#0BzS{&y|_Ymh|c{ga6>V5kqEX2)`y0MKh4yiK5t2T9)yAjHBF$7{&@&vPKh zeYjoyXcg>NW<$$vKedkIo!f6AbQ`wv^ru=fQH+eQ;JICi+(l1sE@SlS$*-vVo|((6 zvh2*4MT^>FOL}&1-*_MY()QJ!=(mx1Evx)9Ew%VqOXX*{MO3_N< zh7LZggE9L`V#8$t2n5-K;-H;-If-52mqHc~o8qS=9!sN-v|Q}Rkc%$mbG&OUlNGp< z+Z`_QSZ-jeW5?Ipzz1K0nc4A>-asni!Y_yngaJ7GmFF>$L$95Fx-J7EKZe1pJwSkE zZ?O1pD<)SIZHNq{<2DqwtiA%oYWRwtXMqwD!bhv85p!mkG$o(oMzy3b4cD>1jP!GW z3qDpqfh|P-9;=I(1Nl21vH`D~8^~({y7Qq%`Ge|Th$PbhpD}^gu{*W0dd8O)YJwkS zE*Ta1a(b2n7C?Tn;4$DKlo4KW*p(yfUd6-Tqp5(xt`1Lu3$%7YZa&R*M)@PL%7M3L zC2UPC^qiY;Kf)p%mf>kQ+>4>>F^@ko-D{H=pg>T}|OiADpHnesDC& zm6&)LQVuy8)a_}>NrmK}e4Z>n205LDuIoi)8vEy+E_Dfe)+ho+2NVLqt*=o$A&;0L zs5OCaGvv#va)EoZMZ5qsTA?sVNPszhxJ9pC>r4A;>Qi4Grz$Wi1Ilbbx!H(K8ux+F zbzY7X7bZYHPP_4K{DcM6@0k4E|Iw*j?ajWo>icpc`%emng@A6)n!XRy8x@t*-Q1pCQo^+L#Sf-(2WG{w;cYx| z04+h1ys#kr0FtQpU(BhP4(>)9gj-peDNKa5x;|b6);I}h(^ZGQ@5!~E2*1;7m`5HF zAe?UNH7$FX+RZDe{tu&jQ~=P1m8rF{zDn`4LTp7(c9O6mECt5dC705=H;ie_dO8dA z0jm_dN$5ipr9{8*_C5iE^>z{4|9&vl2^@n~S@DkS(y;C~yg4wlO0HT`U(chO{nN=? z4uz3>;6GA!aXA5+YSwNo_jFriYzGGCw0nzJaCGq(;>QQ5G^KR(^-qoKJ;tOop`vD! zk$hdJ%t%?(q{A>h`26zvPI z@(cm^ik^Uc*5f7^MO5sGa;O~tmbZJ+>rb7~xY z9Zg1RFCDwuZ{Vj2$BT(Xnb6oRb@ISHPcQpD{Y}F|0bP;A3n&lYiI7cg{@oh2Txw^W_wMymJz{Jtt%e~$?d~_c5{H7id!V}29=QgloZQdzM_*Mc2eYg`&vjqKd zp4NvZnXm>|JuCz2Wi96O%$SO4^JiK`Ue(_NNiHSjO*-B~#Zg<$h)KIcNyEsRoj#Q| z$)ilQM{ss&H6!|wPMFW{0MVyfTFH#f%o=Y+%b&XfS~8~BM)<|ghb|Py4Q>mw$OXD? zSvnnBK;ytYtC`grW6cAOa$X_U<5z7DB}gFq;6^Q6lquKyA|B)snwFgpI_l_XgQ2|y^r;|wpzu+fe z>vQw>w^z@Dp~Ub)n-HVp95`op?S;2jTD^EYzvEH?~$Tz!QIxL*INY5 z0$&du)N+$d%1t|Pqy*gB5rOJd>AT~qJ?k)9Nh+fncCIDw9$FmP%Vb{57u#67PO9jUTU_CA3iKEi9M@FKy){X+ zwz8tC=#TBkp|9F`N)&T0)(Ws1EUuE- zGSQ`Oyu47DU!iY%BIVK0=!NfISDKG)Biw1d;!Nh5qK>$T1Vwg*3?zJo?l-|40IZY7 z%uj$sq~q2$`=Vj{4{^=H?~0+-R*zW-AI9BWM>>!^=FJ!CqVeWEqeJJ?EYscVL!}*) z45jNY8cLM17V4|-FN(a*?HwIex!}CDL!a|v9>7FATva}j)$pK z`)>&Mv#J0|w@9qLF;8<}6bRGfE!dtB_UpXa+CU=lJ+I^&rw!Z7zI;D{0hzyP1@h788i>zpE>GBf zPhi0u{;WhzY5~ptn&oXC6|=#B&@QwfvPy2iLc{wLoI(TWA=v>;bJr4o&z=2Tmb#LH z-qx5BB>5rl{Gmdw_&uP3WE`ZZ_LL;OVn|q}d`PUb0a)O`&sER()7m%4J9;7Etuhj~ zCsB&;UQG;~hNWgGO8kUpyE^AUD(%UD=lWCrjL9P&{C>aU6)z(QerMZZ3`YSKYKw#B zghf=L{ULo-YCGR}qz$wMgn^pOTZ|4~Z5Yls(Hm(pgSpFKit&y2Z1wKH*hSS{7z0-xx_j0?KlW& zpbaWtOV%vUt3~xeL*t@m|4NJ0?|zyOm>z$Y1_7}8OlOy0gLV$2;OjpdLFwW3nLh)? z_Sn74x^KfOMxVyoZ z<>=J9<#rr&F$jF7G0X|VE$CL@WmY-WVm?Eu{Xm$ye9GZr{$r5U1*o*1*2=P5tab@& zitY*i-zYsd%H8F~w|??%O{wm*ubk?)na1_5R{GusNO%4QzH`FmK1hy+@ias7%ekGM zw4H84zf&-%Mv*ck@j2V*1XV1VSXNX_pb#RW)d>JEfT?@6Pe2-2&agYu=={{X4}eT< zm*HUsT^j)XHLr#tx{!}OU?l>DXWT9ERfn zZ0lWa$DlvI04%xo%H=-{F6rZlF7o|A;Rcxz-^R)uEO>NFt!imGMF+%^2X$1xia1Hd%mDw59nn&scE%-F+z3Qnbf=>Z5NwIV<`dW0RoFen7nS!`zB>j)MOCQDSAx1a@~QUs7dM2e z{LI@74sFh08OWWGWjUc{`l{h=C@BuC#U^dq)_>y96Vwjhb!UcM078_}3cR{1G($iZ zCv>u7fzl(T&b}_5t@G6Z226>kgI7sK+dUVcw#5$vf*cxu{wf1ml#~{lvlk~he^&VV z{tCXd7`5EJZ=RYRak89I2T|z0HB@>_G*pcN3{edaKucP8#2+xS?(VDHwC#!$V~2N%DnX=3tw5J$7UeqXDC z{3^&*W>>{rPOIUygHal!io&Nbh+u>3ArTvA&~`>Gd03=i8A#?;5@ZY@)|$Jq5j|+8q-=>HQeX0bmw|!BK0_i;_S!T#0Rd*Ku%mNT3$-ngbE}>F9`SGC zsF9a3ibEV0u^JB;B@U$ShKw(80*z@FZ8iME>+}Gu@}N^#Hx0vJh)=kX0<$6e*6jJ= z&}%4|1HGKSX~!^GF@VrO-GJpxN9D7YQ&3Pbw2VYUdVuOR1Q-Pmc4W!m-!-}WLC7?B zDrH(~#KK=^>->)Y7*X)|NbAtH_(yW|0mSj6a-R@N#JRu;sH;19WGB3&P!N_D>!IkG zCEFy@qtduB&l>^wHN3e( z3NqL)C2b)Sd_Hn-n7)efaNgx}a`BE2q+THpUdHQXCb`xLr=0&fRwavHY|MyHDTsb| z#SqPKM8l^*B3c;6DTjygICVfqA8PEp!_BSO+NZMB9z_}QJdTCE)=BqPa zIHD);fqy5S0#j@P^zT87btkAvdn;_i7q0xNlL;4SebDD)(?YS_@wm8BKHpH=)D{_+ zrUh>h@$;QfxZ#>TcV6iR4({u~++A<>DuKgkZdR$XQg=*0ol3NH@;+zK=QrkSvh z`@0F;7$S}spQ4%$-u-nT27IV(9&)-K#T*NloYJtZ^>KMkG1pqS6G7@Z16HkNR>qJJ zOYcsrDl@O?IslHGo-`e5|4S{|)}f*u)XX|1MVn|%sZ4;fRsgg;SN)-&G`-J7dtwbt z@j5;!cN_7NJo9MW0+8P~nJ0n>2r}#~)Z}SUsd03lrOeU&HQcGm(O{na|EmS4b({t* zxqiM*X9?(--K3ej-vwed)L;!?GB*?kikGefhkbWOoV^5Ir<$Z#`jKi;DJy-D>j8Q7 z<=sgF-H8y$=4)mFD+`J|Y60Bl=pda&Ec)*%z@PV(!Q+a}!L`dJuRar4UhFszkWow20`VR{CR6C&I?u>ijMG#C`Dp_|_x&Dc%D#j@ z&o%3(wMObdISi?R)*a9r+Bz&J_xOmJxD0RmTV#3Slb_Wkz`4pzqEh>55aeGQU;j_G z`aao}-(7-@aI#uqc_ZKCxV$eXo92U#z-kIus-J2b;K^GK8l?|GnW9wDaSJG1#Xh)2 z5xxEiU&!Ne-CvZ>;F5@lfQ`OI)UgIkN$pvj%x1Mn{MsoRsXzF?5aTEtk<5^UXQ2wz zga+|GMWDs3Y(0-|YGHk;F_yr9f~JHlYVKGo@_Von(eK~T_EDBh)_c;NXHDFog^1e`LA2OI@MXf3eBG~E4(nbui2!ESs9BI85XjdRGlwOz zflmP+Au1g(R6MZJ`cjP_{5ydOj;&n}N^K|xmt5%}e8h4DtBW?R5h$7g)>yi)6oCzF z@#{Dh89mN<1GKS+?e96B`Cw@S{9fNVKbg;J^fS)G|91JT==u-BR|KFCy4KTBz=*o} z5JXpiSPd_?>CHCXXrjsmlTw%PHpP#H^zyT=fg|}kijpU&Gw}du7ktbVy0b^n<~2{4 zq;wY~cf9iNkSJ}k8akN5zTB_fe@?}~^LjZ7V^LNlNJeXgbJ9w2-~G`)E`VGn4ud)S zj|u_;eLD7!R3MoN)p%AW3G-(Jl><-aQ(20sh7PHv2M7+2CKdCfa0TCIW16Bd;ICw~fQL!mj#Ixs&!!N>p_HR|b5&L_aEV$r&=_vvCE;9racWr4*ix>P$`q9dDL7lUB`?QoYINS$B4*s}x$|0HYIq1r2MxnlUf=I`j zNkCo_7_(x+%VtG;zUO!M!E<0%0#F1&y88+y?hkwaARu)!VG&#o{LtT3#2{<`%>J}c z2+k5Xo2`Qj;6+t)J##pQb16D=yf%OfHPd=b_5{l3*9RJ(LXmRb(ek6Q;R_OU-B2I=X+|YIhoUWROl)n82Yx6&gxKiPg!QgpMqx7yCgP1(Naj66z?;jy3Gdgq^QC zw)2pZwEc`8RF8giLj3O{8u)F=6kC;&y4%j2vPy{*8!>Pv*uQ{%TfRMZe9I^kiLE(E zM7fF7OGSYt5t&bgMHQC6R^rk{zSBsb*&i$Y0=t`oDnX-&!hoMDwRAu3xgMMhr8WJ( z4W}4Wrlor^%}z8puD%KB<~z>6Jc>TeKNRU}^KtENy-G5Ol-(&vi${<9z#|bHNVXEo ztJp;j7Pe6FDhG#3vqR94)g{Eb+q9C0(Ol?US61G@Cs5?y2?T!x_^T>(fDa$an(QLU zYnW7;D>070l^|S{X9(Hy=nHi*lHIv(Lmu^yCURn@B07vCbIg(mCOYu`jVx8{l+}@e zk|?8W%!Ij(^^rYLk~qU^ZPGfBz`(aB^8MrgdoXysmSePyBQvJbZ!Bb4HOx|{2K_K* zf-j?V>M{N2xs((TK>n^=kIH~mh6Es0-Git;ip8Ct!#e==ev`qU8QooW_F zp2@Kw;ELtHj6cn=;s}l?6&|U=>vx;0wR(*If4=ggR&Ea;iPEMAE_F#Onya8pYd98i zZvL)9(dm?<0##U}rGt6Jj|?(M*E_LfKr6<6=r5*-BX`Ww3ef6We33zPLSY6sgQoFa zu>4F=_rQf?2ZW`RC;!)0%|2gaa9n2`awof(H%Z`9tF#LjaqA^A6AZuU3w5M?Ao9F- zhU6cSfae0^5z;8GkQRuqph^{*Nh8&k6hG`CsZOxhkG<|vCP$TvXoP~y_hw^9RC3eE zJWh%s_gF*SY9YLNyagiT~dz?6ho*{-}>VdpE4!k~4{N+6^<4pIC+L=(tN} z2^lwYzY#+;Mj`yPBSNkn-DFJfswFq0$h;BeTtgd|Z-z^)TEW|=n)O*966%dbeA>-- z0F707feaZw&xi+8VS#)H*b;IGJF?SwH&!=R;z zKaA@U$Ni?DU9%fF2K%qs1K+$8s`Hvu7>AW_J6Y`l;`xpQGm3&@QkLv)D_DgN}w6|DvP8;Y26z?{sCXL-DO;$fXJdh zBapB&e#7OxAs(do);g_;PpFt(>5%=QI-wlHL%@xvfBfk%qWlxZVhs z36w{|6(D#b0@J=I@`)Vvz0nmj_n{7*Ea+F=Olh!$#Rk#vizl#SJRO%&X>xmR!+h@M zl32&Mh2pvr$0Dw7N+n6QT|j4$M11V~vETh;^dTaQj^K-`hM$J=|9au|r(66;%|95n z_&=uc$cJ{M?ePW|dSA*62u|pJr_v<2;FXMI#^vrzd5M`}i~VjkExS5y&FvGB^FEvO=WB#D2J#*ohx1^=bD8> zrV`3d0-JG;=~)Ca$G3PL70eocDpwKcMKOt~yhxS^2oz@r3qpSktR$Fq^O-j)zj0W4 z0m15W?@+bc&fogHy6Xw+7$8uUR74v0Yd0YZmbbW7dr>^BEiBHqjFL}90eY#a)FrN2 zjWvadL(mX$IU+Khq3zT6*8T<VAjN*PQ>ev-tkC49ewAl>hxN`|p62|BzT4ybIPg(8c z@;mEPk7C6)uK)(4cBAk8R?RYmH0V;&9732ziIVRE?A zhob*^Sv+~%7rJ-42dh0PVtElWZ)?K zbc>UzQ?>z#%F~fpoy=%+H#zZ>YafB*0Wn(^SDXQ_>YZbb^~l*=ge>klDlXeBvlDAl{oZNhkxn?pU30dh<|X${a?GS($aM0eSCvR zo~`_w`1xc@ejXVWAuB*J0lFoziR$*hWfOg%XpX7u^`*3NM+y@xTUoo2^;2p0wh$^F z`EekyPNx1z!CxL{C?b21-w>f8#-e}7AbTC^IFmBeh>s7QxR9L>bBca6D1@8BL9XrI z{-)@WO=_1*Hi}lxT>W8!ALLp9WrqQKSTn!hHUG42d+?ay6W0xauYqOeSrLx&dqQ7v zhJ$hEtoVRN^!=|EPn<>>`wo$U>t)wvA%9+TFv&Wa&cE2#Ywba~H|u|7@4UHfw24nc zU%7eI@=4Q%f->Dox{~W@fOoP`@_XM)nHP=}EB@y6h9X_9{|gm45@r+hxr&a(RK#6m zOV5iq`3{~qo4VB%SUSbAzvvzi>K|MrDf(usg7Zc0`Xbqgl2tLiHd#XVm3oj%q_pSw z^Ie?TbN$a{G#b)7=_|GqKA!)Nt*;Db4-?LMPWy2 z;;g7q;Oon{Ld#lBST`%bOnd3CFL=+z6Rba&vJU`J4gO#2j7s%2#pY==2UKwkPCI)x zcD-U|ymD+V{uHUc&~k}R-C4s|Idc~u$1}td(J9lMj(`~oSPr@JbTTeVx8y4s(~17F zzmiST!~!9$Ee0h&V@?vGj;I@NJvI^JtnQ6{6~W=a@d^+N_B9wuYc&?VTgnCzm`!?q zyzb+Z=7MQ|#HOOa9IRtEv$4i6h>WeQ#XZ;Ff{1mv;Foh&I_7B|dvaQA(@RV=q?eUc zt>nIP=)0sA$9Rcg{Z@y;^`0{w(#!eXK6<(UUC>$48?&a7luu@xJ0!Wb)=}1iQduXi z{|~=Q^Ld0hQQ}cI3zUPV$~Jv!Ck%ea#!%`}rF^HT$^V0bs0kN-4tKB*HHwJPxM#D| z0i#gLQh~Xj$zfIn^VIq5?EU-ECYcL9^8K(@CiRFsD-lFH;C&NW_H|a_4yibfPdchT zPhlQ(sw&U)IJk3gkO7$HmDBUzjO31gktpyq#ZWHZ5Zt%fcYDlka;V{c|3ZK^<8=~I zr9QxvcA4(7#OnavDPKyP#MynSZaxuq$ZwWuN=4zo8FCIQn@uj0jCEhECd*`*xI$>y z$~O-D8|;+CTY-xFl$gUGcJe>#ee2GBnKPGN+*5!tM}x-O_2^1kzf~ut_rO;Eqb~A> zfAtLVCWDyricqg&G9E{Z#juKXE*B6fq+6W5CmyA|3DHAbitMSNhdQ9iaohJ^NaHq% z;v8|B%~zGjzm5fDIXW5MO{<#xj}xt2Ku$Bt;t-71vDRGN@}}&-q51Y8k~m8PPmU<- zX7LNs@{Y-V-Ue>>CTVo`FzgvUzGfcbpt{+{0T1UB8>VOh(5d>rHH&}$Hys6Fj+k=w z?!x6x0Y+2)Uk^EGQk#5b`ZQ0vrsYw=yHn zPDX_{-}VE;8&<%x$B&QjU~r;rjBE07MC#l}4&1IJvxUzTSE-rKt0FluM8QTxCY#o{ zGKxj8%%zm9?h|SfX0a#vvH%>=D(kffca{wH;APq9@V_aZ=BJiP)UrK$G_P(|@AW4& z{*1P^Kb4QphA2(6?~m2J3Yo%+==Xj2=C09hTu`(&rhS|#gHm(f#)bYdcXL@b|J3CN z)Kjhx%|VY!At*MizMT!qNv#u8bR`CvDpe?hEQ#_Q-4kfQXTanCp$tvaJ5bEpCHfUD zv1s`@1nkAmS<_Gfx&KH5^lH$=S621^dGXODJ^N>(P=at4v#+TV$tyX8qc@WxIN0*_ zTp^M=Hzhu=#*Sq(P1Ytyzte?j#109gq|U-tmBkcuaR_+gI6h0gXbbtuTVb|W9Y@LL za)|3X77~9t`bk5Qmogff!X*j|p|0u6?7Fs40qPS&Fqtf!#{6rJQ-!93KRclj?BbBZ z(DAoW!{PZ^m+XFs#FTf@Z5X1t&1vLqvNuja6=)Zo!xY7+0 zR-k_Z;3UzM0J!l%?)Afm*(6 zF2_i;E7c;~JG4={v8t@6e~a6h!ijW$nTTEiB-}(0?H6Q~e$-PvGnAs~;W(51$Km$B zGzJUojh`$iauminr*aUqwEJ;o%pb(z$=S0912;(sErgf{zLQ;kl@LE~M zf{T#q2<&y#s_D%mAzX-pX9F|e!~-ZYnMANQQM8Ba(~m}5SZJadmg-_KWn{}~lgRUn z!O570vvTL3^eOC;@gJ)yT2w81SZ=C^LM+hZS;o*YGKOo`>ywoa*07!Lc*Fq*NTgQ; zI9Yo{4Yw>OQ1SUJiSAyJ)LR~gAIgYZNj8w3(u|!l`9v)={7IxEJ|u|OidorMhelj7 zf_Fgwg5bWeExy993N*dFE_qYqy_ie?~`b^ZwYNq%wt*KEA?d?Q5Ku^ z&MC-Zh9Hn-CC0K1NW<@pUz5K4Wu#DGhMlQ)ILO&Xm=UtA!=h12lkqC5$vosOX5L4y zin(EzP^P9u|CpJ*4S@5j3dm)|l1WAaf>W~PrjO0JrJ3f1R}M=F2!>MAFc>ZKm| zghZ?}9_G2Q5oH+L?&ONfR;T1;l}^Dn7<$Wi2hZ}xR57Y3xh0Yq{K=@_W zeC?I$b7`TR7X;IiM_R>i4{`?M)$(QQ5Sq}gu}`MJCoidN|f9Kv>v z=xzvY7q}k>4D%?X{NYZNJe-LQrrZD}#71T2#s=u~lIiF^PdY2*$WTVp&?epH9xfWW z{3t*9D~8UuXCB2j%q4>z@C>xOfzyafkdy9A?s>LDTe&n@jKUDNF3=9Om%nAqC@^B(k2e&yzjN)QA}GeM z4OST^YB73G8**m$p5?7+@VbTe9hHn`Ic`qya7!3t5vISYW znLRbMO4-Us@&)QVl;SuS&+UA$%2gvD@CtFB#>}Nkj%5g~jlYxxgH>6{$|y?RGIyoP z!kjWo?9j*MxA_+2UkM*`S;<|}_~w`Wz76Oz#MaGtkMQzHrIK#9Yhm)wn>|)!&Eb1; zCpt+qB%w8=t}v0x&>M_PH;E12n!uw2T;oPGn9ndD`uS8vwYbZj+W|CG>+RV5Z zu;K4~;0e#Fj1@o>UWunXZYLI5E!;?~nBGH>8+V=7N<)4}JJ%o^fP&Rru!<-6t64W% zjWj~%Y6-E(H=dK2LfR&MenGS?k7*=VVIpH*TH%>8$ZX?Fsj4 zm|LDtd3H*ry=XOHcG<2?QiH#q=0 zP=UC*c;2ttc6?i<%y7a4hK4NPcK_?1F>UArG_p@pl7NQ~VDcM7uuXAD>rWp#wmpa6 zX4>Tce80bsjfl_9>pulN0|T3EOOC+!jC+G`g(14V*ucmVq1SOIV0tFdYHbg68&!Yd zxccX=I|W8K*do7Vhu3sEcJ)#d*5gncTc z`sg_IWxjb-*JxSYCbl)B|;NZ%D4{{7ABb! zI~?Xj2Q3DYEGbqI5ORb_WiT>QBsa`8PzK4q0p54vP87-_h1XF01eUTpBTkop=2X3? zKbI*ctsush9%_rIYo?)X=afcIig;*_5TAc(2M6OOhKyc`WqF@NI#6>rz>G7*WCWi> zbw=heW3Nk(l46WyQboQcO4*tKd&ef7em=bW+UfU0&jx0DN}|&2wO8L*l(2d zE;r4-I996(%8b!BD0E9Fl=l4SZV<1=$i0aOyLhd@TIhtU)%o^8-S0JzmG}8`hHV>~ zIssGR^u3^UIB6E;VK>7RJC3TT+{66q!Jh`hCZP`u|9Hh&gPFhfKnrzW#+cikl=qPu zQnVs%v;)vp1{j5$itGLU^+5xY>>@IK2r$tmaGvXSkWGu#*0w(X=c(h`7KuYjQVU>h zG^Zo~^3PNiSbg7dKKH}XXC}<3`vs6R_hte-JAFy(JOBwgd+jRBEjkSM)>@E+~Ejt z66sIh&JT>pt6vAT*~*v4u&jm&7P#BZL#QwXRV3<>3_@s9$Hq{sXu=c4qz|OWs3^!s z%CN^B6){sn@Keg~D*Tw5%gu7}ez1JC&c>4K{1}!ln(pnbS!(6WMXr@Z+2*M2cqpVL z^sub0l*f+ZionyJa>lv)nPYsB_Jfi4b)Y}J?KhKvVwc*^E*yBx(42om?l%U&MxKK5#qy{3 z=4!S|PgS9{gpX)vd=(Oeh&E<>I+xcQ;b|}2X^{cvWd^e0ymry^D{bb|kiiV!PAr>ZJ%@jC%^iO< zWsNNR2!T>l*qbV4*^?$IpT&m*)*{<1W&a>K<}CAiM}M7u$X`Ck2xh(Disx?N7+ks? zeglg^_a?1&O!tUf%ElXj0Rty@bQ0qk(qQyL5nz=~GU(7Av>BOQ-&&3Sqt3;ST{ZS# z#gsZf2Jb!2ApFQ1mVO??a6a}ogvXqhTdBsgQ#g+O9Z1LBi==~;w(c0Jkt*FmAg_~# zairQ%hE7R|5NSR#YH%U}nUUO1s`~k{^w*+ggkqnF1-s-9z{FT`0tP#xx3dK4krKd~ zwojtdNLq^?pEd|gm08%)r~7%|c_vtKH0k0erhIP-E9>@e-0;r&t>xKw=SKMfugcV_ z+S3O<*ZXvwJHS4qpkV^opYjWPMmV)|-mt*=HL12K$sV5_X+fxhr28Vq1y8FR;B)Es<7JSX1 zUMcv;gMj03K6jS%!Z6a~#M_XA#+zZx)Zg{W70|{!y;S$=BV#&_#4n=@<=*K|YPVNT z+QJQTU2IHg8>=-9CAF76WJvd7Z|O`vu$K{_tHR*T$?i0GMVLW_<_bv&SN#eCF7!ns z;T^WLWA)$n8j}1o`s4%22>H@Y=E+nwUgTrR2V2XT0KaVZ82W>-(vASNHBZ}~06#*Q)O&nzwdkm#9%3Xx4th~+W?0mm8mRL6Rv4iyaC*OwqgxB5Lo|%p| z$9Ewfor05-b>HW8*Xd2G3wOn3&xUW{Y76@CS+O8GZmYFwPU++H=2Z48_rfnJc5UUR z7G*YNbS1Oe!Q)ZbmQ4&IDAkZnsIozrzfZ0#89KvUFnX8|dKq66u@0Lo_?f+?Z$(tY zOHYzNG1Etps?!|}bCZBlbBL82!#~5Ur6GE7r;af`$e=iMNdCp%WA<;t-y^S7A6vlC z`Bq}^Kng#LnaWAh1*5N6j=`829x*Kn^+OK`_i;5WULL6ww@r%~nD&uM_Jw3M8rvC4#hsRpNYp!ou zqS`+jU}>(mg%SikZ+D@Hk#aIv0;@4LDyVKe*pXe6F*d4&x-3`4@(_ktD?TReN&2~F z-Kex9D{gMt^orf`&1x2h4$@{d@$ZHy?IMs0me0#eP^zlaH#JFX;);IvH%)nlAQvlE zrb`tIO%$^RMOR`#s1$>n#RQRxzPREu35L9hFVdllatod&*SAkvl6MtG6=DTp_X~E} zfIxYjC+yZGhL(H;8?_=*0odXqYQA$CH))JYg@vDt**p07z)++iiKMfgyFClSmm*AF zj@Z#m0q1hrcx+>-2stUNAE1Q%W&=$_%UZsC6scJu+$GHoFl%K<Ms|-L_z^(c^~A z6{`3=F9#Bt?Ij6ALU%SC?QrP46w;J|Wx~j$Qm~ML79=5+GaG#o3hs2cf%|TkYgO{= zi{CtKN|yKbGwYY1CDDt1R~@Wg@s{A+JOp*hPnH8aoZm^`p4c#GKD*R;rD>%s?f+f}N^C7g%y zMXxcdX+w{ZSMmmrZ({UkdBW(1N{h|~7uk(VeGYp>gl}I!Rx2IH_4!hwW2OccvfCo1`m2jT(AMcXB7U=^X(ToIwtb8D_*|%0zN_Yw zeV}EHMoD?c6Qb&|kBc9NtAASxD=%V;zE(dpyU57cPL<1EJCkSU#+1I#7^%ih80}i~ zoUVTrTrWwmEl<~EKa()^ioj08hj!-wrPpAiEo{X3?N9$t&fAWg8T}6mfj56+;Edv1 z`bmUt4h9KhUaHuUIi%!=GFg#_Cb^3u$!o+?Z*sIVY9C43;a};?&M_zzm7~%jiLm_<^jW8w_de;vFn! z1-HCqvk!Ny`NQnbJ3WPBB6=@_AGSohhtPz>p&d(%Ou88c>nppVeY$eNvsz0VH>fh3!6Ptca)>Czk#}~In9Mt(u1DVZc z1Q*0upZZkTL#cLcob*$aL;*TuPEnm*V~M(9p>;VGI}uV^7zvX|H|9+et8SMrT-E9$ z~@1cLdj3?6BL z8G8{l?=1xvs97@~mZU*eC1!yE-=O$d0a>t;w2py^q6?v^-_q07@jwJ7z!X$14!Cq4 z>4meS>$Oo1Qjiuwi6T{Fn5F}PrDOfIp;bdQ9r%H>w>pm$Qb`BQDwUri-+v1oRilm5 zk4lgl&}7CzfaIyx7B}w06a)O{@)6haa%;40AedNr|e`=rZK=I7#YZsmeN(^f)?I zQKgWoh`;0)Ws8&v!@yvJxwQvU{X6R2Kd6I3P2!M@g_<-mhFHRO>fQ`Bvis~Go^GV< z)zrNe1`h?e2;O6Tv_`ap$tzhY@{E6%l=~eaw+A6C7^3u2l1ZoZ$YMlK%d8ldFr`Un zX;u8`b7O6P2~ZUid%6DoBM3In5<*;>6Mu1fS4-DFI}t(51ix&>x`#)TI?HMm`}g8+ z!RlVPS#M*D16OSF3i(Nig0OUbZvWt4MfOgHJ=T1DKkUK*B`p7Cv!U+XW4ucXjxV*{^i9^XfypDvqiJkfb zD{mFzW?X8f>m{AE35Tc{(;71%(b}RUB$gL$TCCW%*E&PrLo>a3!1chBTq0RIW!Ytp zJm-|X>ZgVPD2lLDzI0xUv``XT7iqDQOB#k819I*twYU-sw_6X=Lf3p@0gvRaGqs0| znH-~RUO^r~H$+x62qhh{(e+Y4AtInWr5^F6i&0i4n9}a%Pj*jt|WTwP3`AU|si{)uN_H$V|hkvvT_{qg7 zy3{T=&G)+OKH<-<0mA=KWxo+gu#24}qNEwYQtYdmTutGr_ zmNM&x6Sb8#)|Zw?END_6Q;hoejgAKWd}?7c{Ra7NJ`|S9p?g;;WExnSZVJzWJj^o= z>dH4A(${Sfb?PVthSGGNQV^usNzN zi*-0h@g5Y2R8Y?ohU#;>l6{7HNR6W}=@^*ND1}ZjUE8v_pYA5RR1aA{#T*|X9ObC9 zA}~tdZs4+hD8i$jSD5p%Pr>7v`}sIlmGXoT7=FSdP42m)2t#f)Zs9H2yo$o)--rt^nOV_p4Tt9x> zP(!hP-sW(WELgufw9@w_dHFgM z|J25#zv#cE;UC_$0ob`WwYG|29{FpoyZ4s>;MjW*DhEtZ^(|R{S=i1A8!*y}z;ZpBm2fn(ikqZUUbkgUx&+6LJEM&Z=vV^;Fv#oS%aS^Dc;lKE5Jc z@b@`Q$UIi`1PgY*t)qgpR|5H`#2$|>5~h|jp@V8zwAXaf0z@GDFB@CN{!*DZm3oXN zL{!26_v-%ZS%b*4$;IisAKEa>ZRkEQ|ATNe_g+{)_6H3yJ+^S(>*DEIb?%CqL7;;a zR`Y^&?jOSJhaXmw_q|W?1oL?s9i5IcHwKR^iZ%wXBjJ|jE3?C8!U56e3pp8*;Db)5 ze8q;q^6{298?4a8iX@2K4!RuJ1#1_#4Z69Y+}nAjPz3?B3m<3GtxIRuRt01`ir9fb zd&aUl?Tt2b3$6WmR{;kDJiO%%;7{awf7!ZCL8;+yGw}2%$=hn(b$@SdrYGq;@A)7y z$?A&cjx&c>WV+qRhCDsBM-e8^E%m%{T^DwzP{Sj`k-lr4e8PdAfjNVC$)MZf zQh-H?hBH5n{5JQ=HapxyL*KZu1t!c+SL;Rq<>kVX$d>-F{WYtN2Xf;#XW+2cB;FdW zs0SAch2&vxUFYErid-Z7)DQ z8o^g7l~J=i^HMVLRZ;q;2Oe%2dlWm7vSl)0GXG@>?+wCKbPZSJhX*wtpYyb45tbPI zHVLeJv0T>A!4|PhD~KZOLXl`OdNM3&%#=fA_~?3)$pP8fKk(!+3s)q9u(NU(5_oOA zP@sNn9C6kq9xRJ5bC!237Q7i>+yKZJV>|{KhC&m3rvt7!Mt8kNL_Gt5-e%(s>nD?hi%8%6kE>qWO>~b=JU~O%%mT&S(khRi-`3?- zWP!j_XrI1LvmQp^xf87+&O9Tzpa0oO0yJyf6J89$M+OqP7x1X(d5U z)(ZgkIMDB>FP+DfYeK&)P-pZE?e}_Fn4mr_H5jam6Q2Ta1r`#Xjquj0dszJ6C4afM zf_qQy`9o@8Q}q~H)~f2crg5@n^ap+*;Nv_^O=z|VEegM;2uqrD?Q*>?$~h9<@VcI^ zy&M5Q5wM-QDZbh`6_1VOd|FTf%2J({fj@th$Xw0UC**%|d7r+S8NIcw6l48`Z5>Mq zJxu?Bw$WzQa*1FLd~tu!FSPi;`{Sg4AMIjM%jm1yZurDe?plCT_Z-%D&w`Idjy#6# zvETV3vcwTzpf=5jo-mGd9o?4PCT%aeC%T9ye%pVJzeGi7n9MTe5QC@d=cj;{PReZO zN6R;MGM*_9B;t?qNrRL-8Zwu_MKtR*M-lTv2#Wf)|KV>q zyyu|d*D*bi4RSbi-M~6C7q%Tb|86K$TD_9r?o^lEIwD;-Cnii#s+jh7)G$!lEai{% zAh8g++%xh^@Zx#6gLg?sULVf59sdKJF+bVY82C((H98JDVJS3Dz1^s0z3~DTE!^s8 zLU|xQwrzQ6RpIA>LgYHPQ+4a3(k&T}Qkb*2mw{IfkxNF(Gqe29klAg_*~h|Z1VFgp1G zqJIXJ6N~KQtN(Akz8xro1BSWPw;kb%LUZslbdaOJDejJ-5D;{mK+Y4ZpttBQzX8QB>wIz%DI(JK=?jFv24BDHU4m2)4%#X@o&wR+UJtx zczo2w`5XV|Xbl2R!w@j?}03y8R{SD3gD%!=G zvFEX32zZ=>MWmMCgA3)fN~}ARVBB)u>E7XGF!Pg#=d^?J$F*l%e}P%GGM|y!`~PpH ztO!5Zq+mJR?im9(weE3%r1$-o7ldp+uv6Fz7$(@=wFxzn`xHM+*F)xX&@Bo8fQKrH zo8Ukl%;`n!Prr88xBO9M5%$njVy6reWWrF6J`=8Ip!6xxev@YP(f1u2{YT6%$=Q{p zj4B0Rc~oZ<1O@d##KV#YYw`djsA6}O@Sr#Aev!VtWxPTI+$%Y z<_Ft{$6BpW_MI(C&RHnDb|RuYReT6>gER7#0u&E5!%As_t^L;?(AnwFkCJU@*LB8X z-_#PKq&wh@WM`+ap+R&_4r3u<;PrVAoAz+n))*%*^NjhLKg{gxSY@o(R>#Dpc2Q2V z5splmTw8bsH(OJCm0|EVCNrD@KG7I+`(Jz}K9ND8pG~2B+?M*{*o)k^mHt)e$MCI( zhUPsJoA6u#tdxz>`~LP37RmSlo6Il7dk~07jN}TyQ=eVf7em)|(x@k`|A7CD<|HHH zc$e|ip4j!a^xwean-op zt;jm7#`Rn}I4{rYV}5J0?L^pc%<}%rHl?!ysf&CEh$jcSWWR%`0up$0iInyEugu8D zUf17O88PspQw_fS8hMkF%|R&o(i;78%~OAt@8K7l^ntu)C(?)0|J-jUPR4y_umg*Y zSG5ax+EGfC?#hARF~vo>Bang=rUjcO3m%nm1$j-mz#i4uheEptPN0MZc$yfTbY&Ha|%}s6>i4OikjEI01 zH1`HF&!=|VG>3);4|_1!ZGE6*K?m8tsSev+bzNYQ3XP{!z-0%OR%y@vk<1G^TNcId z6`TCF61cf(*b1AtZGUqu%8dugZN_OfL_n7DuKy;SB6i_4ho8TH8qEZr<-XL|yZ3kUY438!qK>u#u7)-iVIG0hhZes`NaaytySZ}r zi?w8?UI(|#zWcBE#I+HNd19&wM=jrJ3vS=9D7}xu*nC?8HP<8GHhMWcQ51kmlUx?* z^ZM14)>TfiRQX6g9WN)Zs;~8ho=WG0|Im-mstrK-N|A7gg6F;!aO|=+27rsV%Pbx} z&sfy>Me-k4REN2}k5~*x*3p<(8=e{kjs{)NZ12c!%en11 z{G2|g*4NGb$)xv&xPIZloAGPA8G3q)Ct^M+U)kCGRMN85`iG#*)v2(;XC|!&$=aLDudU_#T*Hy~E= z>>uJ=(kT1X3f!bwdnLsr(-d(X#(D|wUDIm@$^4y9>Y{?dZSNae)aEKRI)*YSd9esI zSe=kwGDxj393;~GhR0>=s%AuM4KQpZf?}Ykn>6_k}`w{(Xv6S#_Z0G z%zBthPlNA6+%v=01=y=L-ZFJf*?;jqU132SE3UyeaL;+i1@YT9kTOZ++4edK$##-7 z1=0eqqi-(F`WY5_%fwdY9ahu%kKkC$GW(_@y^#&}=FD5HMe^+H-R)k1!;q-j!x34f z*OynTl7&m!((&W7VAv@5Dg=9){_0LBw_XK)|839hKhA$Z6nu*~Uxpu`f2`~o{m1;= zFvuiB?5gWJ2rMH4ruariUX0sopj)z77``EG3l6UofrEv>kb&boihQINMmat_=bESa zbf$-*3ZV&tP=-PT?Z+l~tIr0F_eN1mv}Ts%;tAze;w!%YM16P;ffUKvm|KH8dxEfc z9=C%&vZD!y%L}}IN4mT}uN_0z2iw54K+=hrr7vU(v8)EnIjpRdxYaVHTq%i z(_FsqV1E^Gdx3i4=VTHT@4!<4_~*5t^<6b`9D%x?LdpQd%SpJDM9AhT*mBRbmU8-} zR1w$%Twt1xueu(x+AMVxsBr;?;T}8RC`FdA$&bkuwHbA;*Osw5me9MYoLXX%xUQr~ z75^#e>YG*KVbyfrNQmg3{pR@akp8J;>e+RudfI*^?`$i33{tU^BQuCQ5Gp&uJuelY zLzMj;e%+YeV_QJ<_nnPhRSKn@{116$9_Btn_bYbo@TniQ&~T9-mcD%JSPrnCQYn_*av=@#n%O zg4UMtzxW|GQto=}@X(S;=fY?4K-BU;s#tTzbI%ff!__TX)@7)I4hk9$K>7w-H_2Uz zV*`$hS__zaE~z^0STz3YX!w}}mHIRqYA0HJmj1F&na2IVEWs`0G71Sf4PyOt67P7e zj0@fZFVyzJxb#<8@2LEgOC+s~D-9L8v{*&yGO4^{kiu>{moQ8n#|&c_1w>F3E>%&I zZ}tP#8G-=UwBbH<%h+<11oLNPnhji*^C4%Ni3Sj%WQkyIT3;&z_E6nmtZcsCnE+l*+h)|Tb zX!@b*^Ox&QdrB!fu1)uIqt1ccI2)L3^Ro&0GDro>;8**do?S z7%(6kaGeUBIw4k^Y*8wR6nzp&1gu_(LwloMs6uxm9kf=rM&|iFdHWCF!=N-M{F=*to7?UK~LGR_4%?1tAx_fS9bk6oh90E z>ug>9@N)s8Of&P9OSbkz>iEN*((r%QP_II-v%TlG+rRJ26A|<0w{)Ckek|u5P;rwx z$Dg0X3ky@M;c%_|+XL9Gg?`yDHX3!cSDdxzMeq?6Dw>~g!?Z!;zu4xzuH@Oc2dnie zh(M!kx|?!wmnoN3a+TAS)6s`lJy)%$EV);^>HgsSt{yBq;C0@@E%%2b@`FetNwwhY z0;d+Y_H(TWgEFBTqHM5g;c21TETS7CKTMov&YZQsDopR^OE_AFj)4zexdy%(C%So^ zXd6axx>a$u?70|JBp?usm92i`a!yHUW+S_ zElP~xZRc~H!HwEZK=?!FOa&{JXUA2R7%O{KDn7jXHdW5?(gFw_ni|si9xBO?^u!N- z+I~8^9((EMOQoiRM94UDVT`2|A^(2#s8)agIy(2(Gz>9M`X+{Y-a5N_FpM>nJtk<~ zo1B!~8e9kBf(+68D0IyQ z(w$mAilb}Y0`@$%I*K}CW=M4o9GSG50BG`TUe*o1FxpKjJE4paKg$LiHDLcEEawq_ zWvBlK)w@4JmGU(00q`F&Q}qVL?7!eKPRza(M{2xZCJ+LmNMVbuF|qnddAD!TN#ZgY zSKe>r@Qt8a&FFLXvoX5rnKQy{>(teYS#6*DbBgdlew{4NvcoP#Io)*)?2e_VQ7L^d zCV*p7{&5wdVen`_E|QuIMB_wu`}^{}CwTv!{2TQSkZc@FIl=UbDTpE}*weL_gGr`s zZ}L%s4+<{nlDmg}@tguF4Sp>Zmp3`f9#UL4l3OY%1^bgxj7nA-2jar-CojLpB^A%; z8r|O;0aV&@{R^(7+q=gg?H7wZ*ItX@dZ)u9)uB0XE)XJdogmN9Lndvf^AKDop@J0_ z+@CFOlCS~?W88nVImn{7y}Ux=bnQdRPI7=Mt>c2z{APU&Ntx@SFovs*`h-jlcSl=| zYJ1F}6Mznr;vZ^6ZO5-?jORk4qx4q3Lxd!}3AG#f0Vuoj6AzhNR|Iust{<>e+dkUN zuud{2xz0(_%l&q`MT8#vrACf6f!#lg^Ce1%9<`QQ>j&r39bq|_L4%3z%4cF*_u3U! z1^4XYYN9~>S|w{n4?(7{TJ_4Tu38*=)ECP_z8A^ts^BT_ypzIHaK4I1u;1@1N0tmX z#oUGZ$4YjRTo0l4TCCXTs*jPFYX&&P6_m!8N`I@guxA;R##`t3Kb#h-&T_fjX>hBD z;In&%UH^(?EQE+oyO|SHPpH*dX})$HxC8OOyW_eQ%Lp5XBJfTyHTF^D@!}VlU~>1{ z!9+wC2Mx+`A0yy7h=@}K(Pp#+stY<->ccsF8xn-h10$(H<{4ttt!0JuQ>qRaTD3`c z>TMzIv&9WaK+OB?$cTGtVAKQ7}y$ub_JLx2$g-urs6x15_4$ z-p)H|Z6`xN=JDcl@f(gu6FjVI&y5=XRR5s}?0jFHyUH$yIYA^is&dd?I_3(!%I@{O zPa1W`rD}Wmlg%FS492-G48M7K5P`eH=ho`TUL5hg=)&cCQrnt0HUu<{-UGP62O#Zm zRMkDa?GL~ZHyq0aaNYu(<)mr+aL+cY?T&PuajrEr)8~Y#*8QmlXE@eJOSQu9k~Z<_I_L094f#W)ebCi9grBHGz7!3pLf*W9wZ0 ziL+o7FESawtgU7noSzS1DW$j1cvB3SXhxfz@7kFmT&H3G7fAIgrk<$lralHw z$$b5O24g42G3kx4OVIdPfI|j09JWlQTvwXzqr?b^iWJllf@kPyH#(@&j z$gj()*uy^`CGB>0xmwQ%l zHEK$dqm(-$KSYtpk|?|~WZ2l}DhRCp7_JC-Pykm#%26t0ZDjE`qt5M*q@ikb5cpIz zi&?BynCR|+E6kgjohM56Qs2C-w(E|8vRAN6ATbB>Tg)EiLC@-MI{|-mv8LD|O_-7;wbI!^RXeCyb^$;Eb~8(vYa~f_hx;ePeXt@ z;Mv352-HO>(u_m4!zo?>kD+2c4U`oAfL4%^>-zNMDv)h%@C@jm$9M&P&gAoE)*}5t z>~YMcXLInX=Ct++VEXuW(Wm4eD%f^Wz=Uh&##krMG4H^*Py@w|^F_YO$>j!*-^)Bn zA146C09w}QX|$_iZWAbC)Ky`E03LEmn7G&MaNGTr*yzRSev(CbugJkrvQc7MPbfV7 zqgPMSG>@L(i+*D5bv7EV(fy2{^=h7ydD(-95>Eg#kdgxK$2Zwif4-bZ`DP+|@VY>YGjI-KTyxrbXF;Whsn%Ll^e#A;V6a*Wc9(Ziap#x&M`aqHchE-oDS5 z{KD0nsAu7R+PN?MxS_IDY<<4o?t$x|iaaEbNBKs0ajZdVuBk`1p14%Wl1)aq<0PK`W48RjS(ST>#eEtE=%Edn{{+c-Mzv!q{Wg9Hfi zUz?yoq#}?(gr%@dqWtdTt}6EcI4Ld%4Og(wYa6KYm=27Qfuhw!nyI3 z@dOJL)nG*tL>g-hGkiU<{ZdVG=~P?>IjEm=h2glg@>IHHsk0zb zOk*33AB%#R))^1F6xH&@48-6Z2pT!0vvKBfKs2DEEtZjkVTY&^0nHt9%KX)a(g
qPqXY5 zl|uJ#<%Z8_xpV1BR1x*xU8>0x{xM|NeLdVgBE>+kZ$11ZSd?(^0yMA!pW`#%`&b}B zxj&aVThfEtKil6E3!puRN;aygxJ)p_48~f1+3MzHv-f9Kb<^}vuo9b+)%`B5@j75s z-~BeHRe}a5WwZ5uHtO#y`9J)ln!9GedkVw*>F9(?;Yd;AqAl#Z>r_`cu_8~(nxld$ zB=cwhA%NJ}_$Pp>BMvy2Lhpu|zXCO`3?K!k1=XE_nuj1lWt#l3pu@|Dsq><`7tjN* z|5nQ|wJeVbyy{s|%l1W1uYT5BkU7-!wALo{df$BAM|X$xvM)b>dtJ?MS045aP+g* z!WQDNQ&g>NA!`z|o$cggF+@R0=R1GI4T~4{%++lKWTm)=eI0KQt}GkTdYEnB+|@Kd z*3iME-{SDa)3{^_Uz3NGLp+aC@MX_-6?0!14I94x#v-E`B6QXQ6*`GkffPtU=&TS$ zrfR>-j0hRTp~kBy0tS9m)}HS07|q#ei&CmJQad_}uLkPYY zt-w7YT>d_@<)X zs;}g_uW^y+>qdO-&>@pGx3V^Ds*#|M`{*zzpZX>#|ErVx5O9tVhO&?!i=CBGXH*t4`|S{NI1jp32$f}EtZ=5YAhOQXnE%$xt};5_?c`byOwq*^oLb&c*0UR(ASECwpa>`> zk`j{AAxJlfl*mE48M=l>6a?w+MnJktq`RfNyBQdmf6Y1HcmMm{d$;4vfa9LE*Lv5R z&--jKV-;O(qj{av`S(m#Zd2Wb94gto_+cEKR>w@z>yl@#+gy3XS)+<5h+Zv2!-s?{ zj>oKpMGMF*g%&CqvX2vzVu+_j7EiEJcoM`)F<$e!PzrflHKpRBz|STVlgkK?jZj;4pptT?>*m#WzD`&HOM%$1Lk9&S{C%JKmre+zdPt z0HcsHdvuTP;WD$$c&5K)s$O}>{`v5N$<)K-Esn)3*(7Eny1%?3Tz&+LES^H1Q z*P%CM7~rRk(`7Gi7QQvuuKupt8L;+D1u1Ees|C#f%L4V^hzlG5{NLfA@L;#*b~j$X zjGQ&0vd8I#0oV+YHe;9dMgEkMGS3&98?+-#Yp2#okXq~}8-LN*ydR%(`=oU8bJvVh zXor7GIDcKLE!Nrpi78AUd(pN=3>3z+gYDQogEh%^C`05X`kA`A|v30*lbK(Zipd^IGC=|X_ll?MlV~G zxx!+j(yoL1C8rwhLDt2ac-Y({62*A-R&+?(!%u(R(UrL4Ilx96)+=SO?!lNTH(j5e ztU29-tC(~rc1pG8w?Yn_%L(^8mwx1WP`Y6gb`PGNf_CU*%NGBmKeA?>-UNmWt?sl? zt7=7X%SMSH=6h35Y!zL0QU*d&pWS5;+eG(dO~8zQw;>@sL(Wd0{D6V^HX|I3v61~9 zb|VAx<2*IxN8$NINwZ|L5_RQ|tW~>SMjDJpLNEwV*RMOjLTP`?3LLQHcsuHJ4qLAm zVb^R$I)55`hpnn6%JQg=or7(f+M65_|H|PRMTk$=%P6hvZ&X)LYs3mMrGHv*Hwfmx za22P@AP8i7rdOz9t+x2$N3thxZ8TY4-kv4M7cbcsqyO|U`}S4-FeA%L>u4+*Z>?le z*|`$fyt;a0XcjeJpPek ze8#nc=%xn|{kmqVzOq&|-uIa+&WtN`o|T9(uK2x?P-fbRj=(;+Mug*^3;e`@fgJzb-V_#=c&L1t%s*a$pCrPo&EIhQ9>;S+-0NDdv8!zs-WDOflqX|#*ziZ;i?Rup!32Hx< z5#;_NGidCR@k`1Ow@B9DyC3mWWeoChbfE1*P)IyEz4>ymbjvLAj*vzssU^4>Lb9Gz z-lW&{iQ2bMJ2ewN=Cq(^YIh!~8FsIXFjbu(z9=)$G zJjown>DcCc(n}{;tb*q+Petz3pxb!0%;^TN`KBdjZQ=f@EWQ2bt{+?r z=RN#LevOlR%I$HQ5KC8IjCZ>WlUZ)8U77TzsPJb2tV@>cIfz@3Q?<{^Q+#3!;yPZ( zO8ks?A~RBeMKIM;Eq?2#*OPsO_U<#Gsj4m+hW7-6e<7#gMDGl~Ce2BAgixHvJJW5e zCzMG%8G7pl*5s3Z_np2MZ$^bt+8;q7rOxv~pUvn8iN8SCC9k(^C@{W29QtpA1L|L4Mni>$<%x#OCpR-<*cs(xHSy z#Aftc8x8%dyEhZj6eJCIt>FxEhM)=7mJ_3&5GhubR#j%~B6dLMM`ICtcr;$6R9F6( zM^M}}akq3hz3AR6C5I0Mr&~%g>Y+%P#{1P2V{G_ImnWpKw;kROS3^F$pzigg_m=pc zz$TePbu1k8!3F=85e;qvF*zyvcSdV$dR6{lD<*Vx0V=A61;k0(;n%A8dzAXF8@_nhX0v*qnF`YYgvd@`+=W*D5 z3miA@q}lihsR=?X8cJ74uz=l9=U>Urn1tWw58Zm;(*YUpPg@Wwen;P->t-(#M0sSvoEdUUjFJa;5H8 zwCfF@V0#0_KaOAq8T!M*w`m!@A(<;4ay?scWZ;0&nR_|9E|E=HCAE zl=Zsl7~QuMVfWuZJX(e-=B5G+4js4k%r7HKivirgWw4RHB?sNi3=|-4={2nzY~zuu zpyUFo;t7<#0Q2NsL-4)H{(y52tqrMexNZ>KJVW=@mRL=0-xt4g^eVk4rVU$>cNuzg>x^WpVJN3EMD>h%wkYy?}#a~=C`%ZifCH0T%=4q}UP~`tm=9b1LLUR<{J+Z;7BrRLn z9N$f{S~B@=x-I7$|M6P|K^<0s@j}9~=oe|zsd4pBs*%Fo>Y`RF9pPh7GTS=#f2%Xe zicOaH#emZ8^Xy>N7A^tSA<|X7Hs~wg3>EFySRQXY=Bs9(bjh*@szJJM<>&|24o#$^ zhcBJNc`j=J$v`MkmczXCsgv~7$mZfPY^x2Gd3;S-|cR6+y=#?_@-_jZ=@y0)Nr z^7~q!zqzJ<;~+nAoV>#=y)}4OxszJRK%}~TV;ghr0=?Y(1t;Eb^LWb4Y9kq0H4ZF~ znK_8Gk~fXe$Et2iNwQN>hd;ZUduTh*K3XoO%@JHmuLHA4>+)ielTlPhm~GbE=nw$3 zdUu)~PY6Fn`}hycvpNF?fE*}<=vDP80&OuRS1REW1`;hQmRiE%M#b@8pXvjD`Xs?v z^J~Lkzz@6?5eiq5a!R9T4kGPtgB&)K%j>iOfQCvCVjSK& zoy<_a)`1rbOo2_a17G6@&{aX!{)_{^JT0w=UL65d6@B`7Q?4uS)S~0;ozioqBxI>< zN>%$t+3VnuyY`XGNgQI_mCC`vUJd2}UtaH1A9hp`rR#jDBQCOFH1|qij8#`RJiS|FZR%k4=VmhE761TfZozwY@u$Zw59aS!^owsdnd;WDMjNfq7rikjP#|V=m*_*c ze%umG>G*tjF)<$fguc&X$cwo0#RmEaQXg9U{k7&>`$35Jlx~%8w1x{%)Cj`T6^Z1WNKWdK75gxSD0tEYw7hrkj-%z5<5X+r|Qh- zGslO~Q1PP>3~9ZVx4H4#-AX+mg`arolsQqkX^pCLtuL>EO!t;BOS;YTv;BBY+zeJ4 zm_=j?S6uI_+~aPz32TmxK1kcAe7dxaQ*zX^h~EVMOsu~O-LRJ;V@6*LZzyj+2ez7MeG}&dr^iN;7Xvd;z$-7Y$(*T^p3-tb&8g6dv&e^pMNGAm5{NxNUY< z)6xnG*1SXwP>1K0wm4Sr$7NFQfsp`UM`WExTCVJyO~~Ro4A$rh1?iGIxNn3qnt7Dh ze=a2+`{IZR`Z>!{yTOuF^Bz`?Xs2J)=M!rbXcTPux~Z9UTrc){Z4UHGFHO7B|0LD4 zd>|;XRwo^n{y};VL$7PE!GCpP!2N>M+p+SxLB44`f%r7zW<@9w(^E&0-6bCLfhk8s zek3aymVvrLn)UV%?4BkrVSc5bcb>f3_0K79VQ}SVQy~e=?GwWr;`PT6H6`JTg~vW( zAQIGcW;37CiAJ9&GMszo#|d}WSl2&y=bTx6zQ^A0*4<;zc=YbNjgO=%G_UYGhor=U zuPgKu$vsW7{gqBvLHrCHZOjCGayIcO=&s4fVL8IXtXDcgDweqHYEVBTT;;LqwKf?# zX%V}CvIpEu^|(^Z*_9JZ&wX4K*_ANUxS2w~;2Y{yoJ5E}X1M&RB9I(SG&yRCOVXt> z5Qp(F#$oHNLEU780}fW^WbX(U45DYS*sqQ79n(7rxS)wtEueez`c1i(d`?wPhlsi3 z(no2-LTf2vahC?C)u>_-XH^cTjO;OAy3giDnnslHp_&#Ck8#ijqtGlkD~MdWfC(HQ zLZw8StFECI^N+GSXUj~uj$IxXAMq0Cv!Tt)DGJ+fxc=P-lcxZ6206TwVBQ$k7&Vp* zC(y1I)UiF&M*C%j+Y?!y36vK9keVm**xY84<^-NzdIjSiG6qNn%LZFgM*@IhWrYt9 zZA?1BJX+p)asqaM+E4lmN#wcPRU4iAu5O>b5|9`O4mPXQTXpV` zzjOQmQ*l;bu&~W~99#qN3+vo16t4T5*aLY9a#coyn?93Dvrr=&1Eu2IYmr%dJ0-3< zAf{C-i`u>E5)aJOkj=}~*ggrpGw`f6fF-Q}=0yffN#*=v?Ou36?qzed`b0V7HYt`!@q{~T@ zP-YRv;2I;B(dZwgy9du%mIp_}M(Uj@;oS${-Y)BYkeKzD`U1PrdqCR=dY}?05lhqe zSx(kIqM*p5^O+rE*ZuPQvTo?k{uT~-$If6eL5>PeLH;y86(nY2S3$J(HGY5hY<)AJ ze_3oQi0Azcje!j76)W0zZ{nls6Ni&B-PU6BHv$yy(AeMN{I$7KwSM;Zy68F2AyYX`bC9rudNB%_VjQ$NTd4CN>m# zKwQd;bZ{Bz55c*}KZ#ZKF>lDRx!pPhQ5%3vH1Nc@#ZCYa_`F0mCoZU}HlQR04gn^j zm!#UFq60Zf!g_scBZZhpY=C-E@vl9Y)5n+7aJE~hC7F3E$T2o8x5ieJjPsE{jTg@v zL-mb;^y1K-XDSzbf>+q7LpLjd%O$s<RA?8!)ZT4FXD*+l()##ylzY>!C_5ee+X zEg!}`+5%$kd>0wubA17=a_RJkE#;Yb8G|<@jQ!(U$z@MdI+fsbv$iVLDs;f2!0p^Y zefzXd@N8tp`9PR3d3CLj{{Y6LjM0poc?csD?%4({-KZ*eU5_yj{CjG(4fJ;(fa4Wk zipCzG@s|sG8npfXbuubaZmKQG>`)C z?%lu%HmsHdxzwn6rU&f$=?Lf+Y6A6k2Q!9$TFO;w`FkwmQ}I&uXA}naz!ro#dTC}3 zc?24)6CzVTsUYmiODf3cuZ*EGzfI~x#RuJ=AB6uApciri0S>I`Q@j!UcG8b0LEs6vvU`%1(ZcyX8UG98!yu2PV&pYY_p7$ zEUp8?qz0xycg7CefaPgBnlvn;j^GKfEXlkfqHk{zpP^Bp47)BNV?=)gsI6}}b>2FH{)x`i)K^=RHnlaK*z&yT zl4?9cvhI{-zmZpm_2hFojHn89Kp`#)2>6!{G~~6E^CP1wf888q$R7~2xP3lKu>P>~ z&ESc#;^90-$ON3{>Ta|Bakzz<*x)L!KzGZJOPXz8iY`^H?Z{F%*7V(gw#C(bG<;lJ zMGm~4;k#qzW{@H)A6|{1G5SwX-1u(kcOof`Rcle^;W^<~HPs13gC1{txZKl;vwFs# z9#8F=r}a~RGszC#rZ2nFFkY#!K3SjQ@S+vVa6VuaEK2l^93d1hub-E(!5yNn3&H3@ z?1jC*tKVA8k{%JkEMflK&TP1qpYhH6!GaMc{fN;)Q=E~;DzxwYywNA>4@g6PVnMej%2Rd_kc6NGq^bUCD$In z+dcJN^&`_#J(5`@*arZ@f^E>B+IsPH^`S^hJJj?=rfz1YVR2cAGJ zZbHaQwn~s3#qw}aXOj8)m*`HGOxV?_Y#1kg0M5zF6y&N8B_w!2q=xGcF&NZJ4i6szIIjUYGS@-+bT32^q8 zu9yK$$6`Mi+StFzWan3%aC3RT9=436sm?56mwNM(PnE4Ys&FsFvmGuFIsXb$Qz$;N6%fp;!U7yrPjh^|hgM6Nytw)}12NJFvninj5Ht^-I z1KJ}9CKLw+(qb*5K1Hz1-SpQ*NdC#i6t@r5383E(D-YnyLG{4qTV4!dSowC_PxUMr zKjaj<9h|TB_!z#`4YJ%XJGu_PBJq7Ua-wEOYylE#0dYp|z@^BRxFi1=x1C#7P=G`t zo&{|&=Bqm{cNN$IATKeDAM-CJva!z~N1=Z?a{@YW8}{I@Y)V2(d!+6zK#dM!g^%|^ z4zY5lJ(%RJxmRCeVRKnw+4_&sTE8bEzEwzkCnidgE|Y`>crJbH^ge#kgG(nV-Gh z7I1jUO}P!+aLDBczhCyeMSHh^s$<0NwqL(_>2FS#KI$>Mw@{s05$X~KIT+}U5^z+ak>4^XxCB+wu$3W2O~%E&7l$WI@1B_n>%4?p8mQn{bRr0A-Imu!oB4!M!jI_2{_Y=+R{ zps4~V3E?MnRqaNWd~sE@TAT<;Wi9+@Cfcv*WjGPwg-Q#*o2J@la)VYBePq*hghCG7 zy=+gMxr~8mFG3??8QMcXc-jgTMrW&Je5HVs9DGv#kp6-HTsUm~ArEFtPMX1R>pRo( zTy&Mok;cKvBxV|E2GN8Co@x0^MN?%bRwZ@Qd$aXeHnML|*6T@F9K~4$i>>$#2w!`B zW5k^KMW&~ZIGm+p`Sc{m(e$YHv0ov$M(~KkUwMC5?|!7q!|6UWUdG{s&BGO$CP({l zybRyBRkUdDs5GJnznEZDN#el}vIo8uHa%UIIX`uQ+KXZ|GT|`4N^o*iHq&(_kV%kW z8-TxMfe7NIxzHr3oi>&H)T`< z(HC?RzXrk%Vcr9OI>WR@&zaV%r{#yBq9jYC1h$|_N=2wa+|)9rs>eR)gvcJf;eW-fS45!eD?%Y&cDLGcM9d1IiBv%unbA z=o}!v3a9eFQ8?qn(0Mg5SvR`hNn0#$p+zVcxjZ5S4{GGbP3+4t`Qq5#Z!SmF|%&O>S$<7y%V!BU_&ftuLsXZfPYP42eK@i99S!`UcmE$Oi2~yF?0q8M%st$c6>~ax`=<`GGWI1%2VgRwqvyu zPsU5R(&0Q@!#tN-BF$`EFlS zY7sl-*_@YclGmleF|)@jcYX~1!%784kVNzG-RmBDgL3{@EMy7B5F-R# zz08F>U(9@nEJBh>1|Yio_&f)4IUP(4M!$uE7J%8pVsckNJYCrjwk` zHr}Tg#@VZcZ`hStDbnF4(rN!$&!S$aQ?tVVgdoV$QVm`0_;T=Uq6#iB8ohThuwkI`{wPDGi45aUr zFEg7Oz2FCKvLYv30j?fn_AX(d$DYnhQO9l|XbAJ_>4Fd`*qwx}M^JgKKR*j10m%ny zX8tA0Wy_hH7313#JV^8$s>qka*%MUUp8>EF1sSEI+K&He;sp`?Y`G!qKfqwMD85_~ zmNCj>Il$y(R`Y_;j-Z^#7@R0h_1D!?HC*eOyz8dItH)O_|LgpyH@N6E2~fKXIZP_F zhZw%L@hoCK(xTEW7!v4l-Z508@{vSMmq~ruTto(tq%vv;Q_$E}#2y>$G;L+LJx?uT zL7L3;^Ia!cpePW%$H9%$a`|7Yo|K>vXCxxCjZLXL+W)nNs85BjS8n>VoM7Qh=Q-er zqnn45Df@+|?I^~`eHNylou`FB%$=OYp{QPna> z6Xz2eG;@bc;guA}(>PV(Cc`Bnr|tP8*W?xD68ktpk_=HZKGJse^q+0>@8?L>s7*r1 z5N(A7`He9K--)=XrnzEx0`$A^#<|rQAMG;b_>zYne>JIjeLUN1l3wV`Nz};J0BzvE zrQx4t({5$>K%>^=KRQx&&tGGA*BG64nnWBz^_jNIl5}_Gvlai>FTG>03+BHoA+!t{ z*iiqQk@LT8q<^lDs6u1KpNcS{*Es)S3!>XHqzYG$HbcvkVEF+*3@3|{&%|t&qDpj^E$Eb%Eo{ECNLh)e#s&9c4TCdG|)itt?3IzZ{216^{oH3 z;_GNm{G9kub0QhLBAd31xo({QIT@&xD3>+lF2;->@C|ip|Dp$GU{Tsj~sFHxd|NasU92j8HcCaQFf~1o-!;Ce&X-fIp$;xK*>mVj6aw+&kFd zIA>qiKXKEU0tgEJi3Y&t=A;BcoojC)Ksuok1_0cL=gev?BD%1ww~7>CbRQ{A5iZlC$Za7b*QnqnQHH z=qzA4tLsOl@c{02wb{Mhj}2t)_7{r(p@z%6h94n$x4CP@0kIs)yLV2}>_uf9Ej`cj zW9L&GM8Nn_WL6raxy}-hKY)i0;JmoDTS7@#-2o(PRRpxHFMn z5;wI3=wzEfeqW)265@jDV-Y#z&=|JXtM&ku*t-5k_qWgmKaeI@Puls>15oNa3jl@b zHuG&V@hcR<{$Uiw@q? zME$?ZrBVdk!1Pp?6^iFL`{1MU{D8No|354cN@R_xAO7mK`Gt(TzZd3zX_c9}Y;%_y zk>cm4_~zX3F&RilU}Y@wKtM_n;6y>l7wlUquS<60&twGw3|jqirYMfBrbfFx+W05^ z=^&EZ+WSJ3H-B>41xheEMLs3SRwqy#)Zokjs@FdQ+8;owLxGcP zzTSlEihm9X$P!bOM@}q3qO8RC@iWhc9OO~_@cMDe_a0Whjnn?r_W;S)HfH&iLy3Y+ zPUIv~BtZ!gqa2lK53;IY?)5;>N+*R_FIj+0I#N<@ve!=oj=lw?6D{saJ-~#6gbh!& zwgYE_D5^?cSN(V2Q|xs!Itn9IOs_LS0@TER*C26x^m0awWs`L0_YgV{-sygo;EwDh zv|^Q$K>Lj2jq{lbgUWbeaW!TSzjLsX7l_66uaGFf2JueaZVy0Nz`Mscv=JWGpw@oR z;j8#&M(DxSSI(1MkWcqKg(12@ru^G)KIrK|pxZo(J7|Qa4diM7!Gg`FpI~rcdr2s; zZ;)Eug#_sA;}}jJ19YOOlH-Erd^O;r2p?NM`!^QAOB02)xgfbhQ9fjZf_d{LP795L zV}2RYoLsDU96L-ox$`+f+_DwuI#6k z+sj-9hJ5YHhmyY{bdHp8#F)N6pfMj;y;DFe`637Y{3A^U?H`p#>JQ%K7cKtz?h{2q zo?^IUveD>#p0Kh*X*f_jF~2c;vR{8O>waEq<7Knc)HpfizQ?_h0PU;oa<6evH-Y(Bs(Htvd4+s%LQ44D9?mye6GQHBI3O3+ytUOYY47my z|2P(}?6mT0Up#zqFF8^!xI6CMs_uaNlzs6awCen$`}>5KYj1~*$}JgRz4)E)7GKxc z7w?x{Z}bE)yVoHle1^DM!)`uF!S8qW8vP*KfxJ#VPyv zUrm)reCuqPN#(2crN1s1t?+^iz#JI|q zwT!nm2}1_3rC#Tzx^L+Vij`hK*O zl+fU9NAp{<5Ux9+ZwaHj?}(xWYFmHveiLF2jc9wpM_VhWc%UR5+xN(Ei?(IwMZQM% zS3Gj=?Qf6#QtR6= z1s5mBtKVT>=gi2_64+7o?P5Tzf_`x9N;ScDp`)(+o{OYvKezDBY_IM}Z^}lZezVpF z9I?8gUzd8&7(;%l->~AiLw>XdMdl-S@F=fB*_uv5O7>U1P2h)duSKb(bG+?&E(^e1OsQVwhQko^NiA)wyO=&w)KoFbHqI zfR++1cUgUp23yiO3dk!|aG#JC{T5sqn7c6p^7iw3_4nr1A z-flYUML7S@{k7;~XGU1HBu4SXrE=+Uyzd=j)^Bm+CKkr5nB*qLcU52mglg>2;N)rx zH-TM^Z;`Y(wk2h?s#&tV`8BiY8LH}EW|smK#i*8wm4}DLRjPkFypjHIm$E!MsHgbs zW~f@^#z!BHXl-?mcE$5J7(~vaxf7#yTW;0)xrt)x^fViC;N&8+(EXRK9`9x>@oC&tGcxCmF5( zUfi(fAydsDg`$rpq#Dw6<#_n5+KPw9s14a;?h6m9s>q0@KZ5VJuLhLMVTi>_)EKs3 z%qdgeYY@Z|xmaDir?iIB1J@oWoF%o02_#@lLbUr)!pB}<-&sI@9W3syttWR!&V0BQ zvT1S;e|2n_deh_i;ZIRZquYVGVBftf)Iu&gH{Eu^_G;y{>cT{>m)*T?FJSxEO}D~C zUIhO`pag|=3&Ls#oe2hyTUvvN+ooJwJ^(&ZE~X*XsE?tMa9%FR78Zzx+pDpY3y#$6vmwEP;okc`0 zYkmInc>c?1|ML21i0yXej5JWk_2VMXve+KvTPN{TuJrHFqAp#Ajfm{ACpCRZEHBo( zRo@Q8(7)@jULN}mWh^&4@Sb4&f*(uZ9JNfHp7^zAS8TYyd}KgwctG9QJEN2sP0>62 z4n!}bgpvPLBDO7t;vSZW^VcJdT(g^F?>BDQmwhAy$m0cM<}F8=nipbhX03Zc*3bOfdN#uRG zGNsGp4zkeZdi#w0c3T9I=@;g46j0{n>I{Y3UV#Y_7<5j$oJmEDJc8W-bU@f7+rpuG z)1a))q`M!%VKMJK{0|J?Q7r;36TW1QeT4q>$$uU$Kk{m)alDUk-m85X3)&N@>lNT= z4|u(HJPIUx7X1g^t`B32XM*oHztMS&^e7QoT0{M)kRk^~9+DuAT zWTb<=d9KOU>dU#>$o-`3@^Xg_>pCcJ>Rh+;L6(3Cd{?o_op+%zad3aNynbt6OIV=S z#AFNn7RsAEC*!=IBVe+1`Z9S|hCUa5%9Wfn-5mq$YSrI{)C{*aMe8rm2UW7|e6}8c zdj`%PD^pi%NW>2ExOD&;K6Sh|n1pBnOb@7uOt^(#mb2Y10^^3Jcsq$ay7OV&unoX_ zZZAK;P}2rms=m0r8n}MGfj;Tzw;uo|$gPq7e@%|4Zr0ia_I=90!)F(r)7;H&{uZ}S z8cy!qxpNWF`{W9n8`G(l(Z$m8rht0K!ua0v&#F-Bu&=Z+Iy07*(7(h6l2fL)wD^UJ z#!xTykpuf#qvZ~6xx&pv4Rdo2PR^2xs4HKuSIQRHv!Zt%*-Cb>k5L@lJ^ihyXytBB zPj80uD5}WU@A1YnY8s_XeUO%*U7fWVYY8|hBT{em!-8CDKnLvFB zAY%t9o(gtZ-d&_$I0%v7t zOfH<)$t}1tQXFG>0$-y}+y0_`Nr1|iG zH4bgtFM*%9i;PlUTD`WiEMGHDteOe$&)x%*)|+brPD z)$FMjvu~b{<$~KGSNp!GR zrOc(@I;AzOa&V%b%a78jgH|oC?q`9m>t=Fb=Tz6VtNo>S?KyRqfPjKYu9>@bfJ0)! zPFYM-Vp%prpk}?>R-=Jr*j+eEgBcvHAX}TAT6{d$@ir(gqX%J&n!-q6V3e%(;ISV4 z@y`~i%Gc&XJs0&#K=ts>f!j?2#+6j_b&1!q;ouF>&G#4eY#L60J26qYCVZUF23u82 z{q`g7<`Q_S&b&_T+_SGU9sv)s{otA$iTG08xRj4jgRSp;*y^S{#zOmSaotklaZ5F~ z**b%YahhoMZT~A6S$wq=?S_`pHGNi+NA&joM~7dLf9{kVuVbf-DZUQxbwJvX^i<3` zjF%F;G~32_FBp7YApD)mhajIKGVO}md|W^mhTvJ;Hn2UM)EO(j{vFAWw@;~b=<5>A zXiBsYqJr1&roy-Hd9JFq_@T&K=SFQcX*Xx_=nq;%2zq>F0-f5IGH4%pB2Sgo_#zMe z7H!Y3Kgr;96yzJK7KhBtncTO-K;2a!`CK@DyD*WuW z))VWZu9ZfMkYR11{<3O!FWUeYg739|w*~n2WRTdNu@5nX^>I_jG@U0v>$?J%roOoK zJQjINc~e{6K=;W2d6RmZ@KwJt=Nky@a*=H%*M&&i1D$X8MBpjB4I3LUq_v=lZG zolm=N<~LNj{0u()txQ}JsAZck)x^JMWw8D-h5W~}eAkJXFgbJy8A94RTJZ>pptQ#Q z@3!m~-$>ht1sfP^#)S?GUSS5+TCIHS)tJHeWkJkJswp#8|AhET$Y9X&nX>YEbL)gj z8Q@ODv*qciVCz$j{_7I%(B{`(Taj-nhzhDu?@y(NtXRv9+CZbizy4y5q7ADfP z^h?*paW9nZ;~`WDeg0x%(xX55bm3-Xqj=E<-NuCp2^`}&eTq+2AU?kDwXwtIwcXit zZ6R_jFnHnY1)oI*>EE;=S47~Gb}!(>^oZJn>#K{#Gj6GY!N#3XXD^fw=?A+wxuEHr zzwPU0L*ybm?u0{qu$#n61QgiZkN@SHqTIWWlRWJIK5PwZ9KQtZv=gn^KXdsgSUZq& z-A?r8*F6V^d)NhRP2)ni@zU;Pnt=skUr3k~YiVoLA_Qh7C;Zw;Z1#ybjXQj6Itr^O zQ)qw4RbrMuVk!+!mb|HI7uGe#rLASqWY_>)D>7*e_a*gKoG2PCixaquKS3E2U*v=c zPF0ld$xK^Xmdhj(0i(YcoXkZ%XKXPVn0W7mP9fe^TsDR?gV_|f$EK3g!DqjcqS(ZghgA{T$6J&js6GQ=#AFDfpyxv7R7 zqYTu-6|DJmYvY6ViRgEUBVgdY1``jkk{`cD**5Ivn=LuUJYJzjl(1iyH^bY#R!O`- zuyn&a2lsJqAV6)61laYC;w=Cm#Zq4HYF=G5-NZ*aHysVYP7b`z?m)G}NDth1EKqBU z=vugL)4xsLaA%%BJzZC7^g1zFI8HF#YFk9JR@NOaxoNjS%%pnUqC!&G5uzdI{>pUI z*^Uyj+~%fW1_0aseUv=j6_~COwtwZ-5JZ2mutZWGvs85>E212E>KAK#tc7iQp{|HM z;xjXjXlYm6lqoL09BQX%DRui^+>yPsqY}6 zMGDS1v2GIP>n4iVHSKeQ1KX}8A6%ZZ4F8Bpw8fxJMc6kV0ZcI;q0p)hX!wi}h4f%cFH{JsBpVQMl)eaf3WUTNC;coRqb9w|b^xDrfI*Zg)=iap0)B-2PevWr?zp|L{-R7Cpi> zS#WFkGS#Jql?FB0*%uM=9VZix8IW40$}MYFXL?%a&*9@Kb|ue`0zX07nt81c^bbPA zJjFbece5=(xEl?OR<=9Lc<_nZSniUrbG9i^Af5d_U~ z+g5x7vAxFGeqOIFPmaL65)x`2LwD*al~Vj)d25c`d0!JxZ}NCCIt)#4DrZrc-=LZD zTLt#S66RN&KxU=^(|lp2+N(8|A~{wT;rA60a_vj3wGDZ%8}f)Rd|mdrH*})3_#Ngb z^f$&dZx-J_uv@_U3uZ4zA~7o!_e-N~@%-iI*aTnM!JuzErnM4s^8M)VGk2t))c(%% zXFM!O4wZ^u`?kE#DXS6Ktv5&T)W1sb6Y^_?5 zgi1$&*PXLBp{TfguWE`zDTaPsA~7!0gyU}9tMS#C=u|`II;}6dzJ^Zl5Y-0aROjjB zYma@l?1$G`B_0!VHU0mma>|9&I6kbDiVO-(KAV=7K*9terct6p1;*OFpV~yLBS)^}`8&YApg*XnaB_nxnii(^s_V~Er z1ls)|p!$xxbq)XVRO(8Z!Gl%* zL)tjsJ0!I3*)Mee!q>UK8}F;AviRAgb-GjALiWWwW7g4-QFXrBsJPs>ljFp(_`MHC zJ~M?uA9Xg2?pn*7D86uYDV4MHh0@meAvOi*ZSBN$P{( zs6wPwm05kMnc+;R6;G8=>Dz(q{y^6@K|r*P&}G0WtS4=F zAa4DSoYxz}XLQZyt&-A*s+yU?Q`bKx{Qb?jzwc)(FK6*@FRYSe)3)^Bb-y^iBCKus z5~FtDACO(Im(6H?|04|lbfZBk%7Gl>y_mzzSxSLbZGivL=E+kY1chPR3v2TqAviS} z^m-T}l+E7`spP%5Au)mFTiN?&xZ7Qm3DLT|l+}h$e(WxZs{{>_S+w`HvTu(4;Jw>r zm*8uCa-YtfIH8O#u@{x+=u;Ccjnd+5c%kk4o$L2!>kQK94ArReb<%Ri_;peN7mxfb z49Ka=lIV(E7>0$Kt@QsR>n(%gh`M!Q++~2lCAd3-OK=7aGPnc}?ry=|WpIMa;O+zn z&fpf@T@xTA5czn&s(bD|=Tvuf?Y~{Udaw1!UTgB|U8HIf-G)iiiqU+8kXceC!VDo& zm!!i+f@irPGz*5VIsQ+w6J%X4QJUxa~S3ws#dg?hVk*QEZ?GsQGb+r&(`^P|c z6+*rahTBro84E1inaR__`0kmkbMDYcZLt#F@ySQrxRrk6Bnt)|+mc_MYgO7d+m?)EtRgd}{Au*mZ8nD}CDu;| zH#a1DUPZcCrzp9_TQ(Qz$EwIc&T>JQ=)0Te5qkhX#ADXtf^ZdX0 z=5>W1hTORkMmc+6zgi z6^X0`%&a2AF=-t&h$UL$hLMM2N~CKt+SA>189p)<5nWjAwwt7Xw)=H{15p)|H?B_c z#@z)dlUv!ur>)BlYqcO!@EJ=Ib!&j_?j{+9HL~|tIGae1;r#2wM^Vz*O9=HhC`*1v zL5s6cR%w1E?cK$4D5jX#)SUPy-<%Ae-aDnuWEmrWYRm<;I^?4XwUPb!rW_=zGld_I zneMmxW10rUt765q`tLpOxgJARB;O>UZd}zvvsR!(>S*8A8_&xIsZ8;2? z_{ifI2+xZnfN_7ivM=9&pEfARXXh!hrUS2&RtTN^j-LA*Wcp_Q=MVi@5MC8=BXNUXqiBL29av zh|5jYb(H+(<%lYAn>b;a&`kF|{Ci23;>a;xtVgO=2-=`~oRt|#!W&piD=mYWRP@jg z*&qzvx*S_$LZNY1;E`YaHCy%w^t_ZsF(AYW2 z3!tjAX)7m4>X=to4q}p?r|bKy*6K*@1jyC*;QUcE76`x=6<%p`T^g~^s8sK2%9T}0 z&kZ!|y_+WOK59KF<-hEM))B#R5vbi%0En4};A%YEm^a7#3#WeS52Vle)gtYmXL zHU**;pPJ%9I>ON+EqrdM6vFEh-88M1+1#;7S?F-4s#^BIqtNXERK6zC8tJ?e{Fecq zVSXdVp$)QB>4nX3G6Kr+ZXP2iDZN+y)7mF*5#-)lkF#oP%vKANRYl1qMCeLL?X}IG z+qlk|4fdTC^>?DN%t@W4?o((6rU7ZRK7lS`^`6>lgxy-~t?a+px4epTMEBO4&cW%e z>gygSZOtaFf9a2Dc~&C69&q4^6A4l%r6-gYfYHKv=j(7$BlZ-e?04MQg-A2Q{i_PH zSSoTwJ!e%=TZafuiov&idsbB7%O)9viBN(|k6p}g-<&@`; zZ8vV;1v%leyZIQUmQR00JSEJy3;6XsX7zkOlB4b4AZc2aZ!--QdMzAVFJ~jsukC7G zV&E?m&Zv`@1C`51vU=>|-ZZ|sFuaPl_K2bEL&)r7x?8e(DwAXl=9bDyGKl+{m1mSPrX$a5bGdR`E39=IZhjiY|A%qg;bofcURsGY!1eY2=D#Yz!F1yT z@+3T!Ud~QWwUV7M(a_0&BPj7Qa_1p=(4Ag*q<+ngj>ySkCvz@gXIdRG02JCfOn%Dd zbAz8rhDJfn=8)>xdR(v=$Kv<61IUKv)3Lg;uxsxC)a#jQR+}}+lp)b8LqjMaXE4L`GWT+89{2jH z(-MY7PI+DH%NO~kN_LpRBu%Fx$NR!h|Io2*;y2a1Q}ZhMd!IY;9ibs)A`%J)W#mcxvXS8C%IV^FI;lpqq)*JUbL{)vJZ{5=94WF%rQ%T3 zZx+1&LzF=uJsy});9hD2cK06TiSuj!d;HF>;oDsXOmzcCtrogJvOE=WV2yuc_+5;C z@!2H9Qw&5yhcJzlR7_DU^`j%xqgGiN+BdUmkEN+xn#g#{kjub@*i z(CPLB2x7~kpLjhvU}7Rx?%C3=!D#kzJ6-(!qGOlFu(yQ{eL{)B&H2)7B%VA7yy6Ke3~69o&7_QI zZ=X@#(CaA5cb=luh+DE|3yT|~j54e1Ix+P8=8>E!+p=mjv&UmvOd9F$gcovOa;c183FZbYQRpLD3oLLi3+n?dn?&YTk?p6j`xs z>DVdeY}Ydvn;%sAEr%fYqN^aiSsG>e4_31e!@N~#I@xEL|GbmS19Oo&5C*>DS0w~m zU8*;}(E9?>F`)s*mLwxPKSesD8UYcXDF0ug-_P#q-*2-_Z9!|1sC+awt~~bTBSBrz zVX`(jzh4;)i(K5ZJF8Y{P3xF53*k2<#|($ zz~CuJOu67~Ug|NXyPw0aVA3o)bA^!w6o~A^Q?-mCP@u6*-2>W=P?AC4GZ+ z-`m%;&Ce-}HiM4svLfMjw)Ni&)N^%3egC5#6ur{4n?(5m(CLv(inr)?dhSo*{DCT< z45spnhOEv~lM4A0W2UO!BVMHF(l16TUG6`^FMp4LCpp&+g1lv%K+ccJ*y&S4;o#yC zwquRFWP$$u<1=h}dwCeqkOvxrixqYA0x;5}>&3~MAOVpkvR{^gX%ZhmDRJ;eg9PmZu&E@QPob*OkFmHH<~TE z=lEy}O69Lz#5L!Vaa_l??yo0ggePF^%fLrosP41Qy|<85U?X!Cc=#20IhY_P=Vhns zn`f}=6)Gp|f>nhrq1%L8Au+lu@P7rKSBJf_b0*BJocYJ3!LRb3H&|}KF04LUn0qQ~ zokkpV{@mRU(12RoL(%^eYsSIcTOx(?BS%QXE9L9Q2c`FMi3UUs-^nfyo?9KW>6bh( zPDXO4g9YE}N^l-m@EUiOm)&F%SCndxP-Dj(5GqUnHcAHFh28{{EcYcp?w%y!DiCOQ z++{bz&S39%RPQ`n$fMjg#6DROB)YAVJy{iD^b^%J>lEf<*T@hrN9!jGmgKHI6x|H` zQX}KyW2v-H%QkgWTD3^^F~f1%s9F4H_7ek}Q<7C| z&Bc_6nAOUu1kZ8$cBm-JYcse0ZDsH`WfzP17rOC%_5ofwVp zX|@o&B021yI(JZzJc4IhkWb3eV(39#2#|L2a~*eg(fv2A(5XN}YEZjU;=ED)%}mg{ zwz%eR6O$f4GUIZKjihu}zVev#`rM>n-r755Z*|D*3aOI)rqVAY!Y(yo>Ym{}oE@R4 zM??4v=D?HE!wGTOTfzAA)7a&i@c$a0Ttx9a)`~~fJGT^zxiZy?0(x4r1h{`YYx{7t zs{Gl7q=xaw)ndbgUW^O$aMnW`Wfc_-7=*Oi+_?}*gsmx?f| zbosWM#_N|kzChzToF8hD3VWKO5GERh9&wJ&XbuaOVWd?Vk0gFk&N~ue4^~57o9hWk zR1I&8DuL(vw-g;V3n6M-xcd{FgMhM@)*qwxw23s4LghW!x;!ALolQC6ieC*5ZFD@R zsxInZ$&+HntYU^t=ZLQ!g^h?dLo;EelmQAb#8?n zND=^P{VegisUO>bA3#~Y{d$44F(GoqPAIo2~sCx>SUv>B{hP@GQUNegXafmmA zsDZ|}V#HEu!Jej)##6gCHX`gg^C=L#qHSQ?xA*z)bx?iO{m;gZ%r3$)5Ik*4Ep33y z?07T@h+Qb{*L;UM)U-9Fs%qEHXjjEGUV-w_@BV6ENfIs12(adj^Z6lCAbXM$td#o z%utvisn3qn-x7V5VOAqVbxmK<$|<`hHq1W0x-O9-QY*@GD`RGKW#u$TG+x8>4M~1k zRu2B;S+q4Av&f;aTY|4p;3C%+9sF%#)TX;uFlx6s_r|p> zqihKteqFW70qa(pqY0xeNm0+cNa%*w6jTVFt5a&j-=3oE(e2qugxN}Ks|KSsw_LC& znq!csM63JaeU7M_Yabssm?_$xm#HQ>$1zQX!vsd;menRCK13;Pg&v42=8<%nVgP?DLT$0PU#`v=nC7)C` z>tDZY2VHsVjF3EJEYjK>vhL+S)F%g9WR>C0CH4QtzuBiV$(b=Do(Zw^(lu1AK6?N= zux)?rtP}fiPSg;0v1Kb$hfz{0ApgV2KL-geASZ$NN=ZY4oE>qx(x2W3z@N?v0_L2vWNCHFI^#^|J6gPS zRK0RpH1d1+c!hK{A^alK7eZ_~qUKQ28Okif`$V=Sf%^G)_#U>ecYkHEyK1oF(?(b~ zY_)E@kTZ~`sY1|}x9yrSsat{Nw4wNno~nw)zf$a&BaSzZZH!pJ*%m9xB|I%nL!ucP zo(xU{uo*!*n51=~Hw0H4J#5m~uQ;lXT&{A`!w#3oGkHMv=)q@AVp>h3(fF;F+2&0{ zUiCfrY3wOw=)cqb5)uJ9S4JF7<*PNbT@r>3A-|?tx*%$!eLV%EoU{|&jF`&pl@spc zQ;pK*?$X6C$5n89%OZ!4Ik2{TaaWG@=;_`2iOl}dmhC@m z)|pU&`!VeVrbdZ^z+Bu`08S#Oa(XXOv^o3PEBsLjcSvbKsMFsQI2mE%f3KdX_Fh-G z6*i!UCGmR!)TA=j7agy(voNqeZ ztL((bbMU?udP}OQ=*}|=p7C1!UbcdVk~Y@?q0!nj4wNfec?bt9POo}r@eQ4(qabsNw{Tj1azs+m?!;#qV2zL{0_D5YKPA!eAK=AI-O~T~Fm)p^;7{(y^VjE+r_2jn7EO zyTy9+|`m1p1Jyoo=#V{022rs z;E0RxNYykRckH2@s#u&&>MBgq(6BRR z^>GBVp5ES#Q0SN))HtTeFI;OMg6qbU7#;*t5YkWEp%;24-8s^rB{{K0hsSE>q^ZHJ zm#~eBM2=PF-cjaPI^cL#T_DD6+32m0`}w80ip;5<>Us$>%~ZZ>ReZSxcVAiV^1M7> zD25DzjhYA9F-<=t+9|;pz38gR7kin5Pxf;g2cknhP;$JJt?a?SYZ|ItUd|O|D~EE8 zg5Vb+W%$Ylm(@%c$IqR*R~#f93NrzGT&JE6d>lk&tYsCix75*7!~Lv@UCE zrJf55VsWpv9vn3QD$m0nHwmGa*v;o4>AQiOu7yWcH}G&MtW^h;>YKvCVRs|PCoL%3 z#&|mOg+*tTi-eH@1izcdUfv?~gg0UGY?xs9I>X|)85HT#gV5yc6=Uk;eT)$@4XE_K z1`P$~a;3RB1dg?;Thr&`D|~NP;^inj-invc zpQG_sdJ~GK@GHN1MkQHxrgHZ?N+qLPJw3%cykHMWk^i=zsz$uFF)X++1e zJ%}Pz7w$|o!5I2Pz_#qEi_z;8N;>S(4I6bC7(Py{N1`6AxXJTQo*HZ$8DHL7u)c`2 ze9?K(wtAvjn9ZyG;1pp^JiKBX#?-cpDriP`fHJex~p1VJ40lpM~4w5&tIl`;>;{~J&h>tilyEKV=o7sJ3Vw$Qft-m{A^I-yiuQPSLb7#r`PeQ1k`C7T-ByPW!$<67jrOTQ>e z*WeG9rE!myt#O;OveYAt+Acr1x_V4f<{jwxdQ_k>`#jLrDUBL|-uiN#cZF+FS(npY zLpugVE5<8p;YD{Hl~NrLRf7tOPDB7&w}CV!8=s{hXKof@Qv1&0ra3y2(_-_OE(GsO zqNt$*fy4qP_f#Z(s0!z7=DCX^etHW}TvrqQRvXjTFEk2~Dr&35^;K^Yo3iF5j#ajh z+(u6&d;Qs)sUnpVEpC}J$CkDjbw7$=<-5BC;ar}KhYPEim-i;tn_E^_JJ?RQ<%Bb8 z({E)MYV$`O^d)CZE~=X8e+7DPgC9`R>nX?^KeR4adR!?P`=MMuGzf?(3eu<5T!gyz zcKLmlk=Zu&`1{fwtvacoT?r1=kfvXK#Nz_yu5fb*>X7Gql}OOYwFoWLzQHgz#mg~g z)85r*9zYAqfk?!q*7&J#R{R6e;^%aC(9-0yb-jH>zyIZpd|m97G>^myH-ho4T`XIe zZga_psmUK#Ot#%?GI?X>M>a4W+e9418jZhw$qzIo_+I1G4$!I{(fNZxm_@f>QjmYp z#KMea)F5h|(udrY7&*lZ(sz2?&>!YsQ4y#Q$CeRR5(-F+bhFa?r`jjeubB&l3Gj`6aHeT+wVyT{JpV%p zH+pvNt^y%npt!J3exmIb`@Z!y9kvoRXNH%3@8kpKC3*V;>io_!*-+}KBT^vX^r z@F~KHE3t;(fE_P(^2ioy@#?HsE<@}udY^(hZ`F+4G4Lr;_m4w<2Ac{!MZSfR^ZUk^ z4ZUyVb)0^M6v6*&jI=DBg%^ccQvAo-q|P!BKobFZnoVh*7FebH?jD0@%m%9Xz13j- z)w|XIRbZva+2C)w-d)t0*Yl$A(~movar4ZeZ!goSn*9FTc(b{ey!Au^dS{}e%_%ce zx8u--FxiL3zor67Tdw-`E_nyFKgAoJb8Q;~+kA9IkQ_w;HNW$zsA7GE+<)0#1JR_f zt1C@#ju^ucVuR8h!3%I7r(=Ue6c#8xsq9J{(~u;oUb?72RyYwLAz}r~?SJg({w3A# zGnoK~4j)E%M7j+3$&QUy=lfT5m2nuhP$S(j2;`Y)4Y;b|fY&2*6xk-SpLHGw2(a&6 zzP+nrf}V8ByRDoy*p;z=N~mVS_qco9 zbWtjoR9Qcks$CtQ17304Xt8_GhRzE=t`! z(T~o+w<8g6ccknjVZnkYhaVp`IIPhycD??&{TAE_fHRIrmQ;KT7sq3Mvc#hD0Hp7| zn0omgy6#GwH>KP--|$GukN$jGxqL3s6irW!_H8fIT}V5}(#d02pchUUY+B2hk^8-}>$@-bq+{)<=D{RH?^k|PCkIgP ze7OF6-tiFC_8a@bw)*c!8!V{rb!Y!7t>YW1#mao>wpz?fRN%R^vtOSS+@Q}O@kdP# zjkn~rC%W(gzS$u=`F%R+QgG^*eyoYTRF27##;GlDKKdhlcc5Cevtt^n`%B;Yy~~Oz`*mSO zo6wk%Cd=n|9z}CLYLnT>J#QCc@v@LN#$~Xfr!nP5y~(rO4Y!g80O{GJF=D%;tD~_v zFyehGq_266W~MPeOnalJoBwa14n`+TX;pGw2#tN(tNPXf+w$&Rv%v%IqBHq@7H`7H z=uWPEdznYcvW!{2p$Uzubw0L{n92&HSN`6jk=CQUuI$yl|5D!MQgu| z9?AQiNe}AA$E%{!L{?Bp9&$9e5Pu9-gI736f0kugFItN(dnvjQ!T(n#t#qf`CMFAk z#AoLapx*3{zL$70>aI>Exinr1t_emM0X?b zkeN0|U~C*h4?n}>u|Nm1Z8(VaN`bNBHNz`moKaN!i94VE3Jc*J80&AOp8V7O^vz6k zEob9KX3#H=oIp}DGd)`tIc>yNtRJkvu31y=ZF3tBIjZ0$Sz$3Fja7Wu;ca7mh9s0t zGDQ?r6Af+8n2u&)Z$qfOviC57Te|T_Hx${ruwt!uhis%-De zc~R&gkPU`0x!!Z{WYA(|@ThvnZBg}R1VsU1LO5dp1Eb~Sv_g@t$`J@ELHJMu^mlLE zEK$XPQ5O8;IFrWf_;A-;=WF*=Psoc}(MEpFssJr06-A6o4XlwbeCpKI(L5S`SpU-^rqgx6a-}`8$p7&I*mYKVMsZI?&5m_%^#dcq1f&)Ce-jdYu7+Fumb47G zP+4H0!5g?9s~U}X_4ogG?g8VPiIs>pnn&8QLf>RG%S6hmGQjltB1g|#VP-!9`-98V zXxrBd8uH+Rw|%{V6#_)*CT&^bi>9s9Sn*-v@8Uc}=t}uX1p4fXR(|WPbhm#Wr?$UG zUJPK>G{sqq)HH*{+h)wLN1{ygdJUyc)%mZX4RT(iE;K$6l8Wt<^;85}k;XT;PYy6w z+jf`abValQ8883nocKX9m4Y2-=>GY2)@?ZZ~3!D@${?R-CE`w~X-p3X$%& z5~TqUb;B-+9%Yg6hFHfll48+g{hKAB=!i@#*RotC3n9^SLE$`A8suWTBJmpX-zX&Z zV+3s!jYwgtu4>Qev?CAS2G8WSM);4)1xKhdlId;CmyE>)Pr`TMvcaPCp}3s>1QVxUB2hIlo&UE4ZIxG%u;{m3{6_P3MM8ioEHbg zfFl((s>XZS*7&FDWT|OxVdTc9kE{8cf8RE1)ejB&*UdWTHgq&WYHFp zB%mdJOod=ZlyU}9rA=OOQdE~`h3ZRj?ODK1RVBT`B??vcd$WXC09H-*>)vmwF7!c) z^zf>f0B<23%pZQ<&rg&!VZUSW-k+E>=0IyBEP56a^^)qWxNKqGx|E2*=xfw%htr>) zsjyS?41M%+M&^<7-@>hb>Jv*SBRAvmBar=dWi#$^<9YH+)LxP3i>+8hB@#@d0eEfu z$#-D$SKpG$sZEuC)po;qyoxDvo~~1Hl1;8b!?)-$4ZUHjr!u=fJ}?*jVPIzXuPp>=sjvqOJv*#JMeW&E zp9=0+hd;%Y*cSKSPkHu0Y> zVUJY)M#1erR?t+^gX|}UKS-9_HQ`Pvk)@CKy3IYLFWyOVReyL5pkIPLW_9D8PHk;R zojYvV>@@hvM!dHOr|JmDc8y8w>TkX`Z-&?YuK-l?92ue$s**;mfq-r``nVWeQJ;gAOLIPFP37)E2oV1sbE@mWlNKyaPgvyiZk4$x?tsl%q z7(96i`>9DkpDhn&5e3AgNj-@*(h|&vPYrubv&a=Rst(Lrd)k+Ml%_Rm5-`5dfM8OK zJ&OgCBI5StL?;c_)`aw{Z@Y30d%M5$-6u9wnr=0YuiXhQcP$my^@T#%iqp7B+)v9f zxjz);;4~F`)}C4K*mJWg^e(nDaOgChYDki_{W2r!(R+Ct$abR8Tlnc6wAB7}kM_Zn zPFKe}evSW?1#Th>eS6sUtZGhF;TFMzbH=QlvWvfj*+!OXziMpX;G`@IZfgV@aE^0^ ziTW4Uu~}b7Kitem7h<{*9=ie-ad^nr{$<273h&EVuu~AA7MEovjVMWk5u5Bk227WM-fnN3#tfpBd+OFQ3|8rE!})Qiv*^#3`^r8{|m1Z8GFVP-hn|gG)Lw z@t5gocZ>vo{w9*;IQYNROVxO~t_TGUk>E~b@FL?J(Y7HLX^fLy70dU*SEdS^J_TvtX<4y{sarV7he7v zXz@)*nVv(Ad^qPMOONkVZGv%*T+bj;u*b;lNn6vFr$TJ+nan^i$bI0=I~J3~pp<=; z`E^5wLHh36TXS97bUQ^&K{`t9VLT@PbW^d8|H|96r=Kc5QfweKipxw-9gUGcK6yV z}iS0Z8PcKF4s#pStxGfn1h$Y=pB5KxLL5`xs27F+Nnu1)!i(v zU_i*qi~HPqVEr$v+u+%O)19Xa-Ic({n_$=Ai}RKK)1tte^_POyx!}v*&yE#oP4jlD z_6|a`|M;dn8Q7UiRTdpqR2Xs7*lSv1MbIl{3ss(7#Pu2CGk?jR-Lx*f^{rJ7DLG0& zk{^P>QmrYG5K$@&LWvlOqj;r&c71fV#ITv)TSaJxABI=)P`hUgBUEazl~HzTYdylJ zqJK;05$L|oHC1xgD8WW)LbE_~=S;1H0eE&)x53+Xrm8xxm#ON~$nLLROC224Yi_4k zGik4|diR0}yKZZ~DE_p}a!f%$=garX?jwh(Yd8vV=%FDDWijVoJ0l8@NcUo_(DT<# zichjeZCIso`P_QM(iZb4La<8Fxi%y9wMOniv`2}gP&|UHJ~b8kz-v_ zC@?LXK1+v`T^mDQ`gM{#{deg$h%OQ)yiPMzM^g%>vj{zKoaT!bs#*uexl9LIshIOA zF9T5Hus{H5TEbA;IxI|WU9B$H-BQ9mg;CYJLd8qZ{^kZO(~xlm*Gtjjv*hepZE1wq z5YQi0&d_=XuEn>ITih=-6B<)`9B{6w7RvdDoQDU-JH z$uJBFwMzszB9U6U7&+#+WG9&(P(A~`NJ~`QSl+O%YL6_yF&u~dcK4hl@j?y-x+$sI z=eSsCm7+M(O4Xc7YytRxj) z$^Zm|fY?|OLUbg8Dp%Bc=sNNKy|d|}gbg}4hh#SafsRfZf)sn8S?pY|qGiL( zjV8C?ayK`HqHVNhSLL#DMoxy+cABQGTLD-`C9c|WT&Zqsb3btK(AWaAW?SeWm}4GL zSwh$R=#EDAw33z}?!# zD<{(!Xk(jlsdFXib7X*=Of9`q3{wIYU97gAW4-n42Svvi%hG>b{%%h~m^yaCu$ZSXmNQ{*rFGBeVQA_rR?i^g!;>XmA&;%vc zQ+X?*4?kUK*Of{M5ir8!puD;f7^8j3aPR@7PKn4f5A`Q#w;Ou~UAa47e&gE726;_d za5m^&8Utb&2n7MjMp{8RQ=XMENv}qeRZxjBSo#ub=Xu;d+G$HZgGnP8$Sm)HNJ6V{ zq$`>#Vu_dB3#d4%_G0hLv>kE;5GStD7C1wCRgF>4L4d(=okCrO%&&BjJDSCGAKGzc z!j-&IkBDfKiR-=1Yd5>G3N^XdztL6HnYyFuBQnCw=`1Z}ITgsiG!j}mw5(#L2?mB0 z=htb5tU{M2Qx}+t!3GK=ftJy=dsGe@D%NeY_OzJ%WMa2_RIy#uSh9dOjDF4z&JN^B zvZgVsl1s}J6h9Rs<`^PeU*xM`dd9L<21j-wz|-RM=;}}~#;@oJ!@PJR38PjB)T+23^wvH{^;(o&(64pgEn zaC0nqiYkFo`L31FLb+pByd3G(&Tx=fFxMkk(R=m0EWZO0 zBYYc+(OLQ78-+wr*_W_L>FYJ4$%@$A;syRUt(zyV*oQ>hW6z#fG9Bm+c~hzRRO&yp zCQ|+$cBDHpl%zqLc~)HDwqDXl2fL8QNfByk<_0@5_?k zXb1r)7Q<>1b2(BU{;TO=!P`mEWrYeIj0jM5dG0t{S~ixJbgUrDM)#y>Rt5%y@s|RU zd~aVesX(loMjO$Q6~46%5C|_R0cA#hIpkS;CNc)sl0Be&~lhtEE+rC4>{) z2StBHr5j)`HD|^fNgO<;>|K8vA}s{;EXk>Q?!Zf&^*a6F3O$8z3y0aXnXkxvryoK- z)2rxcn<3GiKS=s}ydH(3!kUlE)Fj44(R9p5hGc;N-FA6wJdtE}8a3_;+HviAwJ!ZS z8J|KH&e8e{iZVg#5g-jgbCTP6&Eqvbj3|0%8h1^E+agm#?oRbM(03sv~oNF_jodE}_FZt`8ehpEPTSWtT> z(S{{dpEyp)@taI=DlMT0+8*m{yJxVrL%g4>Gb+U181&=OLvD_qt1;Ybj9(rQ>?M6! z8x=ctt1#+oh3h{u9Vd66ee_TzBm8}CPt%S2zeOUCSndcKA5E2Eu`CExB+3IvmLTh6 z*y#cJMs#U8kg*LX0q*1_Rk|@7ll)DD6&yY?rKV9*S0|6Mf7B3rt2*R+zOnN|E}f~z z;7F~b?@hUC_a-1P)FV>41jjbjZpuAxx3KJoGfAb1FcVw507ZP#gY`yfGSE^{PPyhY zILmMl51zM0C<>{MUE0@e@hnxf|H>oSmX##Q(eKx6dx^6#-$SU%E+y}bf2424$o(YE z-2*+I+(H(6JhY7zqQb1kVpFO~Pp3yDjN-Lq$;7hZBXgjPaw76ShIDTGb?M@A+UxSo zkch?GVl)#5cIAxYLCJk zzXcw?__zm_@hlIOL5Dp*U1LOOTNWkKB0Jjdlw$Zin39lp0pZLRbx% z90(C%p%E}7=FHLI-`u4)3Ht~k;0T@DEK2)oYVr*HMRW>3A8ve9wRQEv^bjS!0sC_0UtAS5 z%G+Qa)%;y;`9I~JE>>C768D@m*?{HI)nf|3kZSd^h%mo7EgDA(o2nr;;k$D1sWAkJ1`Gl{87r|Dx2NNT_fItZuQ6g2Ff>e6`6%)dvr@7^EcYEdzvbJFjMGqLc|H8 z<`81(1_iq4N8yvW+c6o0>w;sesN8W|mzf7mHzBo80zyR$EEjW<&t>SJdw+gy&JytM zvDor5Un{%)__%tL^MCrR=8ybjG#v4xB4gyUtu2Sio9cb}NmPZVnuoSCDX>x5N(2_2 z^!+g*755ipF*yV!`x`58+)Yo?CK+04Zv23KV&sTHM{3I9a641yT9>49YHcf>ta92` zfW^d%8J6>vKdymhnR(MurYCQuV!tJ?ZLI zsVi*zS1Of&5xi0<;4rax$%KJIGIJ7ws39i+c9hDpbTr-H?@{ds%bgV?I)lagK zS^?N`Fm_cGscWnePUv=d1^4IUReaP6 z+QRGfZw^Xr^L!kEbSmW3S%~G$lCeJr)wxze3m7!{zgCEI05L6p%&JS?n7v7ij5ZEM*GuldP5 zdJtF+M?p+;5Pw%iRCB*mRa*#pcX0l&@mt&vH)-VEQxeGk-NvVm@UnMHQZ1=*5~o%~ zkR4r@Sbttd07@7Hl9LmMWTYCLPbRbr=ygS++s4{tb{ZRaAM~H-%Xvp7^>^WOP;=I? z5si393)9Pgy2k(K9#dfYQ^*O3j`v6eV@1jU1w=;q1w^sU5(&U9vC6Rv_EGeabuKgM zc+Enhj08+#(wT^ek+I-HnEVTEbs=pcRbs-g%i`{{JfGHx527UNWl1V!S!YOKHVO>O z)Z}mvPS6s`&SFP@AuBQVt^Hd@Np!mgDW+0+6jrtx>5MQWdEoSXTXG!GU6C?OJzVK3 zhQ9L@Ds*L{ne=EwivUpu3vM_FotjsAit5)k`Xt$9En#ZvzQ%=koUO6ybpbY z(o&n@VB1Y%`>D=bb}=yvVI{TF-k+p=vCW@pftIh6Mc=Hj3`Xw*Zg@UpOR$*?QrZ6s zy2#(7tf#bBT=cVdcueZ}#dWXQ{Y_A1$p=yfh7No}At250gyA1ZqVuTBJz_M_uW3rs zj~~J2m$lqfP-EDbBx1w}S5{@I(L;yDqBG-5at1S>QoA_9Qt87NJiZexx z(va{$|A@nYl-(UnhVMo+nQ%=(ByH@J0|AE+wOAg&C4hBwt6=z1ubTeI>&YK9LI3A! z8!(5xblO4rNhIiZ+NoZVl+^Q({lDM#yWG2Dfj1vQ|R3Cq7J}StjX~ywQ;xe4oc~>>wb8AtokYB=h4f6 zN1aLOn~&h?vJgWq&!--Hqf5MS$xgYx8(*xc6o*NaU1)N`Wruclu2#>|Gl0MEd|ai z?7p-YL!6>9fbSgyMACd5J1oNaX?5id0u;jx|2PH>O8}#Y>GV+9h)_`pz}ojqo1jN& zg7j%5;+~M46}@*Q+q$xZ)nH)emh7^FkTXj;wDNpQlzG$N^1D56uc@X9>e&nlI)rgu zQC9}5f-0H;VELzkoBO>PhUAjLI3-%~U7`rpzG|U=A@(^v_sBI9r58{){X0Xg64D%e@j6lMNNqyc#e{FBj?nz$+cP zG?$(w5clA|^h78&44e3F96zo4Pzjm@(pyOEm-vu^*WMbhXdUA=ce2n&c?N9YHVa0y zF__g(dDZ8CDM6@jrUAqqO1BwRB@-rdJjEcTttP)+N4ARzC%+1VXWl9h3R1AmhB3m` z`sqjrP4%2v=E4kV-*`#uM#g6N|9@0{1z1$w_cdKgm(n2J-5t`+fJg~QmxOe8cS?76 zBPHEk(mgcN4d3OB@Av*6b$Ha7x%Zs2_u6Z(z0W;AMCA^tA%vAEl;4U|RNZ{$P)_?` zT$P*VKw_aR;#X&ee_^U5yT!4s9ck6oU4y4-)6ZDMvrp}4%@#uA?&n(BgO|pcpvF<1 zTaqKf@LggIT|*q3O?nCp;f=kiE#o;HPCbiBW7l$co~qB;OzMG)fxNg|H-n~9-&_z< z&r?ri#!3)HPT6RQSKfA8P(C%UD`Pm_!HJq~UKS*imJbsVC>r82d7+WJ>85<-Z3i=O z(__Zf^ZqC0T-3h(QqYb2-2!gM)0Xts=k2@c=eub^T7rIsyU~eIBK{U`$<5`bsLcBn zo7SelQl|FH2{u{4%FYc!weY(#`Q6Wt=&bO9q8JtEHF@VFEKi|fvOolen4TA<<7=)f z`NS<&YgY3*4YNOAk4SkJX#3n#B$KqE1c^Zb1r{dHj*##f+WQU`O_2`7%M9~<6bVCrIjL=R6`61kSc z8a-{8MwJ+|q`E9oR2FPF2KM4@4>$-SyW&D2eOF)1*q{3W-;xa>Roz>p#G0>52EkKl z*skJD5Ej3%Zdq#O_GNfBsdfqKT8RDuUTTs> zDnuwFkTL~&a3QnO=^&3m!4RZePk9F=o&|fbOs*h~BNASYNRNf79q&)IblWn#-$&XX z;?k$@wsJlOa6@2VbM<7UYAtvsJaBjw?BArSOgr{ zEOCZC2L94NX33jjL1%caoVvdAMI-rX5h5Pmft(*M3xLNiSy`EFLG2`WB`wgvUjczR zRYqGvUhMm*Hllrom-jW^{5G`>ynVzymat4&>7Ko9Mv{5SuL2R3Nj_pWuerq;7H}bF zeYm-qc`<&mq)JrJ{K`lmNwju_A}k)K+_1KZq{5wbS}MVtZ+)kI!e?!!p4P#*fq;$q zJP-?AZFJmj#9#u%udm}lT@UlVbNu)zBGY4^(&dWLKf}p9)pi`3DSmD+Y=jDhIP>w6 zt$z8U^9`}v#qeBn%&~`efn7nOm|{B5G} zx^oiuehC)M{4@@lK!LTl#U=-_nwRFlAW4JJVEc_GThO6?LTJPo8c52=tTO05mz0o_ zh~LP?E!!1Gt4LkPyem1uAlh9?78%MWIZ#3By;Wj9j#PT5eLnSG2zd_3nx_&Je#qFy zeDuREH>6IU-ZgZmhVZ^Zfi)EHP9a9}rtT4D&u^HGPL$A83GfxeR)Y!>UiBelQBB=v zvHEampM#()HV{B1cI{r-h4XJOz`QH62)VFd!|cG~jkQdzI4lHuvC2>n9W55rkH97} z$4y#0#wH}jIkjBvq$uqvLJ3+@WxKaVP;vGoWXAjF0)@IGf{ST8pL|1uQPG&HXVPx6 zi;dW2%$H5c}bJm5VlAn|M=&(*ZWN zK5|ASaTKK+Cy@aEyxTE6Wq1yYu+EK(%VZrkO&Ufp(yd%Z9mHwKnErCW?@CxSV#Mn(y<-yz*|iI1WzUIzw;q#zx8U3iJfp;C3=qL1AL1_MjJ_(WRd zy@=7O6u_z`F-TJ!99%;M?u;Bj&t;e@xXS_$^Vf-qj2tGLE0zc^Ws@Lz^Ui(1UFO-J zkx$K`PlKlx@pYxV9x5BbSh1@)3o>?pVhxg^;wv02t5B~)0a!Hl>MRwMbUakY^sgc0sHC9~5 zHg3;l(~?ldJmUhn|+lq&Z7& z^MOB0x}8A;*3lsoeS>#1vS_&zb+W5D)(@E7R(K>^s3kjUYKkJ!Aaw}Z=raybw#aW~ zDI;(&!k}C+&u_ERaTkPGaVn8naF=YX-S3d?O))mhQremO0cY37x+zX#q&}NqOeg;` z@4fA=R@jkiP!*@iDP-Asn`s5Fz8!UN7zN`S+kRLXJXLXCDJ4g86mjuMF&w713ixUu zdCaz7K5QAx=(+RY$|x;ehhp}yZf^$r;JV)T{A%kKa(VEP z#(t%oH6FwJlfBGKqsH&c*B%Zr+bD={o7*gI9|2JxrB0$%02x-pLDShJgIO7qOc2pdzob&U5l`C~V=I=yKGg-+rbkCj1 zWwZyMN-y33RI0ve;NA3ojS@C=*vR*S^Ssq zj6Z>RLW+1u$R~CfG=y@&d3+=_P_(vQ329Q=+hX#i-uFm#b)AmAA8NnMIg>)Mp@$hV zW4`Mt9w5t7mIzlx7X5-A8>%fa7GvKNnfxnF$Um6Zgd%Gt+#gL`l$H|Q#-2OD{y8d7 zH)fJL{3Gk&+ZuD=pn~YB)h`=IUVw&pJxZjf?R8tc5o+asvb`j7IPKa z_0M=;mZAVGp1q(gFMon)I&K+8d0G6P+Ny`&C-8C?P(JE#Ur(i&q_}p)rV)R?JfFee z=4fN{0L`AjXWjZ3iO-ZNr{-3W_;Or2E_DB_VYAWx_xU-N{Fpu^Ru}VEt~5(Oq51U9 zFRJ5QA^3o21ymCW>$M#wj&P)hDUc`^+Y5Ump%>R6VaCB_rr8IJ+%+62H5BH-SASNi zo5>e$y>4Z-UomqOTi)R68E_;*$`Qv^OGySnOG!y-#&iD|3I0UmjRx;e{)g)269JN) zOEbw`wQwj@M0Mn1f#c^Tf*v2qY+qp+5r}hTb;FtJ0XqA&;cE43d$73bmRPHC`xsfHd)_{cGBo*pk>X8NXJ8k{eT2Ce=okn-Fb2$wA6{ z?5jacu5lv16vW2id2*E9dD(ndK-IVGx?*GzX3v)vH=NzL&Eq0Kjs(brxFcpuv_l>J&P{B>>G?%15(U)vlSiV=fBhhFUaUMBcGp(?Eu zEfzDDmZN$NQJX&{T)i$C zp8Ipxc5$&Lbh*6`P*A(${F_zVs{pCkrH^C$b~(?l>m~R!Z{xfVWa~#{r`<0Xd!Nil zmvq0s_tvWXSvRtLo8O@)o0Xon>wx`@{IPfUC zN6a?B&p{{8wdkTA)$MkVK|=67vnTVz7gTWKZbw^CP1X;GV7-l*Sv5^og~a5hyRova zEo!1s*)OSXdQ3J^aLZ)HT~LC+M{?{`Vg{9KfDEEZknHf{4QQocxshRwBE=+J2I;a4 z1E!VQ*{}CgCn+KD%59NILhWU|ov}&KiqIkW4_48fbPdo2Cf8@-kwoC3iP#U|w(*%u z?bSd%Z(*wsOVIk*JF;$di=;5s@DN1~KYDzSFCV22|B*IblW0ty`}qfUNH5rca%4TX zsxWpX$f~2>qn^U#2XY{6!MGJ23b79VgUoc(%bBXzA10=?9X7l^Y?hyO8nBYv4DkK`mm^bJCvMmK zYc0>MPFt8kQC@%MJit%azc)@Ee$E6pu2nq%k>`B#&^{;wPuBVl&|_dabt-?>%z#lM zs5<%SmUbb^<8IoYhrC|Oy=Xa3XDej>e?LNZvhuwM$S3xrMQ|*Ju(;>|c2nQhxrF1- zlV8au)p8C$X~vB0~OU% z4(Bu7RLW;{y&z?^UGw+lamE*8f4p1XxAB;js18@(Rq#s%GomQHP7whFE9{Z7lCUZ?f+VW@g=_Ywwp z+P7_b=EJz|?9GeE!Knffk8qmbQsKW9Bm&B6`{G9+IP+4i0+Hb|(fxiS4nVrU<<(TFm;}?HK*Tb5N9PNDK*k{S z!jsE@zF0;4fuUghL3?gWJj?f8MWpnqXfzsd%$*@7{+ z^RBwv?Mo^$Y5i#?f{-q!{p{j;JJ+XU8=94DUu&k#G4LwuP&Q4VmR$@}ukjf{`b6Jze zDV5r~J&nZ2^H{ymQigyXqmG&xdZxe~ijJneq|L70%f5Vyj@G>-oC`_%!XNgz%0(6c z8I~{M$MuC9#D1_NPJH52HCc3QK^lVvQkzsgFR?Y<>sRHEPw3_8O*}iw3E?VG^#FK{ zyfH36kiK$UCR}|3*5%}W%D!#L<(TSP#o>8OuWcc2&DEx-F2|_U_S@;7YkbEf*~qja zo4c)C`*JTJYMjM+dE|K9XUyw0Q}B?!9EDVOJ7{Ist?*{xT&1q*%Y40r1n} z&L)X~wx!;Sf;&dS+!EGH`8gma@)NZ>Q7hV1DQ-eMff3tZ)@Vb$%an(nY4~*tk%q(c z%~6#UROJY z`Rg~u9elqF2;489Dy@@lkjD1=_1Nyvfw+76q9{bo(eN6N4LknSG%NxBB=iR?M9oI_ ze8QzmPnUSL=mxAuT8X{KUV{f9b|vtkMYz5^YrR}nuKk&Ep})MUY6?SOAwU)6W%l{cUI+iPB5 zS#s-st#N8-=`PIX`a;#YCO6TAvwI+iG2!EBCzW8vVDo14di z_4D^Ffg)1^`jMchJ4=zG|vYhq%Vf$Fo8c9WO zG&Elsw6g<0=gRiEK3RmtnL*ll_JcO|djG6JPF!|`96incuL=zKMk5e>^FmI*S|mPX zwB5wS$DhhE0OPo5827Y!8pJU1XKFpfZq#-&v)M@beB!aaef2Uw{hGa_R^(Ez064PL z_}KGW?(1KDtjYLh2SmeH>M5hO1c1HE!-e4U=vEtjaFEw=iwdSdlhbXzlb&kBUbP`z zzQ@69TmaUU>|~r_Uibd$X9Z6o;VVP+JhggoV{E&nc}26NmlIWR5{W}8bzAJuphqv8 z0B-no`>5xlnP}@a>m&cxt-fJ5s#!46)1-OoqA~p5<3cU-++$%>l)Bd6%E4dgzZ;H4 znMekyd#1W*^tOAU!ojh^OiX0IDZM^Biy6uotf!=mteg4~52z!!lIQZeEGBAn4cUGA zd_6p@?=r~5bf3^$)%uDeQ#pFPTN*e|=PI?WGxQ7`vSFi}0KjY~;XD~QjHR0;;->CM zbkyy|rCaw_(!YXHE5Ip%ZU$9Z0gSayt^@olT|dQ5?U?t@yRK6ZRdH9%e89;G3ZZf) z<}NQ@_rDU4#sj|H&Q}~7!CJm*VO*XEDUF`jzw5E}n3V?*Kb!2qFw9=H(7rG~eh0$yorJmUB%RR&;(r28>%91{b%Uc8gIS9Z< zV!ke-6{GxG(G5oHJsO=8rELZE3RfcWLh^gZBEcKL`vI~c=dph`?MWo|qUUzQk$JxZ z{ct0v_{nvzn2qb{m(!f-yyo}xVuQivDSOS8XH_?K)p_fmCpG8!UfiA{JC~HrfC&*l z=;?U?n3H^ea8dXa)c)M9^>Vuhn|QXQ2IQt{nvN#`TvujSS<|+an%zWG-E1f6vK5RI z0&&&qa2XDM;D5(#hWEH2SW*tW@8jHh^uPZ79x1dK&hYAw8pn@W(FwF3c*F?Kh`D<9 z4`w;sxS$pzO1j%^#`aF={;UYQN2-5FWwQ;d)eRu z>htwZnF6HSev-H^8_rLED=7=rJJTQb5lPn`l#BKU_EG~Sy1?Y`1hLBTWSq`c94Mqsl)it`u8G!l1fg8QMVIajkDk8=a3uu z0&h%4W(+|R8gL&hW$3C1L0eLiQw^gN3qer?oLvHgtSjmr8kpR#q6Q@n;$2EZv|}4X znFTknWCFSbYKQF_@zb%8$HtNnBVC&Wioo~X@=OoM67g4%lk|E}4AOAINhIK#T|0NZ z{dN1?dP>*7dXr!Xu1-hZ1_)3GU<*P|e+GrzHfOR6$1ZgRREKr+Tp24Gu-zJIoq*Hv zEKyHRNWC8Q9(!E2@0O|C{~T9;O#r57-{VT?Wdyt9LW8M65w$Xxo^1CT!is+WRBPct z=RejF(Y@Ils6VpBjdqhleZpHWPf|pU)l-HREs9+>ltJ#!lGK7!?lO;J$Q1Y!P z78vMrDyFWV0OwE^(vk|pkYPZv(>g+-gM7>{M|K)h?X3kDvhNtY?AAUT^eO7p8>#dM zegmQpAhy2vy*vd*weFV;h9|wkdIr-CsM?LldE<2rPhUnuu7d5sqtm$21$L@z@7r&# zG(3-2=G`wm<`ojp!jLB9c9h)@O)?o!r)^#yL0Ux$D=)XjDsZbEg`cfVJg;_>;uSN6 zj$h~NyXTNQVb+^Mr+iB(N6r5c?$?2HhU(_X*46Kk|5(C926n}-6;<}WYKwT%BW{|N z14B|DbCsgL8jXGhA730Kb}lGZsaN^F@cNGw2KGc_eIsE@ZSmlZOU z7MG7n^>8fmI%zm${JGy*QT%!4SzuOu_H6N+;Pq~j$I&R0+jvsc%ELf1n%(IKDzBYg zy2T^k8~#6{W|ro^|9?Rj2q=&H##3z$6>`Qa+Cpo$C!5AvJ+I)fRPpoZxnI%amdW$e zfq;qmU1#RY7XcR$Ca9O)((xqt&9Qmi>iWV%kFCPFi$Vd9+mP?=H{%)HSE~SddmjH0 z2JNu13{2K)!g-*EZVia<-`0e`ec_{|N$LkH9S3pg96w*FklJZvE?*$q}77~S! z&bRF1Zw=58kcKcXCE^kAh^5@4->gV;&LWCWZ>gD}1e-DVu-J}?xBNB^I&svkPQht-oM(Er6c}Zbs{cMLIutck}ugQ~_>*$Wa#@8u3r;TOL z?vp1sxI=9htKls;yZ(rz7gVOTSw5~;uNeq&ePb`x@c6uzMxE(-wj}ht>FhcR19A zn&en(N&hSom3|UZpe2$W0n0?GZ5_b zIu3tCd0hbKFewOmEuIpcm(T0mXD?mXSJWK$yfTkU0hgA`H-aASSj3ONF%1nm3q9{T zcS9iIZ%}0AjZ|RM&;gF_36Q(CMQ7Y!om@_@l_%WI5PsF+SK{#c74{xmoPxqYC=JTs z*I{f1=xC$4$^W9=t3@YAnrnrjOiCqbI|w_>Awr7h;pKCasX)eHAS{R2`%6p9*GKQ= zpe5C}oQN>0CiU=SWlm4kwWOGUfkgstz&bw6^tyS!lS1{vCGois7+5N8^0DSSy4))O zk5g|naR=P=yo?l|Stwop{0!5o4>)%976h2;o^%FKXid#q;`r|kg}qk<%rdThPoK=q<#c4~VJ%txW4u^< zb$VO|g}6zBhb=5ZjVBc1Gw{x8orPi7jV=1X1keLI~ zW4ybr1;|2P6UowZXCPuOBu47~AAeuSsQbt(Uak zL@n#it;-CPduoCSHp+j-LH>~cJR6KlGlJ3lo)9Hl3^~8 zLi_T&l#y=Srzu_CcM%SLuhj);tyrU@X!26L@_O##p?YEe?|i=9BAYZf6gQn^1Bqj5 zm(KPcIAke&7yIm$^QgTnOxJ97=B1aGnlqfD&ZY6HwDJQJBJcGlolnoLw{p3)WE}9B z`;&)Ex92)0+*Mwyox-Sjz@7x^Pl11bt&|}DUIIi__m_;4<+!!Sg9{Da&6B3H^VwB9 zfO(}64N|Q&bZFm>jeEUJ#1)Ru*bUbwRY+!7@(9GDdOQVXzElc%&W~$X2O?aCboUEg z{7DY1a~oh&p|yGXIXMmHbbsk!W~7uC$fe|Ia&iVfoY?o1$^D`K_-q9jJbx1);6HC~ z#I?~m`~?#2?)Yjmf2mdfu3q@@c#~3)XzAf47cB+HD29%!lC_0$XYkM4RoBf6 z|EmY3U2g!115jPM12j74X&)URwmNsyF@+3!)e&U4R|~p3*w(Z;wO@zY-v>))2(7rg zYODO%rn*69vSaOj<NleWidrqMs`W`BcS-yAcjXc)9< zdX(QpDbLb%b=e>-VtruV*Q$TNe7ykxYP#s+YaA_qYyW`kHstOHSclEy3>$z_H}rp| z`w!K5_FOW?Qc+i1PJwj)2>W;F{pJa&UWW}r*5k4?iU3cd`q@B8OW{db99#XX`H79w zUrjM6yylv0HhMPdgR|T~#-LkD)%@^XL-(u432~$o)d-MXwRkqpB|Sf@-8lfB@f5Uf zXyf^t>g8VCCHT~BdwY9{=^T>RF6V>?g$lr@ii^3Cv6iz$0HBCpaE0_Y)BdW({0r%p ziVj`a^Ty5WgH}|(wA!HiB#dsNh0Y5~hemveJ-6DWL4YH))Lpr6%WLR#*y!h8Z&;kR z?*jZ;kx@uhmR+HobgXyQRaSv9C}Ocu+M2iO@X$%8)@5l;)YbQ7R70=k`Q}<}>N{m4 z{ai<#yX)=VoovU_5yT2$A|0Od9F${!mmvO`{62Tg(w?2qwr0Q0g&0c9s;-SAczOY` ze)nWvr`c=VRX+MP?3JE+?!3ah-3CBvE7!h{;`J535~Ecu#5I<{_6bg^xIQF|EgX0$*X#BbO;G?eOfFC43)^^rL@rkxyr`fsoMQsw(@uF z7??@WmFMfoySsCNSzwCJ0on6w zebWmtm_o0^`&UZ$>y)wK+LOW({oh`I`cI=L^J`U_?{S6h8lGM~@@Y9&$Q6hzikU@A z2F4~U;8%7Jfg<2?FUJ5R`TK27+HKFMZ2$#61#k(d!wPw9&crdz0WK#f5IZG?gXy;F zupZmET;;apmfU{TwsU>Uk$Df8YlEa(oGIv(lK0 zG65(j$3*8>4>%1XT7P41IEV+et^5Wjtn2RyuoODt%Yd9PPk=@4URYhe@0VQq;rr^# z)b9U~iCR>)r8Qgo?tq7lqfhw%_?%$%fqW-zJ$q0AE!ANG2_egrNn>h!GJG`^% zIjW$d@XBQ~yG2H>e_)gM1R*2}8#{4CCG4JKXPFp}g88 zq)IBg)YP9q32Go38vFb@lOO+nbcCv4{C{5D$2(BKWuB=*Hw;O#Sv0QvJi30HT$_tB zfTX+Vi-QeTLqsV-15t}7VADz`#$5`>O;@Py4unqq^9`>6`|lS6lUz19la?r*`GHxr zt{m*yk z*gvytjQgLxVs8ys95xB4sv~LzB2$ucS^Fb9K`KWCA2bi6-Ms^H1`(29}> zORyu5&_bbOqmxF*BB{j}MV}yuqiKH6ME-X{S^lXJ{q^^7P_n~VJr4s9WyM!!Os7qP zYG^)3Mn*_JN?&^^7e>SpB{&Fm?<@!?EW9kK`?Qi0aU(`k1U#7S6dt7S6T)cN-i8gb z0lvxh*Tbz~@5P20kpFyNK0Us_054Yr4wf#1&9ZH1(xRa5_ zWlVYIx~W`9vUpcJeos^h8+#Hhl(-Uu9OhhCOv(6+2nI%lluV|eg?d6ltvF$?YNvj0 zE?26&`kWK3bgUSja()d?;LG1$b;;uK@4^9wI+rJ9A#Ih;W2eT_9GX1HQv#=koSXuO z-3+M_*_7k+K?D6~@@1^{NKndUbCZPC6%TrmcwCz6JN6?@07QPwbUbKx76B-;DSy6 zVEDRL3Kmrs;IU;$01?q_2i`3IOH*wW=K9sKS)%#qgYDGZc+_s zB6T%n7v}AaEO}SNiXyua7!9BIFe*@(;t5zt+ne@i%4-Mq5$xD5m42azytgvuh3_E6 z<+}2a5z#Ttyd5yoMWKR4kYdVDdJwQXwz?5#9YeS4($$JWh&r9A_O^#w+FU#TzNvTJ z|G6i_IPN4YFV&hm{Wq70WbtMU=oVuMn`yGvJQ}v~J0(8&j1%7mx*;{*v;q?sT_Q3A z=H>;Lh`8It-Un-$eS|C$_CsQa2`+*;m!AO-xK-d6(`_KNQ}%bgbv$_A1H~Kz;r;PL zdY^dGS&DCPjtEn|5vGoVDxu+wKsLS7WY4{s57aTy$8NpEV(QcDGPeOrK7H-P!}A{s zoI=lBzY%``hv4C*{M!8oufg6x;QE(@_4U3PRO98@0 z4P<{e(FLU;M#T813knsUKNvMo4Y>(#g89H6GzWfX%h$7Y1qQ5B+z&AIY18Rwn>xH` z`yyI%T5jB4Q7i6~Wbb!BR6du8V#7q2e6(d@Sld3)(n-RL+o>V0)+UV!DB7d6EcZ5z za#y(H&c(yNdLCCC67@UN9vXhInb`06NqKPFqUB*``PO2adxdnwokigl9-7`AP2lJJ zcMO454fI@Q`Lq*=nmLt%N_SsX_Y%@&)v2H`ZRy5(f7rh^ZoGBj-u}g?3*a6;jw~XzMaHPQ*&+e^@w2!hfD1wGjf*s}>`aO)5xw-ijGLPhUhOu8U z7=E9cuGWf$HAze8X^~(hvK0*b6MMm&?Px2!4#ngjjcqFmHG0!6qskIR(<@4pb?Ryj zbmSaGC!q~9nho?_c*4_PymuS=6@t~fX7Jf6R4Ya`ckYh~Q?@&>|DXXL_U*5)topf~*Q&bt6*Z}}T8(~(`b z4q#*f^kc}lO+#bNB{|8YYzZ~FwjK%`{>ghPVoGNR&@Po@lUY+OgDxEJ<&ef4>l(t? zjq1V+c(5tp=+w{@~*PyFV~HD8Smc|s>}S@aG{}>Sc}21|@2-;N z<@Yow8)585dC17BKG^j{mWINMRlxBR*l~6ZS*S=RKI0lUI{7hDjaP_35lSRYB7F{{bmU#3ndG>L|BUOmXJ>gf)rG7 zkGUg^PYo-HzWt+pTnpBekMT(!jonJuHOr*GF1~<0>BDwSdIgnI{&$xyu?7k#(CNr{ zV6$@|m1d;9(6=+yvN>z<`skfJRcci#Df1ero^)@4!)695qX&@g@x(KoeU?Z?-=gxs zY)&li^?gGuty@|-yd%sUqJ=-$NtCLrT;a%{$Xf@NZ^R1W4RY^8EF36h-68Teh2G3hVBF7HC|MG&iM zVX;h#xi?I(sJH|I5;oFXI}+fzIHj7Y$(yWU_9KQZSSBGAZbNsoA=GlQJ1}Gu*Hq*_ zGEnQWN`)+n1!JGI3-|~7Ihq%~Ic|*>dQy>xRP4bZlK5cw3&X_JiBb)Zs-c#}A<`W? z>{go{`iHKm>ZK>CRlMS^txwrm`4`D$H2J^#d;X$4W2w?_Cco}5oYe7}W9s0j>KPhz ze@%&HS<+U3KqSE%>l1Q{dse3sztJvv2Nq_eYAhQ-Hf3Jlhz=5KWDe(HaM90pV}3XF z1qd@gAvLg@azaJqdbSDBeR8czs8nc+OAypRX?7E6+{{pP=)no=Me>$Kv@FC0RS4qQ zp=jd%AaSVX?HuUfcr_aJ><=q%BdoR44haky!&Dekz)ceeeF}c+>DWcyxxCm}P$4;3 zK9C`_x!&(fXozVY!pJx=O`emv=ZEa9mXxpzag+;f(g)9<|Yi$v(xP z59PAChRCd;aqN-Pk@j$8m$O%_C1PPCy=rXN@ZlO_%!7Ktc(i})Cz9*h4`$9xCWZxI zw~GINgM$n6dre?>t?e6hgrmM%yj}QmtohmHoU&=9SUT#$E9gE8Yb#N=DTSQx%|0Q? z#U%qgcF=avcqlGSogf+OeS^^9y-a+Rm2Xnruy}8qOBO)ea6JTJIWBqLQM>_2dwylz}N}!D03XRVqe-4axaJu#9 z=l~hBq5L)NNAieJz6f)<%s7YTTwV#tp=by+v@A#!&ol?+)D*O$(At(`n2aNz5~ZU> ztgJmo|2>ko%Bc=n(J0y&l!#Cyy6ELptTzw_(o(mRZ%q6mC*Eq9W3V>fkC%F-;Gqc(%1&A>z`R zPe-FB@W$`uc#s=JsLI@^gDo4o3~`;Ya?~&q(BE7iwI&8I8(fXPHi*FDu z5DwXjJ^g8;D%7fkIn!Vj1VNAzT@BvHCA2K0U&-Q)8?GaHAwdWu#rFhkEVcI9prd5C z$WlnuKmt&Uh~3y(7b6j4QvfUxy%NXzFYgk7-Vp={6i_daO9whXvPq~qN1dr+X(JzB*0xwW3&RH{*js?y8*0E0L3GfV((I~i#yDwK0(q=#3fgcp4Vp!J zW}CJOTH{;w9jKiVVcj-Co$n!wz4e95Zfb z>*&@rG2%Ps^gi8+@%N+2BUVt_;YjXQflppKn7t6ogkhVZPSVA4Bo()JQyV5^5W7JP z=9&ODWEJVcsi%;&SgZ1s&|{5%w*hmk5xz=b%*Vb1X=s*F<=%gE7itW0WZ$f6*FD{C zm58z(1K4c2|B>gEtB2;loAI?t0>piaroY-UN^{JZv90`oh-FM*Wfvuxq^D@!P9~Sn zp_;Uk`9N)$-5L14!Y_C zI2hP7GNL?~hTV9ks4fipMJ}2rQ$|kS(?x#d(2-7mT$@N~9vf zpC=*}`4J5bfy5L#TRTi4w*6;<942^55!S9KP&&>Ybbx8C6S#-8FF{H>m!^tpi-6pd z?-}etfg~QnNZDf99nXfJI3xn>HLuNQj-#mAT?||`z*DRfr8llmv0x?&4mUK^@TfA$ z8}8-qw|g}Ht>TjCzksY?e|x!1xbx;0FJ!4h*Vw+#)sMmkCP(Zq{o1fwUQBgt*L|Vq zI9?=5sfM|!xrsKxEmrsc=MhEPDRpSs!Q84zKKGZ>iiCUuW&cPegs6)3#*(3Z8xV{FEjxim4Rel+438Cv#UCKN zs=6~EsXT!ml9Qg=is%#0I571b9%gTiFfUjUE2x1t;BA=|4)&J(gBGQ<+ew}!t&%3L zDVsGTUvTonF(#p8bA-v>?V~LtVw_(!{C=VQdCCn>{!RFOogWO^hp&|hoYk4afNQL zMWuZMSh9?y`$6!Z2Z*6buskjw8}S<`Gden3_f{VkSwE`iG5)2 zk0y#h3cLu4`UkS77f6iNQ|I8x)(LCHC(MKYB>q<6`sDj50_R-H>=PR?E!r8o9v&;l z9y~>JeqJ7h{7%ilIeAk4!MEW!et|)|7^BIZoZ*~dGT@hQT~T6PBD!D^11oA{t1@Hn z9V6oja}d&({QFPRHZ<+jUru66xxq`uO9hIG4OTUlE%b>pJ*&QTq!!b#K^P8DSsK}H zdg3cZ0`UPwvl@pLvksA@WBEY7x$jE>%15#fm34Bipw-bO#dr79J`|~BEBjD1K!}ul zgHx~W9@KHNZdg7U&EQguvU$nM-XWckGDhA6Up)Th=N!gxHY z(9M{c&n2~d#)=y`ZCl}jyEsH#d%4pm!I^o3ofa)>+Pn`#*~)?y{PhYhs&Ua`7sC{Y z(_h1?QP;ya#Wy63tW6-lv@kyCODlZeB>s)RF@YSZ)mRGc`CHR_Y2}Oge0ePdn4>EV zu48-wpKO?AwYZOCGAJ$4YG{mla$^|`d&Tqj${~;v>%S$!>j|lQYxab(jX}{AIFP*? zb^TWB%ln33FfOGr_LQ?cOgxvZ_uS90itB#sFbo^kv7zgEu{iRwG~!glIf_ZK{j3bK zw8Lh<>?V!Sr6#W*! zrfNtSGH~R>n8GS!G%z*Eo0Efc)wB7UUIf2ZQI3w;8rG2H(@HXUm3&MP&Bc3DRd21d z1?Jp?Y9pf()}J>|WtguELMi%yFHV{*JE%DO1wTz*7&(OQ{Oce%h4B-klvmE3qQGon zkJ(hKbcO|tzWmYq6w%UWuvkzdLbWKv(2aHRm4z9vHa@{;beZ&_s_z7Ip-M7EsuDl$ z@833N%3biu+SoL9oBI4hknpq*G9nLHite4+xC6UB6f(1`_XSkr*O`VC%%u%LKG9xK zd_kf}!e+4V9gYgVBA+U)s+=*BS{k? zj@8MY`WrFj*?>FMH%1^OPApbW{4L7P;0^R8#z=pf^ll8M70?)bt3W6?PJKdW{} zm-loaSnxa(K_mk5M$Tyk-f36C)s(L$Izq+tjNT=e@J5oB_E)eatY{lpL(Oi|* zJGiK*q$IJa4o}o+GI7YRd0;xftfxjwnJn(3wVY2ddBTZr$Z4ucnCxsys00b468yR@ zt!0sOEKG7UL#$k4n0Ae2DkCVayGF`2wvMb^s`%qG11)8RO4=RSCb5d`cHgk=T&=i% zbDen40_;BGFv-k5VagnGMhw@dK*|B^oB?kx5#=0K%|NP8``Ett->=#y({)y)^GO-n zOa}jGmW&2)Ha{W;oI^FwVCol+Cty`J%+w{SJ}df*?*DUYt8-z!woWa4j7qWECM8POz{N>8&b(Lmsn8#IRfR zJ>3UwL)xq3J-)F7Q7TIg#bT($)(z~CC54qqe2}a0XDyulP0FkB*pfoDuaj8Pp_SOR zX*o-gL!}B6f$ekPRjr-J7eN2;m}nf0!8=Dgo=wRi1LtI_Y>koEGhUD3a9%FL$@lWP zR;*rl|Bk}Bu4g0xt%ZtSkz>3tagVpWm@i-L{TmNPFymGI-8oV1z{(dy^KStQ>pN=! z63_l#<_;Z^Z50~3vN|1Qv()e&6+ByVIgi_qgG9$3J$L81Oe!ci@4_bznF1y4@^B4C zn1w%hb6^tMqI#ec) ztzU{hTp}Kk{{+hcJ=U?H6&m)~7JS|WvaiJ&t0m2hxLA`zknfhG*~$a;IBJo6(SkVx;ZD4OcO^!i=#_0bYzb9&Gk%yvIoOc2wIEO#ezeb47H+*4~=*=V&bMtHQwjK_a%R@ zEXoQTE!)e-k&4|_x4NN%Gp5;a8rCO`&}d}Q^p*E<_EgECKp#VI^L3@dE*O2KJ?vO}Rm%+*Ej3ZjoOJOlSZOK+Z4_e>D_Pdq`ng+5 z-YcMW{bqX&5;6WK_7t1wJ@Y|*2#U3O$p!5S$+gdFM$0hwX9wR{|A*Qx#G_HnyWeD0 ze>c;PYezo|;Z35@Cq{H(oc3hLCfVH_GFhoZXgDuleEwfJ zrh!HSb1Oees)$GSf_VU3K3d!^N|(|k2Yfe4$|I$P7liTdb4Y}&*r)7YOn=@EO97w4 zZlC$oO=}mlct%}OJO>WK%sz+_(;PalDQ-2Qo`4zF$+KbGDRN+*6z1V8Sx?v;aaO~7Z{BwC zD_HoiD4|jGU(7vb2o^f?IHh;O+#6;1CE- za0qUTEN%(z?(VKZ7I%UK_u$Ur?)vY!_k4eH>#bUff}OXgd#0!5>7JhV<+iiC(!NOI z@5x4uyeACVTHmK*{u)y0Tfq{18%Q5hE{3sKfRd)*D!r+cc-*~kjT9P6D1bS3NP!E* zZ{dvp>SN`AJiJ3J&IwbDwqvb~(+(G3sVL*I$bR{aoNa47@%R-k{VdD7W8DvMwU1<7PIIS$ z{3GJfK~Qq#?cTijastR_O|(4#Gx#8@3WW^o`Mx;>c}ZIIxpHOO38% zIOUl0=N-*^i-L0NN@kS;>{vByR(f_7b*%D(%mRk|pE_ZbwAJCjURT(SgxInMx5%in zNzS+#1?vLsQoH0NgrzA4TzDaWTn~;x**?O~^Y$AndA)!b3L|CJEi0F4nJ2o~l^>+8 z!4ftZ0!cE~S_SkAWxT~yVwD_OF!vqrb`RS*w`^`#%_*f-_o?X9))w`{I5eKIL}f1s zTI8edFS2qWW!DahNtT=L>fSED4vp^$s3JJ!R5@JX^}XSt1CAsn2zkaS}v>^R^+bFIO%RCJ<+}y9{HK+K}(0|Jb}5 zD8s58e$KSjrJRt6KfW{?2kM(2CZCkeY~CSYiBX9Ic?HZ;fwx9DEpNIJTO|S?i2w0b zA<~SAW_!cdPz)G3xr>`p!e#m_1b_t45#x+R9SuwOSK9i16PN@-X-_Q%ylzhaY$i(6 z5Ic5hbOyeVU&@hc-lYnYIj zG5sK#J>)j=HrsRjOe;>GMLCEqgSB*!lYgi2C_bO0qw6vT=EuI5^_z^`RyV=d`a_T; zR1W`q%SU=3(~96{uP{=^Dmqcjhbupm!Delyf#PdO2d}kNmn|_t)vZtNBi_bHRgp+u z*99(ah_MS_quRt!w5eL&NDv=y(KZoD9!^m^l{DmI8l|#eI%X~W|-fwb`VbUj=?oBS5k%0TmkG!efq z8)A2$#H_MRE>`%UiSkmkME0T)SmBk1XYNdOfrW_|`nlA~5Fvy?`)9WiSpfl4nj>tCEfiORN~SHq{p})gIOm?3;=g0rU5VY z1Z%uAzw%VAX0!<6h@(h~KBV=0s{QYVs=>zJRQGgT~B|I|rd1=c^p+}yMrEKa}*0|%c13vEN z6jx-tUMl|DF6KE&SYUMR8%1N^uqz*7m+!KrKTS$X=gc^J(2rke+30E=y+Va0oa*OD zf#K|Am>=-PyNBSvvpYHNz2bXFHh!O{f_Cn*MVPReAkG~_Q?csBIiX(lt#`p58v!8OLpM|%^$gl-sv!+n} z=Nkkz$9LmSP&l3d-n?)vP^wAZG3*}O6Hfra3Tl0l7*_^KE#9_r>F^=@zR#|E(MMx2 zeJK+X$WX$glK4w6O_x|NVJ_xa3MZM+&KMTbo!M|kVM|hO>z5lh3uH*Db>C5N>AXd? zlZK6IXMY?Qe_(#tAhk1I)a%~ZHNa4=BmK6xTi)-+5NU9E2Ws-|=s^5e#bMNfqv$y9 zb$@L&-qIdi936Aq7o@q?9(MYrwJIat3G5 zg;9IA@S{~Xp9d5awI5|@K}Hyso#TV{Cbzte3K{Yam~RS3hAk7VF&m|R8=wJPqNhLp zan+J9lBDtG=eklZ@8Fi`6AO#o+ZV==00e zZoOH9lgjxL$;rvd*HxEN>=DQK7eKVF&g0sT_m{0{m__K$O~}M2fYCM{orby6FjrFT zhg7$fOBZ7K>l{I{X^>~TXZ;#H=iqN@loPgUt;&90VVVZ;;KVD*U<2Rthx(?G1*SoJ z{{~Pn+#))gm!GsHC0XZ7lla=~oj}BU(MV3`E)Q|y$xlT6(EIc&f$=H#Zf_t#jS*G%;lo8jn5!r~f&LQ`VOy6+Sh7SSPkxkW+j?TyhG^Jp z_q0cTZ{m{bVz^s>%plLu7sus`0%Df)u4S^I$&OcU&Lt_X5psKhbcKe(*M>AfZOe_9vvhvlyqBFHw$BOGyp)kXrLYF^w_L5>I5Y z_aiLl!)}Y>ehrGuXKijdCxw!9uEBfP#$NP#%c7(1_IF-a%u(jS{l1Fv;x2@UZ{0ml zBVO*!UWJ6e3>}K$1;0mC?|Wzu*t(+{Zbr|#*_E}_FZl3anu{;T zDcD=DL98sA4m)ND&lL|zeNshUXx6p+aNfK6gwzsogvbg+R%XKy5TY^_nsT*h;n2EZ zIg@H1`w!Yu^xb5-DHT@|=~8TXmj`q7L*wCp$EHRU?_u&6)w7Upg8;`LH!P>LQSqX( znEXGO8yk_H2D=eLrOVvs>pj0$n+Lw>3Azb!XGCJfoOpv-P@) z25yv9rs+HU1lL`oq-{QZb~3l>zR05NqVr&s7*d4umP6@R)D3~iv|8~R`ItK-li+}c zPldWk{9UYa4HXUy@X3|ECgts#8?I}pBL}UZzwj26)+s+arZw`x7mfdti{da5gokx=t3FCA~q}WdjDMi|e&i(LCC9kJ5#0okW~iVGBtq zbzn+@&hL&X>{|k(xcP6%Iyzz{|S z^=NL_woAT3+RGqxopsv<+RZWpEUGKPyys5XQz#$`bD1DB8L6?{0jRI`k)K6H46K&s zKX~(O>;A0Nt1wE||F$TyBhD33|8lCXlXTXdD(bkRAMVQGj2z)Fg-&k$RsFF@+6*P> zfV@vQBa5Ccpgnb4X%$`2_EZjbl@Vf#7}K`x5H zmB;eN^+|k6DyE4v5q+}Ht{rRSEZL-*Z*z7VSSYSJ)NE`Ove3L+v(H~*i}OF(m09Ty zIy!R=#>0IV3r|a2G3tw%9Nujb?-pJp5u<4oj+3$9={|XGrITfdEVqQ#|H9LwzQIk|N zgk3$1MWlWtawDOFAyeI9tRM>^jhs|}-@@3=O~#3tu;JP3OSFL0&NMCHgW?*mdYjcqsQDZ&6=4{lwaxxC_k><4N5 zBr>GDD`6PMw1R#JKb^IPMIpa!i$&{*c_YPM)b}H@V{zOE9(_C+kJ^TvJ^HRP&6ks^ z3%HaI*ZdZJNu~N;=Y~H>t9OWFWvRkSgVCcAUgBJ?x!+e}?zlfyKj(M(? zOk);UDNFhs67eLqWD+vB?pp-2=g+Ti_Pzala5~V-qmWuhVOCbxJ@ae*N| z>wP}@{#y-{ndv5-ykH9((B$`Vm2$(bJg?@`YQMNhLDLf|u4toZr5fYo^!-(FSM0f0 z4MVS+PO|`JkFKiA^u=rF(ZrkP2a)fxMb7>p?CQobzf84nn)%)~>Gd1(n|@!iu^mO~ zeEAvVroj{DrVB1jG^BR$q)P~_f7yVK_}uB6et0P#IRv{wgq)gBHKb_LdRAw;L)P=8 zosHbvsW|sY2CXC0a(%{UN?x-59xoke^ zr$Oq^${$$*=Qu9lWhZcCzIQeFG!Ra~>y!y=id0hB5f_vm6D;u$Iy0BhwfF|UKD;Fm zGNo2E2v3TKTx_ZtB09W$_P}$e(nxPjz3Y#s(89HrCZm-ERB&2&vsE@Er0reH!xXTs z(O0_b&LpK;N_hOmyEwF3Z!gH6e*b2Q>Gpgj~|q8_?&j1W+>!63nr+h zW+ER_r<_9!CQv6Ni!>Y*(%v-SIfBLPuqno@103i=euY<~z%^xVomcd=zgu*k+Fj1t zlA!w}F4hREXd18~n~UaQ`ekWzSMnhH*NhNJ!Xl43H8?A#>wWm+m4rQ||MK@g21{pw z=X!W@uIc(@jFn?_1g02Ze3GDLcAbyF-%J}pZ;(^L%IajFwD2}{d}D)=5p`b?avd=< zhpgF`8|K!^tUBqSROtYL3c&h(w5bgi*0-tmRY1NR197t3;iIq|)b(GB(dM=tQL(;ju+S`ZZdJO;RajznT4 z2-jaAuKQj6Y+gJ)t;s&r63)GLVX2qUc8iTAsl(ZZPrbLDwbz%NB73J4vEUlMB`e|2 z>M7*bvtTHy=sLqtpagg(N;0mE(%?swLjZFI*T+4Y*@L&1Eb}Wh7(V8(YKF}N$ldFu za;nDIme%tniWCwH`QL7ggdBwnSQ!O$^AUmDY>W@OEw<20)YpVwvEYkwmj&vp!IeGl zjHZdqNofawYgBeOOr~<4beS-fx+70x>+1=i}@kP@4WlP%qzW+Z51 zv>U_R;?%2drd|pvM5Pu!XBp>%;RH5sK)&!iz&WUI{(7^?lkzS|po=x&k6drr9|*-Q z{ku47B!AI}$$~YCyIGzo+KRsqQwtd<8ijqBB>Sne@gwtttL~kf|0d%|Eb360Qm}5V zG~jnFc(f7K{j%)=wAiv&OKu=&n>EYb{Yj$j6TEA%I5}zjPWiw`6O5hBa%BYNw+RLAmZ31y3dI;+s6wjr zk0^0gFuz-D!!bylOSd+BKDD)Co7Q}<2b#!CYFdrC`+?*#&I#fL94jo`SPIJ+v8I2~ zSoXfx-EC5M4A6mn$>+YJ?q^}I0{FO-!GyS;15?yf#nL>M{e7}!b3Tsm@)zA^z-0iS z4fpP8c|X>Pu2`4n-t=Y%ADf$^0eL^54&;(EvF$zx^L*pQZlf9#5^l{Ux8aa;YGoe2 z0I>C)VbSs-YsbrT?>g!~#?1Z3aFr&ABS#`SL3%K$5L|xx(y0`sw>k2cB?SGjtR9U2 zDF&V zm-8>TEIRjsG%8u}psT}xfZgxu+?l84D09Rtl`UU1$YB}T!hrDj6$tf+W$i>xwsz$| zToqEw8T1%Gf8iIevSWW#?u-b=H|7?4FDILbDr=}y*#eFQmG_|?z;~*>Pe8yzd|MxU zHkZ)+ATSn^v|We$wfM~)WnpoBCQ{*_NhDCdD22Gg7 zSy$58uT4KDUzog@H|H|eK~*lW?S-D+W2CoVI&lsaShV$oLeWk<4e=Wk9s1UKj*{Pg zcR{-I873+lVdeN3Z%#7_u(_zQPfN+=b&N58&|(|ArwiZm+eog7-wX1vVMT@OEG0S$ zg2`ri^Elf7ZlldVci_3YTlILP0j-omK!O6EnHPx}@yvd|I{BwA(Bm_EAZ>$e@SlCy z*%O?gFgna8XrlvcmUm@Lu+pLL#+LE+FKVBy;3lPR0%_ak3GAV(^bL>=>Btox(8m+j z!pFzyOzDoS@49p9Fw-t*KpvJBeQ&L!G%D)JZTfo}3}@wc@fpw|{I#%)D&8KUtrtU> zJL;|#;oQ|`4ih?3Agp@up&8#2XaTh7(coOywg^h(?x@Vt98ZU+-_>2hR_VB5bR_52 z&@)@dMlmJvJ*N4QLM#UgZW)eEU#C>=#XS~g?GiN-Mm|Mb+!8tJp0pdbc^21RflJ%0 zG#|S1GPQU3QXRES-r@l{B}5fejWcX&7M#?srZ^jv!`8B=3$Xhx8wGPDUTX_iM9YM@ zBRNQjp^9PXW1c}vnZolcwm_J1maz@BTv&GL-0gEleaXwRS<8ag%rSa0{$8<^<#H!< z#ZS^HMbs$W@4QzkagY0&pJ~k5VZJ*YU0E)NFpgC=9?gW1iFB_G*dIM^DrV6i2Fcrh zyA@01Yk(_COi5g9%!J!&1s4vOwei<;r;PcvJY->>=b;wKvvxrUTBvo|MFIsf)wpEw z_St%ScN-G$C#;{>2`FmAh`dgFXaJ&)01+i=WqNV&hcZ$%%QsaD zgsMSRS&dkVZ#i#ut>?Q}#9_P^FmHI?L;J(~?=r?JExxJ!Ip-H}R$#JMcmNCmtdAZk zPT~G?HE^)%5S)>e8!*dI&$W@XvX%Qcc zr%+MkH&vy}zteS96xw|{&xCD}y)ALC`h_sTNgUJ)0@1m4sO;uY z#}6!?>cc)wkcpdGCgE4Lf&2BfeXd&erzr z$Z48aBKDDEB@Y*^qq)NIER#k=!xp!all~bDyhBeFkm++w$4@(yKNDo8XK+`UOaEW<<*)M z;~EU(mF0SxYmnI1n&Xh8mNB@dT=$|hg0oc~O&k_uXRb`<(B7YrNemdBU7ik2dTw3_ zl@6u|vMDzOGvNx}y8XiX$jg0@-p)DNylan&r*iUbd?T`KflxIuTRIS{eajvtTxk zWx16+%5RyAdEeCXlIyR7OH7K_N!Ez~vVWNHnyo*1SP7sU$a{M>{~s1WaUR^C08low zp&j#9fB%a%JzOqU_6>kil71u|%xp(AhcjF@K3;_Elz5d9--3HzS+s-c+d>YFXn$KE zgQVFMmf#d4SycYzjyy~w^k{G))|NA&K1tF|N3&J``2JvtyIQkV80ieBGIl3kNJ#PKH zWSraKYm$CZV()a*X~(1g<7w9s_TD`+$!^vtkVhSI!N%sW*FM4YD77Rv z+7OYfo-tgfA1cf8-f;f0pjxN*@CFaDYIZRU#skV zi{}qV5&op4liX!V6Bk*u0JZ%^S`;!7Rv{K2h%HBA_jM^#&c+iNCEFeR(-_EUzx@KT z)Az)BxM!>E>NE=wC+qS?UjMzU%1fro>(&4paq4C}Te_}9{)lr|UbkM@fIf6mgz0^u z(2RbS@>{bp^|b%7=fuyg?N%UG;pVS3j25LBnD3Uff#guq%?+COXY8SctCE*AO59eJlVFmWlfDhw-eoBTzM8i?WyiTwR8Wu~l zO5BiJ;A?8uQcy~*vrAWZIN__Fe|pLmdNm&(zj?Bme?7VvF{lO!KVDnSm)oXStDfn~ z+AZ`o|87NJbeJ$qYy62qh)6oDnNVN27-cEDm%L8M$Cj@4{tP7==twION+eTC%H@ z@ri-glS5z^xlq;^Dz88eZM2Ejl~KK$ShlsDU?v>E(IDSrn&=yJW6N_o;q5Tf_ir6U zZTa`#8uyJaJ95{PxyfFqwAhxpAh#?g3yfYs>N;1?1Xx1e{Q-`6;hz6y`VQ7M9Kj)I zIP=5}ffff09Mk|3>lXzVV%$6Uq7?$Bn_E-F`lwr}7kEOxsA$cq$;V02BR6Cvh0CDh z)rq8yIwpmOQirtAY%8egNlFaQC%A;Rwzd+Ctc{kI(NjK?*vnj)+2?Q zghPL5*@AgsKV}Z=?a&RynaoB=Nl%E($$_Y)&&h^=`#q4{Qk6^xepoii+LD=!)A2<8 zqm4D38BHpZv%=b)Zlx!(j}nV%9Mz#l=Mus{+29B6$*o^*Z8R@LcoKi2)~loIfR5Ov zqNersxlG3qWz`?;`Hv^V!|4mh9p)e#buDcl`FF~VM>I*2jV29lzt!uDtP*me?aNQw znpnf{;#)#X3gG0iLagJ|%+RdmFW;IZP&%-sxUaS$*g(`C5h6X`8e%r!&`sQvAlDx*BRCkQR1%{gyoiwS8z@qASI-); zX79-&s@oZ=@1XXX)2utZ7sso_%A7e919n+sm(uA5gT~QHtHzHV+tG%l-KPfhJcxr3 zLcMDOZq*-I+_B5`-JeTPFtHvD5l=gxQ)Z6Tk(c$IN&7QjTPs{T1N9Cq0y-6sU3~zc zL)854YCrMrP8VDh2gi<9>aI-d&FW5ajX$`-dxX(hQbv!>H046;R20+Gkd&f^aA8^Eu1;8!c07UzcJW}3zk zZfcr}5a2F=SNlPhMj(NpH>auo3vb|uzVEd$8!&Wlgd!k~x!sdD*(}uZO=j0>mQqj6 z_wM5*!0O+L%h{4(DJ(5o=b~f%;asrvIDg7-E*o<^ZV@`_I=ihAX=M7kYOUWK#A~m^ z`51(+LPv@sW51XRJPq5jKE22N@M+od=~}P5x)%R0_#OBIyNkv?r5%<1>yHOB()4PL z-ILQG%}To7H*vP_0#oElDa;BrrH$h8ge4Z@bz;px6PW^I1S8otP)-XgTK{n3p{A}ZFqqTzd{sIsaOzBqM2jk;g zXeJV_xl`KTh^Pw{oCN33K~ z7uO!%gT+sewpcGwrQC$Go6A`0bYlYqB0T+k5q6wj9c1R+)=9rcPWuc=7IUq7Yo1xN zhWw>ONEB3K=`;1p1sagE(s6wVUB9@LboU$E%La+D%8sfVUAa-c_V7hK2vC#V?8G@fP#-5xEt2A$s8Q}@(qaJbDp6}oG?hS~sxTMk0*%T$ zJ(x3G9@+*8vYO9V+OK5jT8O#gjs1K~qW{Tx>;L5>e$J(TI}j)TTOQd*KDFuIh}Gt@ zSt1}CmIF!q1c19K$Aih-%qj^LBDQV;lkO?cvA;w!9M6Q93@HtfDZ4&UlejJ!Z#Hf6 zM9jJzZ9JNv1(oZCE4elte$#N&6Bb#z7O_590lCWqEa7>p@0rmzm%+%+;vh<5aoXVR z$QrkW`XS@oSchQmsRJLjtPAI$4?XGX_`ZZ!8tMn2i(M_YHwZT#o^^uvAyRJ01A5+- zzeiZN4J_V6&=eO8jnlfTYwNCmg@nP5@+IO1ymmmhX`(qFigq>qHm+c8f+PrLGQ+$1_f0{vdeZhG>kh_XOhrh=fsb)j3Rw$R|m2KnLVp6 zbV@nFhuqckafck$?s12Fz-GfJAe5u05xJs@9zC<<8e{Sx!2T-k8l_<(%3Pw4!l1$8)Q8j&wk_ z*KLvk%i0GcJJ$z?+LT}~h|ktx3$o3s+d)lQN&moGze)RpR0m!n9#Xd1nn`>(l9?rQ z7s{S-(%^ERGxQAq{=euuqQ8s{$%v_*rSPuTHge^L0-DKGKH`(O_2!oqC|?|*kx?ph z-uSlr+J_z4i-Tdwr10VZ$}wg<1;jih`cn1{Bz%cBlfjYQtQDTM5oVoLK}StUD8TmG zxelVYw6O)&c(yUXa(h{+>9e&!Gu7?oWMpSn6JA^X_>tBhp6+({5)Q_<5o~=h4=moQ zpMf5Sz>`W(lYfc)8?eu7FJIpJJfBSy$A0tCNrl0WZ=`^Mwb=#<2n=pbX>K;_rS=?) z!St|y%i?((TV9W3V0NA=GFFM`Io5Vzf9tEcehju zN-**FR&3?5QQtx9&@$iB()8}WPa`qBjo~)3Q-VdI5V7#7bFRuLI1 zD=V?QjW$U_I*3^iSKQn{v)plI*X)PS<dm;&_fRlf%Wb&=`b5I*+0FW- zCXt&8@_(iG7YzYzAp{cIdGeU7`z*Op1@I*Tam|1ZrZ@$asd`FgP(_&)_0PgGNXq~! zilKygHi2cm<+^{?Jb-$V8f4?{JltWr#?yENNt0|J@wMtiW&ne=N{w%!xc?8~Q7!ua zQ)vjzKiege0WeKXy@dtsYB;@v;}WgOysL%RYEgVWw3DQRo0#)^;LBVN0vwH z+=sNaoIVVz=P@EL$Tje#w5BK5@KtYSoTi;!E-wcgM@@U`yuj!yo;+OK{0F+W{nr*p za1Ugeg#O+N&9KQ0re0oAm>E@I3Zocg4Uu?D zTFgVjLup+#G2abQ8!DN){7b6tu(^;X|Hw*jS|8*`3gP>k(?c{W$0VTed_zQa!#EnJ!M}>7!sJi+Go&Bn zf9t2F=k1n^{I?3uk@PRMcAEEgijQ0}A1ErZuF}Wni!EE-H?EGVuzgc3DN0OyqM>Rv z#}5p_P8YH{DPQ^*lXXw|*9dlyW^$&O#^idB@gIm;FcJciL?u?ZwN6ULkEt3F(#si1 zy!m_{&yWAhl$iLxCUH-chR=iSoZFoErG>IgR@R!Jnf-7H;A8Qq-uX2w_3#(5>A&Tc zKG1*h(v=EH{^k3=*hjRZvb}YaPni?MP$y6)(wA?!X zObYW~POc&UF^aa0Beb>Yxp`kLS(I*r(3@#DmFxct$=ufbn^r*=EXb|S?w8fyu9g3t z%bMTI721Dx_g^0u=-qe!zesd@xCuw`7H{d@$HD5&26lV&U zex637`Qqd0M7%b99uGNxs{dN-kgsgNi^bD#-ywPDv+So$0)b*r|H+pBV>DdYwpGk4 zdqN1m`1}{u|NKQvKPojI@5RM>SgbH!>&NB!JGx!SO5kJs!_t4qKiz`UQY?Zac%fmH zb2@o$cW&Od7d~|T%dI)a^_y6KO zWYBvZfYg|5^3Hb7)Jn`=6tsP>U9_o#n-;xuVZCj?P!0ehvhBa(K>v*0M$+7XznC8J zp!*ae+HY&m7{fC20X+E$ir#Pt`PUffKiKqtrm)T2Ry1<`fv*yo z5(9OmO4hZvm48)yGmU2ciVuB7k*SX_bW|y@#ao#l>7PAHiuXqh|BQA^;m<*<7yQnb zAxAwj9@_|rbYAuw+CM-;Ut4`;87dh1t7wpH;?DBMcQ+eBD|@vq$d@t@|91?{*p*lR z51M&9Tmk%n2wZ`}wAY{M^7P`*UFy?-`qcdN!-!w)1Mxzu0!9gPFdf0MH*q-o|BM6w zzvJLFRgXZ&3l|eEFyW+q{4srpDN|-wy5W=pvro+JShWXqUNF=!Xz7Lnh@*Wf_uM|u zR?342-x21ooe7!Z>R4q)l4OSXpkrCgOd=o7;FLN)%4wzBxaK}}aiMfqh8X9_x=sa? zNORD1s#w2nGWPaA#QX)7g8b@*K2Q+F>rNMFi6^M}ai?IQKd~ZFGIAQiAw-6WH(Cd^ zR2Im&;o45jVPo>~=`_h3`n|h0{+`W^>`i$`1s}6(D5GlPp3_S#T+5YiGC!o{!Vgy{D7-EY z?crKO0zl21aY)^642r5u5TfpPl;lBstpq;zL5XC$Jy_Jvdvhy@m@k(89R3iF41y_H z+dP~v!^mE5GATzZ(}0y@x;Wqs{z>+OD&q@)cN=8g3KR@yNf!sXy*~uIbMfP5dI$VcOGM1unmemp@;)HO0OpTsfxNCG9xos* zSlyuh#JQ3dk0ZSZ1I#iJkU)++<>%zfa-Q#O^vQw#yUehH)=xetjX0g%tmRsXu?2XY z*mow9tK^UhZ6cHMkD1a;ndDe{FE#wPl&s;-=&ehTp@mWZXUTXWvG&Oit&HBBBHBuQ z-2nTS$l=PBy3^~J4KuV+Lu$XtnJ1KX%YV#>!`%YT-MDn=wwb&3~FB)P*v%LOceOP(u)SF zCK9N8_?0udtZGgyE~qtm@RUS(IU8%#x}AjOxQHQGj6@>Yggm9HzCY!6h26};DuY+@ zQG0b*=Yg0wO5c49wSxkjHqF8Oo99cJEVn|e{Lu;$3LH4>8pR$Zl!bjh?-6%;CCO~Z z*1PLaXR+rP07+0m9E}*(Ka~&vD1mH6I-o)vX6Z-GHIpwa&?bImO(DHPc^yyErYRVB zlD-pTgt|5YmikY!B~z#fI-Ps z9izqawVm#uB#K<)X3a~Up-b}^fT*Fxg<>sS%^@sWtX0edg~$@>KU;<4k5ctQuim|Q zc)J{Rk%sx?O0>azzii>VW()l^m1~H{$4dv~D07OXuC-(Hk`j7uAMouimwQ{fvrrU~ zY=9@sE%jY?%^jgWV6+gzlR|%*+W@ETi-0RN@ja`m;TgtRb<>k9VNE|UOAYZg7LvCRM}EwY8LgA3i##chUB!)ihj%OD z-8$=V=POIE=~6jxXY!Fse-5SXX+A->HBT&&s@oMZ#YbXcI^d%QsiXke^GUF+V(ILP z)0fUvQlfzA8{g<`xI5QKdqU)8wOvSqlItBTmbrp7`WO~9YP(n*7PXa7QpF`?OGYB3y{3Cc;y#s0KF$BDuU z87G1y3WA~-l-8a%wIAX5xmtooxxL5M(Ht!NUD(3RghG0usY?5%(5`N)!Wnd5I9;dVWa2d+({ z{vDtmU3Tt#c9a^j#iN~HnSMnxzvb~E)?U9wh0AT&BkwH<;wZKcM5_Pk!-^j8p{O|gY(BAzs^A1b+}k>s8_SU^BZ+; zMpiX#^;+f;2n4*sKMtZ;?u(~ z4n^TClE5h zbqQ?~%<%qlOY^DN(NO8$9wE)=l)C+OTCbc37iBi1*cab)mI z#7B&2nXBjRX`}<`doL5ZCU@L*8}V+vKeoU<_KA?Ev|FEWsj+7jsXr|hNApjOj-@-_ zSpO|swO#Gz(q5_ao>nwk7}s;L1ObKbE6AiJ#=LGn(2scd+T08ap3~-*g)#e4p(3Q+ z&tx-)=>1o37Wi>e55y*p1yQM$bes4hc{S91BL`<(X!L_hzu(R$G1ECE623XLYt%xzvBt( zOQD#9)3!LpvTy1^&5J$7Hej0WNbe*-D%$Hm4^v7Wqu1(szN^|hlqreGssZ!NXV$_oAQmb`J3#)ziXm^Ww7_%5zAY; zXAx=O6P=G)%$m=Zx3Eiwpef^ZlCtF?5eKn%60u3L=`&M9UTw1NNO4mk~79|PPgq`11cLV`x} z!n;BQ%UeJ6%oXVO+3sH5+whO)5)%!~AR|!(Z&omUixEKIU@lJWbM$;TNo^(dj5}hf z+(~Iud_zfTdc7XLf0;vqR?Qv1+!{MqrwkhFlAJ$r;DN1MWlp zOpT&ZM|<9w1P`AU4WUD%0mGUJw};j=$?O@0xHVvTA)skPgcS5(L{j~+pV?v z>CrLFAO?LQ#YnwcOlg^_M=262<>}7vLhClQAc6`yAYxGrDhN7+j=hT`r(J)gc)`Re zp;1wwXy;RVif&}a5_O{&A%~8B4zh|LjYTQZ{K1dJn&xKDdKgdq0_kw5T8|U*k=dSI zK_)XL@x=o_bGNch4dh(L)7weiQ=XN|_-A}R&BZEt;^#t*)A&6s<{@)yP1H=T-?%W9 zl-A7ZAq)~|y@B1%qzvDli1}8rA_hE04&1-di4whTg(QnUn9m&uDX6u*v_fW@u9w7keLb1JF#cSR4{#2yc7jE%M~!g?3t_I8n@VhYFi|UA z8zDxcMa=?!8$A2n<9_phor&@IfvL)Y9zWmup4ixqFHL=gF**Awcp!e~?VwoTb}UIg z5wqt4KyVF4ezSW5X(wf^n38tLp)XV>(Re9pba6Vr-gbiNYt>FOBJVp2-OFNK0@3K3mFa>RE>Nvbmmof3LdR1h#|p^FXXcDO|)-nXw^C zY%>e~v93F&>y@&mI3VUz}^q*9Nr{?8vQTjU06>32-v?19x8^T3FEq7)x5_zpQgAg z35(3s^MLoYxyBzFthaR~hAn4Sr^OZ%xSagCvX6N7fYfG+6gtmdu)0cDa>xp|vH5%Y zgh=`mW^Cwi8tBW%x$EcJ-s16HPWR<`N9n8~9gHHo*YRZ9#e%Ci>tliIQNo(v$kJT4 z{O(eCgR58ZhPa%Ta}?AUR)`uqh4u4K9X@9z@Z4?&KIX-3-F^u;m9To2|Knv}U`C-K ztO(?$4P*IhXd>c82#f^XUsSe08c6o`3I_yVisf)~Nyt5LU}7d7)F&e39**JP%2>eT zxw5`!aJs%l$ur7$$KGoAFgfqNj4}~z_CwtHCt;9V=);;nsr#~XFEl0@CmnA*C&0!i z9G|nPiuAmqVrtpdqenB?kzNt8pdVZml8F4F zb3@b`QY_L6O3+wbs2U8}LIIerp8VtuoC%=PZmc(h$Z8KBu2kiS6t@1hXUI(NZO`DW z?1LHF^!$HGgkZqkmc0rbb3NfHOowD2LMKe3PE9$F7H}6SVZN8zhf0MJB?l+jy$qZ$9GdC5CFP}KeQ1BLwUdJ~8ePDCci)%sz>Aqs6FW~^T1r0Yv+*rV zZpiP$QArQ|n#3V$icgz7?R83No!zo`x6TkBiS(gkPvI|lhK`^w{d}uz{ZD=G8QNPx zapF8o{&n?S`i_*|jEwbt7jhvW)ekM?^VDIRDb_4}86z+p zuCOw6T>cWg?(f@OzL%*;eC>UA<-38Atd=BOPOdpED6C#tb-;zGMO{gRAlNGpKa9%P z%VPqmcp%3^t)^cPO&uW@#?E^T6LX9@`ySMjKuL3?0#}T^vsZ>fpaWX;oS>`M5Ozx` zq1f}8or1CCdsb1frorTz!wvfYZl24iLuTsbDR`q}C?7CEueMPNxe_UkQ8qnfg5N!C zZ%FO9?ufrjwc*o>o00{S^w~0!!XP3@YDh?$<$wvwU(zOW-8VQyN4y(b@Zmzq@GNdi z4ch-eDdS>N`vInPH`>MR-VgsLb~u~yhh9TBDzwU|I<9)cfB@T;a=39$rKVcvLX z5yK=-b5%lNQIyc@9;YzH+aKvx1 zrY;StmdkOspQBHBuc3SFWti8Q9YKvEeirV;tXz6gEGfLlh z>BOy!&F9VFN6ErN3o=-JcIp<(43FcUk(V^;1${Hg7JbkE$J|-J#nCj48Yj5BLxKf& zcS~@05AH1Pgx~}T?(XjH8d%)j-QD4A-tV0E$@BdM=ZBqZubrKq>6)7EnyTu%=tco+ z21t$|;)eNSWG@riRG>#DT|Lz5coY)%I&rKM;s!nZ&?&#&{?K~UPgB$S=8Q3inKWfm z9j93_XAk9XZcCnncN^AjhnV=3=>A-61HTbCf(aUM>T(NL;$XO^@{*%!uiEl{$%ntZ!9!+bt63B5NtV>VlQ2uTV%UF>gQ1l~m=eMyakmlw z$gywj5|c8p|SQX=3#B9kI;M^)_Fg+s!vTG=ZY=cR-)41L|If1o^xpK<& z06+B3Zb-W#*o0?*18uH0@h8h_cY85&{PH!NMc1N4e~w;xBqKYQy47Je%dZf~Wc|9A z{wmyf>W^^<#bXyePdA(|{livbhk|(3aeGLMkDAnl`IOOMZeXbM-0#-PGi_2FYi76&!FS{i^e8 z^~D6{sF-WzxwEGj;yZyiqSas==trk5Rl315DpA#z=-nw04EGQt&+Cui=C)NyjonY6(xL~b6nXY>8qqPysUT4})*pXr- z(#Yw|``6eRYr+%GJx!AQ=JSs`@&)+{SlcS4f2(;=Ji}^Kh7+z0W5rZbiQQ1HmmZRs z*QsX2am(&42-yK~VfHRy67^iraEEtt=2jx7gW_AZv(qr@5d%!N$K@AMd`6Fj z4s#v$hr#JGK&tIGE`5lF%8=8`A(N3i{no2eEX_5glv9g~SUixg;XSB1k$=E-&^-5Z z7~uJVwQ@oK@(TWy%3H-)bp;H_jH-#JeSEdUBEomQD!!j*-8>|hxg^M%(?VH5U^3fa z2@&fwV&XE$eG)uCH-hUUpLgwbKeVO^c}cF@6P5C zTfAWVLfL~xueQaI?OOZJ5>UA+@pu(>`p!ahFmiMuYKac(B*|-F=O;w?{pL6O1I+p+ zQq67KM9itpK!HQ5DmkYi-PW02LZ1%oSa^0X{zq2rVelVf`^9qnn;vdF6xPYGs*Uxq zC9w-FOvVszZzB-g2}D$#;3!M66h5 zx!)Kk4!Gm?Ei(PgQw*Bes}_tW_Zq!Xpv>}>{KGfe| zk}jhM7Tbni{DkHZnc|3F>SuK*m2%2{5@(}uV8v}5)xDNR!NfTnvN#Z^xBP7>GYQ$G zIgP^`^KFVlm)DR{H(MG-jEXJbvy6X4Utn-oTj!qrw2$}pAXo(r(xBh`uLb)-;Klwsu= zbpqI%PsigXv?KZ9PH#MQkIDj#T>!U141T`v9H}sYJYE3=Ucn^Sbjt66&h@E|HI?7o zVg#*T%f^zDZWqb4oyH<<2H&RGmuz#r=$}?NN)>rFkXm~2Z@RLt%Wz7I5i3qNF3UB;ApBz-O4gYjz_fX01 z{dw&*Jay-8)0TlW*piEo&@=#Xo9Lr)46+6P4}POK{@!H-wiRdIJZEhUr%Ig3V+`{6?b(trwO}W> zyfHXE9j99ox%aP~>8&gm=%K^}XjhkplaL;8JmKS2uCh+clR?gvT#j3n=KJFpu~c#Q zs~)QF&?xB_*mKpmWn{7f^^8uk>EaOnjr#dD)YTB0&ufn69~*t;E5d*?7(nv;exx{ z=7Uuyng@Y@48a$k`G@d0+!cZNP@CC(2^u2L&z0Eq>-_ymM~FoZ!HiMDY2Q)XF+5(I z8OBZ*I2K)I=WVl>IcM9YZG}EII`Qg=L*G$C;J50(zx zz9)P<#6Jms$S1kf`wp@NtZ+;l+CR)n1xE`=1kR zWS%F~T*xh2?3nl0@vE-)eo2^%!K(EViNC@|S?8qA^s=4|k-H;Axt8TT2vUosj(j*j ztj*uJPu|IK<$;UbNl+T>lD?mE_GY;M+&A*$<9eBA(9^_S#^r2z;bjEgjy7$aYR*XE zYuL{#4BXFM6r)OwlG4J%h}bw_Hgjv?@d`maSRKj1rovvYp_^s05ubBjxZ|Fijg60e zMKlqsyVv)$IMqZVAi|PlJ4WCx#rVk*+VPcsY1=Y>S~r#hEg+UGbY3GW@$0UWj&CxH z{VXm9wI`;*!MUT8#GQG)#3dJ!z?*Dz5iUaadPAdof8S!}n#Q(N%{BkI_rWr2!jLjS z2fjy$M(9`_I%a;J=oc^U1Owo`q5Tr~pCFt-)R7~ue-i#r{8_ai^zBa{PY>fKBl?PS zoi&dJYM>?rjY0slB_M;ax1l)5*LFHleevFY<`7%FaAVJQG_^@;oy^8GwHwhgC$&`e zS<1JRz@^_(rr4ngV)jiLPbOf{0)%P37~cH#K>} zu(d_Ri_PaAnJ=!3%EHLY3aA-O!Wdu+ZBgKMRcM2P9^Y8o*nicaUbK3cmRN~Kh-r0{ z$ZMI}j%QDdt4|CvzP-8%a*G6YB@hw2uX%(GUAb4qkA1m4o;-fT7xkvoinFW$!96Vq zZ#Hwn!K*r*N;54(s~bBo*}!7vZ~QvD@U3Bk&@?B8Tko;1Ok;DEZ&oey^ullw@=2BS zmbnbd%>bv;!Wjr&Qsu7JfSRul>VZnba1Lp^tE4d81^&26HEcnd4`WD!X^x^v#gcpI&sPZg^$}<=49AfOsl1WxrNgVT9%XJLN|d$} z_WQnaJJ1nmeqB2sismy}a@75p56?I;QaLbI?lY^Ra+Io%=kwt&GePR{Nw*gjF#f>N zI2^g*Jm~3+*Y|cnKMGK4fO~J4xHcn0pVC=FPM2#9w_SH${^l8DGwq#tD((`GI1t(j znjD~DSQlNYDN8D7$r)&kP%o`WB|120Fa2M4*$^#7Y7;eyN+{Wm7iHUVDAGKE(;s(k z>23LNC9=F^{4Eaf0*mjIexL}&7qB4i!>h7E_edy!+`)}4U*r*E3_?`fNhE~S#=)Nq z2fl;NDf5oylYYNSfS{q8xpZw;SqF)7gHGoR~BxViLDvquw=!O zz%L=^Em>)(@baAD&XqW*Ujj}(Q)yHPN&4${?J6Ww4i;r?|J5}Oh30sf@I^on@$1C< z46AWeh?Kg^eicySb$IykVLM6@<{Qx<376|Isj$6mPi77ip_ch>s)-{rInxmKu^jmgXFvBo%?i!8w4o+B z*-RnTxWXFhpl4+~ckKMynEoe$V8;w^z00ibgB58(pV1TNh0HG7{f9wLt6*BMb3?9! z2!8s>{bka`aaIvUCC-TOA`#9-PB@1%llhEORHmCaZ)67?fO@a#l*=S#4zUGO@Rch- z?CY|0o==K(n8a*vOmcsnWFCJxWO)du#Andnr~3__?ndh$ER#{Nq=f&Tu=CAGl6D0pA_4}TsUIj@UE88rTex7yqGXClAi{cxHQaA8U&9DESMh+rtZPj5mV8K>FT{g z;^MgVz>8F&P*vUlrO7w7TPsV)%==0o6s=BeqO z?peqce$Oyth-hxB0Ndut{%oUYArGGZte)`Y=3|RWyT}=Z`kXhGXfctrFc2RSY&pxX z@#@sT8s0re^MdHF2&RfD9n52=vPSbm4&nJ=#+=kW`t9A@N~&T&KDn-O06dh!VU%AO zb(XhrRY|WQ1lHulE_o7-z^4kg{z|Kb&YYmVM9MrJ=78tz7`+_1LNfbj>Il9zBBYye zFW$OF#0hDw?}S$4aa{>zc7T2Rojk(f$Hnlv2@>T`7{KiBPXOR&O>rFkvj>(r(b^wI z%E6HrRzxp=Rb{Crm|lg?0PDW+WBi1E9v}nBX6(|{0n=!!lYowriUvM%RvzJVe3ZJR zXN13E^wrDnk%egH=rFE(DrzIL=>3jMDw&^1qV2JZ!K;!4wq%eE>|yi({cr>C+_uAg zZT;VlS&p)?&S@cK=>_c#bE*rtQG{kc1c+@Xrh?PvT6nbOsk)ohb|^~1xHE&U$k}Vx zU%7Qh$_FuxCtDdPlK`4umj#syuX45!+?y7FkQ(Y&9yxR)WrJc9SPf8OgH!b~!-28^8(S=FKWs;K(i)a4`6xywzHm5rxmzf{G$K82bCRqZi>8Ajx=d1qDF3#OnqglRnns<^-rvxnS( z(hkjH8ja=S-I(vUV=p7L~VK)HiYk1>J5~FSavlhumsb_)W?SEpB(=o1Y@KCEg!b zM*{XOu24kZ|4Y0b?;m;hUfZ_th%L&GLAhc=a&xcB)VFNDu4t<4&mufKBR1{Kh@s)> z*WIv%0kdT~c8)(0#-Ss+#%yv8Fm8w4zTQW}ktPtsBW~ij#zTJ1(higeXXpXQgqiyw z$KIk!LKoadXRbIl-iFgXST9+yc{Y#KR?4L-=);~zeJ8lwzbbZ0K@*d!0o%LN*x>j;I}3oThhrgZ+TRJjb*2dh_Naez2@3_LQ{Q$a zf_vop`ko`L`xly~#_ujq(- z#QB0=hcxd7jNL_fV$4K@A~I4h5jM)*?IHWl6PE_I6=vTZQXRW8!yTmH@+3iipc}f z@qTf@N1K)9b31GqxQ&FJ*+fa(42A=8+Y%vql@2Un%_#SpI^1XJ?k}C4FqWNZ&i+>% zxj<^m6|OTTT++3EZg4(YL_CH0t^}*?Bpl?rPcm>X8BTfv#w94VR_q1Mmar!hck%&0GP$C#by-t(1S=@uADWMwf%c#@f}Q@tsD2B>>Sj+;E9m zTUT5s;_>R^50!OL!MaiXIsY{>W6kRt$!~OiyB=VLU#i;{Gi{a&|H^z|#L#ax!J0Mw zR^H8S2ZpNg`b2c!h!-flDECZF>KcB&;X^~eCw(xw+QMQ+XEmLOAdN-@P`$0r|Ihi~?4p66pIY|OgDxZTIpb!`uL{wRLfa+Dxk zNbLX`6@y#rEdX@Y2pH4nO3mA$Cq}_3*`yKmN6r*TNMx|++ox^`1)s7XvqAuybD|-v ztaXNlY%U6!Ksb}^WrK1-J3L4m?kD4%(d;+A8+J=JL2b-LsFDkX#YTpm8)o}BfDuDs zxXkm~r*5RA((Wc=snIpBjH~ElJ}|T-A+7Pth5X_vI1Z0Wxl*d0)~}qMq;ZOdpu*DH z%?wxW2Z{L`bxC~cRD41Iz(z9rOWm^$sFgb0QiUnNm}x5?%8FTyQ1{cXwmscoTx|(L zhbBr|N8+Xk&!$W`09HPP6$E00es&WXOJl3;>~_HsBV2VXaq9Y7^&AX%{8 z!+OJnwW5Tew^q5((MlN<>LsTzGjcasSyI22ry1QKIU>#zR>ga7sfi7v$9_-WR$#|> z&eTiZ9AYm9m9GgAjo^WygG}V$1y#OSn#zCkR4F2{`KTtDH2yx1%NAs>x^;5u$6*fg zI{RA{H0pR}Njt&5&?C3IMEt*809SfK<&Jd2VZ$okQ{#s|)tpE+M!S6>tya^zI{GDz zS;ag{EF9)%$+h;i?LyCrFH>P2jbha#0|4KKN@A~<%O|PA?Tdg0cWRq})99e>E)I|% zU|%fr*pKKHXl{(F5j7tLuJF^OJzgKb>P)#0C)u({xyr!A>xrc^(&R2 zvW~#+a*HvIOX&i*@coRX?Rf}B&Z6VNetr!7;L{2U=Sc$dIZNVU8qxJ~jrr?u_wrUJ333}VMmrRirbeW z%|JATV4LetWpvqQ$#Z)a_Y$LTU;eU@#4R73KUQ1tTP!aSMZ7SSFB!*VFo(EcZc82O z_cLFuy?{gZ?QhsOg@(3p)+K<*_VsoRl^is zVuc|}MF+W@40wSP$V1590p@n~+}{H5Q(WeUWS#Ay^#3H06YM_;`y=vi-_F?ek%N;k%^?AN+C0;=_tvtMr!6Lmw z0^wUpwlqC$y_xCB)A?SRS#ZXH%fYlst=0|L1aEgaBeZe2M0}%n+e12@de9pEOtl5g zmFFOCQ9dV{be{(N5%gwL`f-=?*W0Ln<>>@v53QPQddJ`;t4gvizzFYMR1=Ry;`Skm z&g0&AZz~*P+Z+5KT`@G;GeW(d%aWv4rZqCHIbBEz5uuni2HY#`t)v8sxf1%Y*_n*k z&*boSzRuqbn3y&3lH&Qsyr{D+eNa3^t*%%kl4i1{d?G-5k678Xg(SO-sbl5sMqKj1 zWDx+d#yo=SB1Qw-9Gntolb|WO(@iw)XmF>y3AL;Z(PI0hfYee`K^<``susrsfz~kk*+e_zubNx+<4=kBCrGDAgZ_6xcQBr!(%BUi zaAa$s_*3T$nR?=2l6h}UG&x}yJ|{8;xlQgaTB6S5>kA0oQq1QueL3{d$vBOfo)pKN*U9b0-)tA(u=sppFHhcl*ohKSIKVGvjnq2R0QeEvr8Un)X{+iG+@-<&*e z*3=E15fc(p7&6M=^BSrr19RR_NHUOZdyUi4vo7ZLJ9hLlHo$%;a)uyZAQlal!RkDe4kpB)Rm8oR%6e@C7tgn86q z+W}?9kH4siq|FJcp*n%eY z*l!p2Awe*TVKOka#MJY8l4{ZJUa@lf8n}}%#R%7?G1%gjKVlT3nyhUt+H6vIw%kNf zjQdDauCGY#9hZq{XsUyQbesfA3_kUr{_0jLmy{UHZDWO!m4OO~HqYa?dwujq+_@DZ zf?U_U=}4Qdcli3?&>6>l20`*zL3F`_prH0E7_jN|%l?daOF+p@A?P_Qoq{=t27Zsht0O@dI6CRsazzkS7o? zHHK@2=F0vjSMTN-B}8x%LJu=WzX@o;LqoGEB02d+0{an&k(z*>9xUJrn%m>iqU+hf zBg(gK6}b@=9M@%$_7r8XRg;=`u@89=+CI9aJ?X&ldK6TL>F8bFOhQV3lBCq05O?y`KsON7ry zpWfC4zYW(VEaeeZ*)av$nA@S!eDOxIUFebR5N6j06wOFBD!Tl)NJ0NXj(+y>GWN=0&r?q`SD*lbb35A>b1JcGk9s4}~1I-L%!|5d7X} z4tHKWs`BSNh7s`l(%Lecw=Vp^Zx3DA4Xd&1g?1`;-H(726#uy+o+^-Q%ki)K?ko|p zHj-!$=JyD6FM_B37T`9%8E|DnpqJBb(#TcO1Jrd0cgqcCgZ{RbL2Q3Br=#iw$?_4& z_4p0tdQbA&)I3-l!0L){YkSvQPJxCN9sdc_ghDNKQe)odrWSTM@(MkM5av5xaLYTB zlnJsQSDCP<*-dnvp2*Z?8G}$l5DIr5$&xQ9jxWVjsuGwG-k5ZG+dF!Ua zyF6efLZn}>%a3Jz`nLBDBrl-ibXh&RMi*hgI>YX86by!0;|<- z8k@K?+}pyd7r^)@a!%4r$fi~*{P`lc#SAz;g#oBm+F*wz;J?I=f!F=Yb4Wu#9%^7K zw`5h$wK36c@kKEps?elS=65a4V_^TbR9m{1fMDF{gtD#_{}M7o$=^v7!TP*suI9oN zk#BIKUE^pGhZiweyeSdBCjS>jp^#}N5sgvejn4zI0(M(VYp|-1G>F*<%IJlg+(AHk z0&e4sA!e&)dgE-brnyL@UlK)ZXUzM+eogEBaBoDG z9z+~<#{9$#6YalOGP*OqiQs-x-V8~B$bUA3T5BF9br)TiA4S{vTOO9ol)5BrdS7-+ z9xWKZ0}rz9Ssa2Ow5_a=->V5TEvOr@e`^ZTw}W%8_q5~T-J`o7#>#v!ZI9hB@pJ)s z0eN-2A=`Y9Np8`fg3jskxH0*}b_lCi>6)gU`BOZXOvaTXKaVC|e3 z@FT9s81A%3CY8~VpU4fLy0UkJQq~toke3EDS*Jlb?7p1AUDo;ko|kL#+-794!5!UJ z2NN1$My0)NV;B~%53gxJe4BRMN3rEc_$$uLRwyI`Cr;MsFWpDFm5HS(GRGBHINv?K z-nGg|mbC2kDyLZ??WCv(vnn23t-qq1Lq^L0kM>(eExDtn+Yp;}bu$Q+W;NXT#HD)v z9u&HtBcH_krmd9hd>#XYY=_z&21{Z-V6<5rjL>BAIVBj7)FnFvfOGUWPrl$qX>Tj= zGyRli!{$u+!2k9kYp9NF;6dsrzCQt2voIB183f^tB$-jrWs_5_SD#FH(Y~MusU_G~ zzEgJ0u?1tb5NW~#6w1pc(ltBsDYjFtAjp|fpRTJJWo$9&&8xc3D97 z3^<*DEMN+(W5Z4e`YCneg?i3%fdpLc?C#Sm>w8 zV^+2folimJ;mu~^gB}d4q78hN`Z!o$o=a*3`{B^8mvEDK7g)~SJ2?`9srpoI${GA{ zju&J-!P+sfjm?4jrx;{%xsxxK&}11JmG7+q*cVkanG!wTTCcHxdA>p;6jUG^B0j;* zcskZrjE@FG4mAZZG8c=ddH$+g=ZB)O566w8ZfkC8F4e|RMLYG#kZSC8N43Ru1&}&w z@o1d}7k2FnTnZp!61x*oSlXE&MnpTkbcQffB^C3cnqZ8qbM=DiY*?JybwU(>yG*$O&=btaN942aR_WVDo zYYwQ+X7M(Z;}C~`i`uWzPIy|O=2br)UnHM=T^r-3fhs0u58$YST*^Z75W;VOw1KSi z?BUVIF=0NbU8>EvTSWv>8VjL#ZR=FZ+Y0%9B4+A#TUz1t^B-mg(EG2Fjd@>L`3 z*MXM2kZ1YM8MI{*yUj>B6Dd=pW|!gwy&Kfra-Fg$yIZUsRJi~|^5QSwxbz$$2MR+2=@Y-ZUN++*V;F`gDby(xUW~yx6rstxGBe>S$)>%sk6&37#mjg;Z~RE2 z>duJX;I2gU?@)zKO4!<6a!W(K-ULtyt6IvBar4QeL@+CSY?*UpkKW4dZ^jrX({Hfh zS3P<@|H~t0w$abW)*p(fcu(sT^Sj(Ssvkd&WWBwTEQ!|^wSx)L48m=GL1jg3MZ9lR zmxY5rW6BuyEEz~I6W3^aN35KoQb~AtM#BHBa zIcn{G=DU&QG-&1%e$Girb*BNbqE9fT9GY8`jg~i_I>G*V9D<1uBl!lhhiZksoGCgt zg`V`#$RDgjDuW*KJV2#2u0J5p$JtB})qulavSIWo8Puj|sgNS<>+lu?j!BP>nMA&P zO$1i+F7JM_$>n^2VKr}R1-*kW5i}6l1bg*nA3`ZL8ATcTc5xO7;j{HjUkS_{Gfux%F-GR7n#Jp2p zI^Y;iagQtNx<}^rM9@W~t%Sw3gW+l8M+n1Eb5}m+QohyQqo;3-L*J{1NLwcp03T+E z-!g@=Czwhs&2z&`>t$o zl~W^+{AUA*q6waIlCpb(jKsqxHTu0V^NCMd6}h?|4_sU2mQ2a@aZk+bNrtRVJseVM zyE=qF5}wlNkxP6i$_ymOo%cQ|H9L(~I!|j19H(G(K1~X?5hYJMiax_El1|wG(0xe7 zF}kMt_9y5uh@zCV9o^5j$Or<~C0tr_oup{(2OBd!%!JL`JL$?tYv9DHu061MRR{Gw z?a|+|$X`cyyFSD`t@v<6_U)%RYefBOtbIA688aM(iAV*;l(IOyT4#g{#e53lAwVfI z0>)aPN6CdGNkjW#Dw|}Wwkq5o{BRQ>;_HXIWJ6pNB)bd#nbYPht6d43A)3*qzz{2q z@$In@2thZh@3Zw9YTqk&p<2-YR4q-fG|p2tbeuD-gz8G#I8GtE_R9NG1Tfdr%fevI2rqM1_77N&tC`kAm8@IO>};$# ziji>zO#B$^#GJzj^pnZcrR%ZNbV8xkUsGVf=AE%gNmw%;sZrthM5*G6*d$Qg>>Tys zx9(Ik${4R4sPA4z)B*5ZLB4VZ2jyq*!^r#_2^Gbc1cO@tH+h%s3 z%6nwO;4bwP*%k2Hx}i>yYJ^eP0#8s+;RssA)CCnP_S@&Q;AA}UAg`;Nk!7Z3uu72$ z$%ed-pH^bF(%NNL8Jv06m&cgm$xS-xMz zkg)G^js!JMrfq$wldk9_phqhl6UNKmuAr6P!P~4uhmfzwOuHB{;xbjO2%Z)tL?IaN zb9=eYKt&))M;$caV)x~%)x+DGWNNaZ0vfOT#yW@);`}_O>pEP*jq=#AEaZpsYqwxB7h-M*0+hyN=TCe%rG_O(Yfv4rl8$@j=^DZLtP0T5+>Y_iWl zW^wmpS*WblTGuhUyfy5?o(Tf%<`oX(((Gh0@xM^ zwBk~p=ccv3q7ej>u0OshdT{Q`l>mP zjh+c3DXTlW4V_ z53~QY(0RUutD(};fm!F8@V&F<(d;jc#w66})J3^R{E9kP85G*`#ijjH$J1Cjnt;TK zc?!JQdud*am%95^HM6zh@hohW2PSVV({S*D(`GA9&YDeSH@@Vm8hB|};P-A^awqRAh{th?29 zH9nZ1$)}iku@tZqYGnWEK|tF~dZ6Jfjwz%E+Q=efxuuLfQ}F8v8<=>+W$`djUl5gE zpxQ*#CX7?OvUL8oAF{yQ??Z}6>6%(BLlCgRSa}F}{pv6!DzOH0hd|t+K4uf3l3}%O z-BGyS9od$sSY&s|rr%#He_A~Kd#s!d-aZ)H-~NfR9B1cPiiBbM2nXHx zc&y*yrZmV^jqsd9R?UIjtqNC99`^XOl@Xf6zL}wfXV+`S_w+{yZ284;d1o$$&mi38 zYU|lB3IjBXZM(1Tjo9e>hkvpa-*C_SblwoW)QvqU(azM}s9v5(wgmYQD1EJ{B5IWK zA2jZzxk(iQK2+rov&Xe5-;Vy;aL_2Wua|jEUb`*}52A}IjE01$yLk7OE@x&Nq8;Jp z2soR7If;g0+`T6i&l$l+@*eXJ1C)1*`t#a*9W%6S6GXn742;-uZAl!X)W*O&h5&pE z21~TCv3U+nzcOo3-o<4*Z4t(~rbRvQ-JFL2#^L2*A3Sk-Np{#^h$%bYjvu3rkzC^s(Fe;qQiegqtlR2+G%{y(u}>*aiI=uj74YtX-@Ld;-!qCM6$ds zT*HUQpCq}JdDTwO1~7+32}tVJs}6oX%|~#q5e8fIV3Emd=eiN(Lew-r_4DK$uWx*- z)joJjOj&s^;A-4y1V&`#>Z})wXD%AgN;G#!V_x)9cUi0oEChqAc_Zs?{mjg5TS2wj zkM7Dfv>&&q(L*Pmy);(GE%=CZnfx9&Jv7~t@_1Iwk>xGeW z&OogF{CWK8Gqs3r=e9oo@u54W$K&KcHO5;9aWw3`_q(S2AMOx=FqA@;Gur0}WeV^; z8glMEpIgt-%!k*lmE4BDD8m!Lp(aBsiVUm!`Gq4lh;q|Fm)YQIBIjzZQjYNRYwe3p zu`qOIGymz(lvR0RqIQ|1T`AXnkV_U(^7>TRsEn0fr0WhUv z{=*s#$$q!#)^I@l`;^iJSO#i0##TOA)Lr%K1V(1c`-77GU%|@tVg2WyPx=@4QnrFf zo?!2fau4CDohinqhGd66H9nmMBCI8f84ZkOPda=yihI#-CDX0GJbE*f<3VmX<0=M2 zX$k9Bo1$D!NrrX{S~&YB(96iHS&XQ%p$vTAkj*Fe3A=K}HIF4)C{7&KC)VuSHXmR- zrQ;diu u0IS?)BZ?2J=^T36VLsR*x?D77<~--k9g)V&zvHL!5`)rw-C;7$AbojG zz^gF8UiNu61=n7X|Ctm2uz7Ig<40QKb~S%mzY%hlu&CR<94Z0MV-lLWz;0z6;5N#& zI}rKV^Bd@hKu&KV?IVEX5rAe=_qKWH^iY$344;lgd!W=?ly}2U)8)+O>8ORv_1hwM z>hPTUT}I{`iU#rZG%j`2b?`PQi5(mS!IA$*e19M~og!Zr>UYj~;OKLa7puB|?_yPz ze7$`OK7WA46~CPdG4+Eli${s=-#1vGyKPpthP^?;reWYb;P}*%4Mg$=`4)NwanM&M z-vdP%zH#$T3A)|@6F@Zs!T`FUqSFrXdoQ4Zj%qkWdk%!|vd@`2EHL(XOij~FAav%{ z?|Ln*V4_`{QP_EgT8*hyFG}#jkGN7I`%u^ zksi7R+#&;^;Ls+oNd_T3SZsd7FxzTXWWIDXpUZ6bu*muOOq6|qpAfQX-HZ~{LG6F0 z9MG*EGOKngNZ?h#N=9K%8uWc3Ct1M!Whu@X0FGXNAfY#m)Bp;cW%0HNa~uTSCH$u6 z3=zWm;J_d~0WS1KlDn@t`GBR&(VgOLiM%kse5{#6+2qdAwh;RjZ_d!(!;G7M@73(R zs~vQr49tH9??0#^o+zj0Oht_jwfIXV7JA>)+iMAW9uTC=Pc%qve!~-gkE6&1frz|6 zA8gyKBg%}ZRq?y+5x^p&HhX#YM}pc-Gj9kWK3p$6^MOuf4}3&UEO@NFhYbd*GLtM#|$9&vk0t%ZL8>b=VP^V1WjnFDeXr-Ca+0P+}^|^3$g^{ zV}&wcX!R5dxMJ_yPL-kv4mjhk>`bGBfN-b(QjQ-lq??Wi!ee0Hkby9YMLqagsDXA& z0d=<72)#Npna6wFnD1#|bGO_xv`+Pe>E*5*Ns?%73^VjtZy*UoYl|ipb_f5zT!4R& zm!MsOK!Bwb`(!@BM?yOY=O(a&`h&h{DJs-JnmT9Mu{`aAtH8N=Mi{PpkRvD*;4Ww> zCtWtiz0#3s_jUMXf5e=kEd?GKAD^WPe|BPj)`n|M(6*#&zztqXi!ub?6v01uXf z3I_23_!NHYIUF7p=0MipPY|SSYwp52!}A=Ps2^)zIUzhR<(0qG<`3}DUEtrH@gY3NZXtEljqDU#O%$mbo$OpLx_(q!Xq z);K7Tujr7LwF(ta{tt$6$Ks!uBuIK69_f_wMeC>Yb<{^eUY1cth-J-$T!=rtI{!Dk zYiIvL^UIm=z)01!d#5Y4Jvquu(;NA%Pv=V0o5bWF!l3d$j&RV$pm<6V7nI(Qz4`_I`2D{r)um=i$Z3)_(>6(-5d<|J^*I z2>&+&@XwANK(ogxsP_9?`23$gy}|r1d2*^OfsT%byVeG&_&?2nzFqDAYpnRs&Y{eJ z|K{cV_q4vq|9ZIp^ZN_;zt8jM=>OjuysLZz`>)(UGI+-&-}hYU+yeaRfj;EFCdHxi z{gU6_FY2@|e}t%)wEpYA-T$8^q6J}J0e`o)-bK-y)^@T1OuI+*bnQu1I7mHjiu5 z;Cb;Z_|&wnT>M!m5ADOH;f0gzv+&zC|3&2-$kce7BL2{^jPkzQp#HYz^KZp^{Adaz z?Qf2=uL1=z>GK@pE4MQFi5thY3B=-U?Iuc~)irnYwQEWMsk|?gLk93anh3fqcLi0S zD2g9^NKqU^zEx&#mq&Le;cK0TKlYFPEi<&^nkP1$UJ%RW#K>sj(fko+z@euE=LwB$ zkpaQYyugjKGut_nW-0H*Rp;O7tsl;L#4GMQmLpO*FIr!olrB#A{W`sA9vI#)_qHhp z7e39NKfnA;zgWkI`Fu0}KQ!7u4@~rW7yIev?%OqM$%Ycid&FgvH0y5q^LW(n0S11* zR@hIysGL(;Vbpcu00-@-O6>zD+;@oJrsur_TkZh76;g;)J)Wf7lY~owdcsxy=Y9RL z?>^_f%=Y6jZy!*2H@8FWb>TA%vHA9PFueEQu45~V!Q^=n-(HO#CclOhlEAF%!pj|l zy?xK$XlK4SkRLJ5xc>35Js}9vxBqD4cwg<){@ZrH=|$$b|GV3Dz`Gok7h!niFBy}% zC&ym-dqL=PXOYw3&-JG$xz~+HKdeRGGcDs11AfTLk@jsm-1>BoJbmufQR07w;|2r7^!Bl_sL#Mr-ZHRTW&~caQBQ$nmKclx*KWk-w zVS4}pa1@x@jqEx<_rgWEw`2feWxhvX0HfSAKI!N?_UV2N?#n*OIG;FyplN82zFl1K zPt8B~TElimQZDu&mL7T?w3G>Iq{PjpAg;D|eG-l&6OA(CmG$i5IVH-K5zbx{{-W`w z>u*=mPm<3p4r8l7Iu8gQP-gJCId2({!M%KO`EbD=!aX)TjW>jOGg~m^mUj}rf0Oc6 zg&~?x@L;5++!scx*fcv#2u>(wOSqMDm(M&_T_Egb{761a?n08C!vUZyonaU&4zsPf z^#!fdKXo8*C@dKY0x|fset0%*tn)qlz%4Co%;)>RHU)>S^v&bAaKJ_tu)xzqq5_}TM2R3bNYoM z%EtNO4@ug50~?uUDq1iW z3YkZ{h23sa$dY5x%s%cP)A{uhDkXmR>NZ28Dq#rBF@*FJVpo?+3l_$MJVUNEs*5$j zT`{cl?fTL)m@^I+RP-*CdurUZ?P8JTxn5)Mn|>uJAL4D<1`vVTs450VVUTD1ANJnz zEw1I;8pR>FTW|saLU0f6!GZ<|?%qh_?iwt(1cEyRhi){uI|OOm1C6`Az0W!O?EQQ1 z{c!()`-Oh!XRTG$t7cWrnsbaXH-qoMF|-MaL1?L*fgKOOOsI2kbE*H?QJ)D(x-DB@ z*zfY6XmNbjJ7-{tfmZ<$gg0B+-s_GI{zwoG#2MWr)}G4}fKwOiHgheSN`Jpsz`US( zegdw9b`2eISr5?FS#hqNb|Yqs%&k(eWPQ0L(Dw4WO& z%GQ=|$_dCFpPgE@c(&_!=#t)XkjEGi{dyf&J_$v;3p2;;ApAS`)|8N?1bTy7kfUE4 zNe~|e@NL*`xWglxR5HFqG8NKpp4fZjMZ^v-Qj3*Sp89oKt<;eawtSm6({?zJgU7YZDI3pj5$c@i-Of6XZ!y6qu;W-XT2e(zZT&1BUBDk$BsUcxf_1 z%c7C9{xiE$kKV%`}PEWd;av#(_Tram~HX3cMhkM&}J*WX#PY# zUih+ksD;d4{dpib3#=$jYvSYq)cJHgC*>B(S_R%au)^Xb*xyARB=`MuT z6vcznqf!12tw;W3w}oaem6>&3yj&k@Xx-#-K zPH%Ad=9A%vyvO5Rn6_VnB7FsycHh#+rfPf4wEKt>N|gA9AtB7v&nk7p*tR&rA1~7A z*~&gz?+R0N#yy><)7m(@@sk7zP;f+$Pl{+v;H6;8a*&IhvKSgguMnahPm1L9<=EMkCT}Z&t3K(gB>KA%TVIz9r@u%PEaMYu6qy|4+b{$+sz}TNus^)JCRW7VCTclVq>!5T@ zhM>Ey<%av-ts?Pz!u?=B!u8n|kTW#MU-GD4+!%#d98w&A`uw(c5z&k}vbD2MezJ(* z&{RjpJ3S_<&(=7HOT_l$uPk=tlYg|42j89WL(;e(6ok6&9ItY#V09L~8nHk>|(32D{qqa;pEjgHdbMIjnn`+FAl zm;FC6&ldsAv+ai?753nA(~)jtxRtRZ0pl^l)$QZ4tJh5&Iy5S1yCSCVp-{%%YPMb6 ztIO1lpv@<_)_rvdC;g0RDILqQyIP4i#}cm!`q-$kvM~TfEB<4UC<)-Ls=*Dz*CLuQ zrYWrQa+%T`L6yXL&NL<|rd>Kyu6g+Md-*eu2u35p_sEcs!d8A1-d|HxKON5MppT9j zqwu7M*-{4tHPWg)-d#W0y}_FCqjS9}w5GJtGBxj`TE5?|Bzwpj{|b~ zAKtEnw?O9o62#-;X->>(`t-Nbr9WQL4Ays-p*n?7zQ!og$v7GYVw zXGsThdp09ejDXC8eW(AbDzJU?GZI*`gs)ZL;@MC5i`prI^wNs6A)^S?Prgo-6X^5` zt}E6pGN2FN{qYBCVMRm1HYwME22QVl*W>mGYE27r?itrjZk2y7 zQ`LfatE-AjnG&nGh>1V}sk!N~_Sl^;G z<}#hEAzrQUUk*AX`P2QDZlawQ`-1s8vzt8A>&TkZBc$~DG55wQ8#e-2T~WWKj|-`b zHTdAhtooNTO*2-N2VX@yO&=kB1Jg_A>T7j?RB_oRrULW9-L#f$yQ{|Nb_u&!rn}@M zqfcMZJ-_*TqWpymw@QiORBG;BC8vwW)wflss(Y0ZMati(SO7`uFQR8eO__w+Re?7~)n@cG_e=}B_jpfBPSbW{p_pvs4< zi~I&x>0BB9qde)3!I6rN=YpD=`}>LO`+)livHOXNj>nmiDTmi|>g)*OQd#z6%lvmu zLrse>JeG3l;VVjdA*~K$kX{eG2&P_xC)Ty+yCJTt^s?c2>EaFI!}3v>T#;cKwn29A zhf1$oOByr_0MDE3U@ER-ZdPQ2?2$}8nf(5aKTfJ+ey^!oMIfVDAl^tgxd3Gkl1TH+ zfvnMBj0$H3nn6r|gQzKoBtL2u4SCLlK6T^WX??8Xgi{sXF0k~1$c^3GmF*)Z5biI1 z2Cmk#dYs~unyedA;7K!)2axQe!nsdB+cPS+ zWnB5$!{LU1fm5p>@`U=n{wE~h%c#hg#WtiFr8512r$f?bHq(V9%2d+Q%*=C*!bH~tdtoJ0p?_EG)xvK8(t>Gkm#r5 zb}uFtU8`d zYI5E_GBZy(oqiantP8;W+sI8G?GMEGUL0e?1Oklma@ikq^jPujI7nB=XV`FJN>a?(;0K@pS%E!OynO^e2(SYB4@HFI{ z9{j+yDW&BsL~DA@s=yv1)B2#RqthKt6H8WPnH&;PnW+$Ls^`Yge`7WocoIY)7%mWrz ztAD&#>st|US?SSoI))$V5eo*YTepcn#r&J?kH>waW;MaBku9FqxiEZ5yFTI% zY?PE%y(0Ui-%SV1@eV?(`dOLYcAQ+B79!I^(Pk+}Ss+IDcWNGs zX4F{-;@lBFcB`2&;!FEuq^MlimA5ncWF4sgM6^0X&clQY@F8ZjTpdj}T(5I7u*?)& z${ph+ruoH_YaV0_4g*16AC3eF zp@|FAAkm1Q3Zpw`70mD7D-;elm6b!mP;kf5{W(kG&iRYseZ$t9ibGS1i-9>NujopotN$Su2b@6Ro!(dM_z_o#FV(SG=IT>n;+; zD{FB|6I;7B;|6bY_p=5gLd%&t2SAhHxT$YyAuWwrwNIVHNzQH>+i|Q=UIX&wcyqIy z1Dr_XXJKDIh|yWC_|g7zWcZDLSLy*4qDSp+xl8E~-c5<)EX0ic6W{`LA@ggpguBP4 z@J2d)&(P2nCd*QL407E1f+xOKI-A+tb7dVE8bN;g{!^qi-M2r#Vzc5KaNN_jRhc{% zFYHMV;x5YJ;M^lJ?8o%#rD!9f3;j40XWSCcsowS){gaM<6!Woa`OOz+w}Ge%?RZ&U z?Wc1JfTLkv{P=;}fv$OP8PM-;KM*P}-%ks7o~uhxGLPmaom=s026Ki+QZ0j@k=Ur| z{qTLH=F!Kxm4Y?}=gwLi5JF_Z))VU~udTsnCD`)3k&jOoyQuWN{7qC${Jm1x(F_{B zuUG(EpBMxg`ppJtddLw3`c2x=CSKh2Ary;e=4;*4C=*a77StH}F%loVNvrn~^0Te{arexVBe&ob=3=O7)e2vC^wYy>(HXylmDJL$N1 zS=o^v@?5g^ywLIV>E%Z3=~}s24{5&$-&QgA&%Es71e$sl6mnzG1T!ZeMW1NNtaJ%N zwTn|8eo+Xk-Lw++JMC^(y9`8X1TCH)(OTapw%g6ew6bBo1XG@kB&ACEDn0($XN|p z_U~V-qX1SlO86xZS-C$-wSFbY7eu=Ld0MlDj?yUSz(-@R?DBd*`B48yg=kv!5AZ7d z8S6foL1x9L5;W<063SB$N%TMa(j$s__(2Iv0rOOfXvAaY``thqJuC~m3_NSUDGII( z*{)Ith7`3RhVadFH_ALZr{9qxhE2XR8sm9oqgcPPQM{A)Rl#{1)wtYgzu!VL-oB|x z5^;~7qTU&jDc3z`Rzsbo%TJD zX0^6vl>VCS`%z05$2@3ckEBa?n!>}(qK^M%I~vk4bQ1!4Ktf|zof_!yQFeA z)^_lv&I6PUvST?7hI%Y_4*7qw;3xmgn?hxplGfR-;_6=Jpwm7eYy3rTXTl+CpD{~Z zlY+wo0-1y6X||*nH~`peIf$y_G&I^e%N;2WHT!ln%GdZHnJbd{tCyd>xTk4wTTXUC zU^r(B;|*Z70Rj`Ma7FavQ z5x9uC%HSI&A17KeWWm@~;NQ8QEKlEX8nN^x^2Wt(+LqkY0zK2DlO7Z&)T=a`w>vLQ zVOm?n93$KxUoq66uA9S&)6ZB{YA^iQb(}RGT_eQiX@6o(`GLn)IIB-9Upgo#L?xh= zU^8)R{5!4Y!1LE-<)vcU--;vnY1l0p{6`<8dp%}l>a69;nt-<7vo|c-H$VK7u{c-M zrJKzVY?!^t5`+KYTGm2jgDaZ($5+B-FkmBFG;#^!nPzS zBd0T+9>J)?(|624d?6Z>YR9j{i&0v8RhNuE0?)=Rv4KbD9y%$iL*@wI>Ze7D>oJ6f4}Y>lkQWRGjZZfv*ehuoCjBQ zZ60yd5eL1qh|N|BW>Al(FDhkDL?1gYZq{o>i0u{7=%I26c{l2tv}&MS*y>(8oRa)* z&f8Cc>P2@RC(*dvVV@J^r#R%bJnh&5F1M@ir~tQCWHh4unMC0&{dJ!|LyaM9xVJ69 zAS52alQKtHf3wP0uFp61UWH6|$8D|MJN}n)Wt3ZUwhW;4yJCLV7O$+Yu_0Pj7N2Xm z#+RqP{NA0WGCs9fJIfOT+SNW`ZvyaTwQX^E`R<|R!R5RNCyC&toHmV%sDv?K(WX^{ z7Rkq`R{I8SIy<&+MBagk zl-~|usx{ztXxa(>;qLeugC9%oIvcs)KxRQn$|xI=?cKnr2t<)`M5*RgqDya$q9-?j z0|!vzGc*|CX~+D_`j4r6R?c%~`fes9@K;l?&py}L($`;d-#?uxp2P%1Fp#D#scTfR zq)ihpxq`LoGa@-W5}7s{qrmAlc9y9Ik($PbT!*6bMy<&f*v#~SpIu#0%z2ksWi|=F zl=7lW|FG=>1pbW$n4A2tGB;IKkfEbtHxG`9F5OFwC^|h@sOw)t=FHWIa!h0@E+_x! z;yhX8P}fN8?B$>~SG9uthR;eoh#!*qz}S8;-{w%QoaJrDh^02KPM^z#ur_8wdLV6N z{rv#iZ$Q~0#{H5R2XbG^ztQ=8P2i?I_n|UOU|}v(_o71zxtQ)S&+tA*x@dud#K3iU zp2zeuQI=qivJdiO-XOofHUoUbSQYhpwD(eV`NA!7jEoN5=B*#y(o|lVH6opnKCQuq zWmdY+9}W#WzqO)j2I1@-8U1eX;O{Cpn%A7e)Cu$IievuhX{q*VH@o@r~hWP9$Yw+xs>(NgP@(KMKoo`rxE|4e6Q?Zm}8A)7jf zogqM&Ec7vK4jR8uiPuwG&2`4c1bC!%(7V5D?K>^v)d6yMLs6}Ko&-ITomXN-s8{`pCt>M|lhkYB`$#_8(fi z4K;#dF<=X0p%;fNo|Gzhtj$1AREm7Is*!5C&1o8+w)+Hn8f|nrQG9*u<#hzN(kMVaSPtaSA>6*9PjB=Ak43^3z}vMFs+tSR}LXP>AKmQgi)gCDFv=d)bs zVZ$kD*#+%R-rS={9g_{ z6vwT&(rvuy8yv3=eR?qd!CMTn8^03IrebrjMIw`vC!x#hj=V+V{CcuXr!!3K=f-oN z>l8OLcUmj0h5e~UTn}YENOf)l+eho^-Url7@?-42Hw zZr;0_`@VN6XOHO_GHgY|I0B{{n+80uQ8zqfTbo?VB9TD3jqM{VNS0+#E#5?;_9kgED{5J3%C|IUs4BQxP zW+=sAQNr|4#uZ_5xX<<3k69#V#=PT2Cc^lqgWuC!vx^S1^DYAOC1VU_3_iiffQ+Sh?({;Se2BlN2FG<@)l)jW2Wm zG+U4}rM51jov+ukWiO8rTq2ppD6ViSd7nm#v#u;ro>k4-UO=1VDYbdz9Tz2=;}2AA znj)RZ`O#D7y3)GuRAj++9anq)M=#?;%|n~;95_$s@=Q~TSUodwsRQklH9aOqvaIl8 zI&U>3e8}zI*E89-?am92KOZ3Q{BTYquWP7X zG1`2M9|PtOF&R+i0=bKZqgAjwH=fBaeo;29Tu1Yog_=MhCRDAoRPfdp^7SwbLlTRJ zz#B~V*Pqk+Zy|NxV6P@3{%`|}62Qxi}UxVY;^Ko8`*l>xEMj%pBW#N*%K&ujNCy;kREp?Biuu8SpGFWyF{Q)(PxSj z9QkPh=HM;}ohE^h5S%jSpj(ORyZvM)US}1C!Xk$2@j<=)$>}5EtoHHFJ!T#Q7bn%*$<2;G*d$XJ63*x=NbXx}fj6K=6!c=msx z4>j(9-cqU^WZ0_bEvNNE)EJhzO3Cw@27cB*JVv0`-MVb0M4j5QCSY>T*HUuYY$K6g ztf$aBcjIbu;8epjZrj17r64yr+=%WfaMlBKpQ;FZgz*eu$TD{RoR&>q!ep{Ui{0Lu z;pt%qNTm$t+585zTpg?GVf{*EJ5Tx$qI$zyU{u29G^t3(&~I1NLpcwt&%G|vk55kk zOfSf)=#mjyp|o@Y0{Ia^;Jh18TmT0~)@YQ8S)4)eR+%zf+QFpl5%gQc+=@~|&s-J0 z-KeQ)OGZ|K2ZeSw#fhRo#KF*3T+Dz3EX%^l!x^H}l*Jq0b8NZlGxj2y+LdoXIgXvi zBM0j2F@04w_KWB?d--RZ{XWzZ3aQ$2yb$zqaWY~M(Rbaq+7#HGQ>7Y)uC4>7!4~7#!JG$C>U^6BEQqJ$f!Iz=nc7eYF$!{BNeXBTYw$ zqAU>uys;qc+Q z)s8`O6m!_-(#+hNx=AK}E@7+0@842Uf4V)Ic!?RrD{u*4S*IOLHrccpS3Ul= zDX*cn)szP7SnrciRz|D#I{iGje|NB`i}tDbKIuC}M1r;P*MnR2C~o5b93gHekO!)| zP+b!4MGp^S#1#W+7i$#$Bk&2NO7CR##@CCKV^0?Op~vQTN-@%}lRMm13p9Ki>KG|Z ztrLVuAA%HBHWSDLwB5%#iTRVziY4jNK^fy!1xb0v*SAs>6;2B2T&`|0N zbCDIi8nJAG0hX3XDNk2qHpiO1 zVfk%#bt^7wBe9OhvdQ86l-UtN?JT+AlS(4QVyazD(b4u|-wfpoJ zX6>|UG4@y$uHz!Pmw(d$go@Q0eXW}AiL0QNlL}NLfxuony00IaBDOZq(mZqQjeLhxyHF zmB%02TQ})clcjD0d;jnnHxjes1-XzcaQJidssZ>0qNSvD0&c~CdEVp{iM%_Iwv$y4 z?t13gK0V#x*X&zF5t-krY3R-^!#gS>U(G@q744khEG;`u{MV|EaBX;Egsy}n?(mr* z2BRw=PoSs5I42pLcG_4GVm`Lq$?BM7)`5@hHQ;~_n|v;XaQN+4psprAUp=p0YiyHE7xX|M$T7Y+>IqFFu(bWn9n0ulvdvCk zPzP!7NJuwWB7{;TChr8 z7myGIUrs9<8AYE}GitDr!MbS`dkwo$ zb#B_1{U?6fo2z(!3bM7FFGJJB0bd53a#5>BYp}SEp$^uCM>nJ^tdl@~3~`bft$MVcG(l=1MP(JBU(%B_#cY{5h)_%a&gG7W zWxxaE#%CvVY2R#T+NuG3Js0q`4ZF7%zfx>eWZao(78d4e1VsBXjrP@aH2gaGL9 zn0S>iU2I{xqY0IBIqc^2=k>y`oRI*0jKUGLUAE;XYUf9VN#)>5X&ii@$uJ{7fAnki z!}Dk#c%a}aWm9hPCm>00iB1Q*EqV~BK@56lP1weavnqNUoR<|dS{^zco~S@>M1hoh zkyhiULNg}%uLO*-odWM)-=@X|xs+ z&%I>#0nx&=jrRXg)|=6zPrf4JFF05Z216v0NfDnt&~?U{KOo8_lFK>mbh0tSi70|jcBy(gpGW- zL1vkgTYD@s-O`ehS^79bAjMs$@Q+r<(_`&Ky3euXseNUn`oVf0m;2F&Yw#NX8gvyp zpV5|e%*FTa16!&(cFK;Vfv(vq0Wc|68x9LJ1 z4Q@o45u%)x_mRKyYYA4$Ec=5_9d~A0d^#25F{NtuWC@fwP<1<+a+%Q&T2NEcu>q8BzgNq<$1$8sMbOv-`9fYOH8}c*pFQy7T^2I%jFYdJz^K~$}u!M z_JzLC!#6eXF4W)BrB?dhTncKO*n<90&htsMAPntAM5YX{w7U%U3p%%}bUeFP3jrgH z#g->m4a~4x;5QkYgrpP~7QiQts~#~T?@(i}DXlzh?1cXX|5^)$IT)V4JK=dB4wr8| z(Qz|1liy4B>p9H6^Qo;_jABQoqBnjUw~E)wwVNS8q+^prN~WnT4}XWF`HUnS%}16A z$`Ut5Z&qdUvvzc*UsljnfiseHWFJr~3x+4><0OR}njM}hK;y+ysJ<>6ZJ$2{K83Sk zL@Gv<9&ILII}W;{*Rzp-K@Md3odTK_tuOYbPC2WqTTCItSfj{1|Ed=e>Dm&x!3M>0 zYF^E<9d;`dT`IM4XHNmd&)eT*YeFW*#x{PFvmSc(~s6sc%MM zqFI78Lmc@Vl;0?3#adTFJiq}r50H+4=4O8Inoqz5Z29HO+GR3mW&8Z+-iNJ~HGcj*6KchxR~`J>otTY8du?3LIm)M#<6ARRlZb zFA48XJ0WYWPO2A^%V4L?^rmDizzv4lkTkO~0_a4LzqkQC)QHqD-B|RySKB?Zs()BH zsx&JxJmQBv=WY~ffLmH)Jk*Yg^ATctONoesj)^rb1dIo@aVWHEX2Wedc+WTOKdK45pP z^y+_^DQ*p8(C^-tNcaD->@Nq<(|y2O(#ARI{MWHRpx=slKy8^Nco5I^G;*x0OS=4t zBB6}KfA9ynQIiqfx%%z54~@N65G+;r71lxp`C?_0k38k2J{Iwdb+q?~Fo&0PhK9?& zl55ZyZt|TMHodPPW3W|p7y0T_F#IF>G+JQKn9DQ9$~Fh83lW^1GAywjG>4W}Hy}K@ zRqd0qA01gJA8#VhW5G>uGOZ;|>tO%l!416epD+!75Y^ZpW|{NdFRwSvxITD-i-3br zX!TElt%R58*>HNfe#+w?iY&*+KDHi#cNc?RX@4OxEXzGixtN;5dN61jJMpl?Dz}a% zq_2-F`r`3tnopC(fl>Ada?Rhl_H^tM@BnahynY&knZ^^~1`N4LRQSe=L(f0uAfWF1 zoKX<>;EnG}sst}Z$af^-52F{eP=V5iR;9O`;YT69Cj-;&e&$nS371>_VeGlsc2Gi> zfN-e|ws~cmKsvv$*1c0LnB-&Fz7B8R8aRxVMC7-7MRCXllJj@1Q}|XR2_zKGKL+*u z7Rk=`rUr!)3S#2_grT^HDEa z{JF~HQPeedMppNrL~$&eb_;Sv%>PX04raPNYQDwe1ne~kyLMj@ooFxwRJ@0cGz#FAc5%VRTDTB@Jhi_$ZWdj*4wzIpDTdE zczbLtqr!E=4Hw;|PG4#TP)(wTqr6WA}{&syj5&B^sO|S#tk`{5x2G|FwBrsOJs%=jY!O*Wv&X4+lFz8RtQ5B953pd zr&4w0FIC8{?esXK-LpV9k*H*kt8-HDlCK@5JtL|TGxv4g)eT%H@DOLBTfoYTX5`tO~ep9n{0q z(!ndMFIo`JAUP~ts45fZLByG>YdmF*a}waEo_-^r=2}(=hia_axGzT}YSPv_3hq`+ z`UAdMv|&V?Nil7C&a>kbot9^QH-(Q2B;ilD;Wm1YTT`CMwzRFvNawJxd%vzt)@^5^ zSRDDwokT+W2wZVreO(-=xdR%_mf}C*Uj)qa5lPr(C==2d5drIAn?dK|%}f0WY$X_5 z?Re1JcTZ9OJocL|`RU4~&xflD>=VKJqoi*gz%n*QNGCAce3^+@HXBFk7nSIa7kiD0 z>Clgv@9g4nfZnxnxCwD3m_yYzR%4p0ILN%Bj8Qoe^{DlIV)5+PG=MA+5q99ZRlbf|STJHn_q1XyHq)jj9{>RGbOYY~QDovRQYjwK4bjF|5Q7R_+EE zn5lodZjFW;;Jll1Vu`B+Q5z;mym?Mom8*#*8CBzmUC%COjC`5@HOeANRi@VOTSNS3 z`LaQ?lW(i30k>ZBuzISx5=_+K!Za&3u@_8wR&Omz-KZ6G^8hZ2##F2PS;ein0=v*{ zI<7|Rxe=BwI?Ik?%FiaNxV)oCA1Yfx`~(_mM92B)DHg+4U7Yp%!ajf$rp9DpzgNOM z>7~$Ryx4uXmS0-4qoA(_7+Va?yo>ph(yT7Cm#-cDDJ3&PeEJ$7f5w0sos0aGEb~-SGLA zevg2t`3PEp;^>)DNqH1)p8mLcGwn7_l4aYpMR`7?^<^c6ELT;%LM4ig{`_!NTE8Kx z8rh|`C0HgWw^#tM-om9($a$mmma3E6S}DX8R+do4;qxF;U9ChOJ=GaQ((NX(ICC-_ zORP?>NHlY4Oh!f2Fwn}n
  • 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 literal 0 HcmV?d00001 diff --git a/ports/cortex_m0/gnu/CMakeLists.txt b/ports/cortex_m0/gnu/CMakeLists.txt new file mode 100644 index 0000000..62111b3 --- /dev/null +++ b/ports/cortex_m0/gnu/CMakeLists.txt @@ -0,0 +1,9 @@ +target_sources(${PROJECT_NAME} PRIVATE + # {{BEGIN_TARGET_SOURCES}} + + # {{END_TARGET_SOURCES}} +) + +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc +) \ No newline at end of file diff --git a/ports/cortex_m0/gnu/inc/fx_port.h b/ports/cortex_m0/gnu/inc/fx_port.h new file mode 100644 index 0000000..ad09054 --- /dev/null +++ b/ports/cortex_m0/gnu/inc/fx_port.h @@ -0,0 +1,25 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +/* Include the generic version of fx_port.h. */ +#include "../../../generic/inc/fx_port.h" + diff --git a/ports/cortex_m3/gnu/CMakeLists.txt b/ports/cortex_m3/gnu/CMakeLists.txt new file mode 100644 index 0000000..62111b3 --- /dev/null +++ b/ports/cortex_m3/gnu/CMakeLists.txt @@ -0,0 +1,9 @@ +target_sources(${PROJECT_NAME} PRIVATE + # {{BEGIN_TARGET_SOURCES}} + + # {{END_TARGET_SOURCES}} +) + +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc +) \ No newline at end of file diff --git a/ports/cortex_m3/gnu/inc/fx_port.h b/ports/cortex_m3/gnu/inc/fx_port.h new file mode 100644 index 0000000..ad09054 --- /dev/null +++ b/ports/cortex_m3/gnu/inc/fx_port.h @@ -0,0 +1,25 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +/* Include the generic version of fx_port.h. */ +#include "../../../generic/inc/fx_port.h" + diff --git a/ports/cortex_m4/gnu/CMakeLists.txt b/ports/cortex_m4/gnu/CMakeLists.txt new file mode 100644 index 0000000..62111b3 --- /dev/null +++ b/ports/cortex_m4/gnu/CMakeLists.txt @@ -0,0 +1,9 @@ +target_sources(${PROJECT_NAME} PRIVATE + # {{BEGIN_TARGET_SOURCES}} + + # {{END_TARGET_SOURCES}} +) + +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc +) \ No newline at end of file diff --git a/ports/cortex_m4/gnu/inc/fx_port.h b/ports/cortex_m4/gnu/inc/fx_port.h new file mode 100644 index 0000000..ad09054 --- /dev/null +++ b/ports/cortex_m4/gnu/inc/fx_port.h @@ -0,0 +1,25 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +/* Include the generic version of fx_port.h. */ +#include "../../../generic/inc/fx_port.h" + diff --git a/ports/cortex_m7/gnu/CMakeLists.txt b/ports/cortex_m7/gnu/CMakeLists.txt new file mode 100644 index 0000000..75c7953 --- /dev/null +++ b/ports/cortex_m7/gnu/CMakeLists.txt @@ -0,0 +1,9 @@ +target_sources(${PROJECT_NAME} PRIVATE + # {{BEGIN_TARGET_SOURCES}} + + # {{END_TARGET_SOURCES}} +) + +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc +) diff --git a/ports/cortex_m7/gnu/inc/fx_port.h b/ports/cortex_m7/gnu/inc/fx_port.h new file mode 100644 index 0000000..ad09054 --- /dev/null +++ b/ports/cortex_m7/gnu/inc/fx_port.h @@ -0,0 +1,25 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +/* Include the generic version of fx_port.h. */ +#include "../../../generic/inc/fx_port.h" + diff --git a/ports/generic/inc/fx_port.h b/ports/generic/inc/fx_port.h new file mode 100644 index 0000000..d4d3863 --- /dev/null +++ b/ports/generic/inc/fx_port.h @@ -0,0 +1,227 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* fx_port.h Generic */ +/* 6.3.0 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the FileX FAT */ +/* compatible file system function identically on a variety of */ +/* different processor architectures. For example, the byte offset of */ +/* various entries in the boot record, and directory entries are */ +/* defined in this file. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 11-09-2020 William E. Lamie Initial Version 6.1.2 */ +/* 03-02-2021 William E. Lamie Modified comment(s), and */ +/* added standalone support, */ +/* resulting in version 6.1.5 */ +/* 10-31-2023 Xiuwen Cai Modified comment(s), */ +/* added basic types guards, */ +/* resulting in version 6.3.0 */ +/* */ +/**************************************************************************/ + +#ifndef FX_PORT_H +#define FX_PORT_H + + +/* Determine if the optional FileX user define file should be used. */ + +#ifdef FX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in fx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "fx_user.h" +#endif + + +/* Include the ThreadX api file. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" + + +/* Define ULONG64 typedef, if not already defined. */ + +#ifndef ULONG64_DEFINED +#define ULONG64_DEFINED +typedef unsigned long long ULONG64; +#endif + +#else + +/* Define compiler library include files. */ + +#include +#include + +#ifndef VOID +#define VOID void +typedef char CHAR; +typedef char BOOL; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef long LONG; +typedef unsigned long ULONG; +typedef short SHORT; +typedef unsigned short USHORT; +#endif + +#ifndef ULONG64_DEFINED +#define ULONG64_DEFINED +typedef unsigned long long ULONG64; +#endif + +/* Define basic alignment type used in block and byte pool operations. This data type must + be at least 32-bits in size and also be large enough to hold a pointer type. */ + +#ifndef ALIGN_TYPE_DEFINED +#define ALIGN_TYPE_DEFINED +#define ALIGN_TYPE ULONG +#endif + +#endif + +/* Define FileX internal protection macros. If FX_SINGLE_THREAD is defined, + these protection macros are effectively disabled. However, for multi-thread + uses, the macros are setup to utilize a ThreadX mutex for multiple thread + access control into an open media. */ + +#if defined(FX_SINGLE_THREAD) || defined(FX_STANDALONE_ENABLE) +#define FX_PROTECT +#define FX_UNPROTECT +#else +#define FX_PROTECT if (media_ptr -> fx_media_id != FX_MEDIA_ID) return(FX_MEDIA_NOT_OPEN); \ + else if (tx_mutex_get(&(media_ptr -> fx_media_protect), TX_WAIT_FOREVER) != TX_SUCCESS) return(FX_MEDIA_NOT_OPEN); +#define FX_UNPROTECT tx_mutex_put(&(media_ptr -> fx_media_protect)); +#endif + + +/* Define interrupt lockout constructs to protect the system date/time from being updated + while they are being read. */ +#ifndef FX_STANDALONE_ENABLE +#ifndef FX_INT_SAVE_AREA +#define FX_INT_SAVE_AREA unsigned int old_interrupt_posture; +#endif + +#ifndef FX_DISABLE_INTS +#define FX_DISABLE_INTS old_interrupt_posture = tx_interrupt_control(TX_INT_DISABLE); +#endif + +#ifndef FX_RESTORE_INTS +#define FX_RESTORE_INTS tx_interrupt_control(old_interrupt_posture); +#endif +#else +/* Disable use of ThreadX protection in standalone mode for FileX */ +#ifndef FX_LEGACY_INTERRUPT_PROTECTION +#define FX_LEGACY_INTERRUPT_PROTECTION +#endif +#define FX_INT_SAVE_AREA +#define FX_DISABLE_INTS +#define FX_RESTORE_INTS +#endif + +/* Define the error checking logic to determine if there is a caller error in the FileX API. + The default definitions assume ThreadX is being used. This code can be completely turned + off by just defining these macros to white space. */ + +#ifndef FX_STANDALONE_ENABLE +#ifndef TX_TIMER_PROCESS_IN_ISR + +#define FX_CALLER_CHECKING_EXTERNS extern TX_THREAD *_tx_thread_current_ptr; \ + extern TX_THREAD _tx_timer_thread; \ + extern volatile ULONG _tx_thread_system_state; + +#define FX_CALLER_CHECKING_CODE if ((TX_THREAD_GET_SYSTEM_STATE()) || \ + (_tx_thread_current_ptr == TX_NULL) || \ + (_tx_thread_current_ptr == &_tx_timer_thread)) \ + return(FX_CALLER_ERROR); + +#else +#define FX_CALLER_CHECKING_EXTERNS extern TX_THREAD *_tx_thread_current_ptr; \ + extern volatile ULONG _tx_thread_system_state; + +#define FX_CALLER_CHECKING_CODE if ((TX_THREAD_GET_SYSTEM_STATE()) || \ + (_tx_thread_current_ptr == TX_NULL)) \ + return(FX_CALLER_ERROR); +#endif +#else +#define FX_CALLER_CHECKING_EXTERNS +#define FX_CALLER_CHECKING_CODE +#endif + + +/* Define the update rate of the system timer. These values may also be defined at the command + line when compiling the fx_system_initialize.c module in the FileX library build. Alternatively, they can + be modified in this file or fx_user.h. Note: the update rate must be an even number of seconds greater + than or equal to 2, which is the minimal update rate for FAT time. */ + +/* Define the number of seconds the timer parameters are updated in FileX. The default + value is 10 seconds. This value can be overwritten externally. */ + +#ifndef FX_UPDATE_RATE_IN_SECONDS +#define FX_UPDATE_RATE_IN_SECONDS 10 +#endif + + +/* Defines the number of ThreadX timer ticks required to achieve the update rate specified by + FX_UPDATE_RATE_IN_SECONDS defined previously. By default, the ThreadX timer tick is 10ms, + so the default value for this constant is 1000. If TX_TIMER_TICKS_PER_SECOND is defined, + this value is derived from TX_TIMER_TICKS_PER_SECOND. */ + +#ifndef FX_UPDATE_RATE_IN_TICKS +#if (defined(TX_TIMER_TICKS_PER_SECOND) && (!defined(FX_STANDALONE_ENABLE))) +#define FX_UPDATE_RATE_IN_TICKS (TX_TIMER_TICKS_PER_SECOND * FX_UPDATE_RATE_IN_SECONDS) +#else +#define FX_UPDATE_RATE_IN_TICKS 1000 +#endif +#endif + + +/* Define the version ID of FileX. This may be utilized by the application. */ + +#ifdef FX_SYSTEM_INIT +CHAR _fx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * FileX Generic Version 6.4.0 *"; +#else +extern CHAR _fx_version_id[]; +#endif + +#endif + diff --git a/ports/linux/gnu/CMakeLists.txt b/ports/linux/gnu/CMakeLists.txt new file mode 100644 index 0000000..6bc1b86 --- /dev/null +++ b/ports/linux/gnu/CMakeLists.txt @@ -0,0 +1,10 @@ +target_sources(${PROJECT_NAME} PRIVATE + # {{BEGIN_TARGET_SOURCES}} + + # {{END_TARGET_SOURCES}} +) + +target_include_directories(${PROJECT_NAME} + PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc +) diff --git a/ports/linux/gnu/inc/fx_port.h b/ports/linux/gnu/inc/fx_port.h new file mode 100644 index 0000000..60d6e6f --- /dev/null +++ b/ports/linux/gnu/inc/fx_port.h @@ -0,0 +1,354 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* fx_port.h Linux/GCC */ +/* 6.3.0 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the FileX FAT */ +/* compatible file system function identically on a variety of */ +/* different processor architectures. For example, the byte offset of */ +/* various entries in the boot record, and directory entries are */ +/* defined in this file. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 03-02-2021 William E. Lamie Modified comment(s), and */ +/* added standalone support, */ +/* resulting in version 6.1.5 */ +/* 08-02-2021 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1.8 */ +/* 10-31-2023 Xiuwen Cai Modified comment(s), */ +/* added basic types guards, */ +/* resulting in version 6.3.0 */ +/* */ +/**************************************************************************/ + +#ifndef FX_PORT_H +#define FX_PORT_H + + +/* Determine if the optional FileX user define file should be used. */ + +#ifdef FX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in fx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "fx_user.h" +#endif +#include + + +/* Include the ThreadX api file. */ + +#ifndef FX_STANDALONE_ENABLE + +#include "tx_api.h" + +#else + +/* Define compiler library include files. */ + +#include +#include + +#ifndef VOID +#define VOID void +typedef char CHAR; +typedef char BOOL; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef long LONG; +typedef unsigned long ULONG; +typedef short SHORT; +typedef unsigned short USHORT; +#endif + +#ifndef ULONG64_DEFINED +#define ULONG64_DEFINED +typedef unsigned long long ULONG64; +#endif + +/* Define basic alignment type used in block and byte pool operations. This data type must + be at least 32-bits in size and also be large enough to hold a pointer type. */ + +#ifndef ALIGN_TYPE_DEFINED +#define ALIGN_TYPE_DEFINED +#define ALIGN_TYPE ULONG +#endif + +#endif + +#ifdef FX_REGRESSION_TEST +/* Define parameters for regression test suite. */ + +#define FX_MAX_SECTOR_CACHE 256 +#define FX_MAX_FAT_CACHE 64 +#define FX_FAT_MAP_SIZE 1 + + +/* Define variables and macros used to introduce errors for the regression test suite. */ + +extern ULONG _fx_ram_driver_io_error_request; +extern ULONG _fx_ram_driver_io_request_count; +extern ULONG _fx_file_open_max_file_size_request; +extern ULONG _fx_directory_entry_read_count; +extern ULONG _fx_directory_entry_read_error_request; +extern ULONG _fx_directory_entry_write_count; +extern ULONG _fx_directory_entry_write_error_request; +extern ULONG _fx_utility_fat_entry_write_count; +extern ULONG _fx_utility_fat_entry_write_error_request; +extern ULONG _fx_utility_fat_entry_read_count; +extern ULONG _fx_utility_fat_entry_read_error_request; +extern ULONG _fx_utility_logical_sector_flush_count; +extern ULONG _fx_utility_logical_sector_flush_error_request; +extern ULONG _fx_utility_logical_sector_write_count; +extern ULONG _fx_utility_logical_sector_write_error_request; +extern ULONG _fx_utility_logical_sector_read_count; +extern ULONG _fx_utility_logical_sector_read_error_request; +extern ULONG _fx_utility_logical_sector_read_1_count; +extern ULONG _fx_utility_logical_sector_read_1_error_request; + +#ifdef FX_ENABLE_FAULT_TOLERANT +struct FX_MEDIA_STRUCT; +extern VOID fault_tolerant_enable_callback(struct FX_MEDIA_STRUCT *media_ptr, + UCHAR *fault_tolerant_memory_buffer, + ULONG log_size); +extern VOID fault_tolerant_apply_log_callback(struct FX_MEDIA_STRUCT *media_ptr, + UCHAR *fault_tolerant_memory_buffer, + ULONG log_size); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +#define FX_DIRECTORY_ENTRY_READ_EXTENSION _fx_directory_entry_read_count++; \ + if (_fx_directory_entry_read_error_request) \ + { \ + _fx_directory_entry_read_error_request--; \ + if (_fx_directory_entry_read_error_request == 0) \ + { \ + return(FX_IO_ERROR); \ + } \ + } + +#define FX_DIRECTORY_ENTRY_WRITE_EXTENSION _fx_directory_entry_write_count++; \ + if (_fx_directory_entry_write_error_request) \ + { \ + _fx_directory_entry_write_error_request--; \ + if (_fx_directory_entry_write_error_request == 0) \ + { \ + return(FX_IO_ERROR); \ + } \ + } + +#define FX_UTILITY_FAT_ENTRY_READ_EXTENSION _fx_utility_fat_entry_read_count++; \ + if (_fx_utility_fat_entry_read_error_request) \ + { \ + _fx_utility_fat_entry_read_error_request--; \ + if (_fx_utility_fat_entry_read_error_request == 0) \ + { \ + return(FX_IO_ERROR); \ + } \ + if (_fx_utility_fat_entry_read_error_request == 10000) \ + { \ + *entry_ptr = 1; \ + _fx_utility_fat_entry_read_error_request = 0; \ + return(FX_SUCCESS); \ + } \ + if (_fx_utility_fat_entry_read_error_request == 20000) \ + { \ + *entry_ptr = media_ptr -> fx_media_fat_reserved; \ + _fx_utility_fat_entry_read_error_request = 0; \ + return(FX_SUCCESS); \ + } \ + if (_fx_utility_fat_entry_read_error_request == 30000) \ + { \ + *entry_ptr = cluster; \ + _fx_utility_fat_entry_read_error_request = 0; \ + return(FX_SUCCESS); \ + } \ + if (_fx_utility_fat_entry_read_error_request == 40000) \ + { \ + media_ptr -> fx_media_total_clusters = 0; \ + _fx_utility_fat_entry_read_error_request = 0; \ + return(FX_SUCCESS); \ + } \ + } + +#define FX_UTILITY_FAT_ENTRY_WRITE_EXTENSION _fx_utility_fat_entry_write_count++; \ + if (_fx_utility_fat_entry_write_error_request) \ + { \ + _fx_utility_fat_entry_write_error_request--; \ + if (_fx_utility_fat_entry_write_error_request == 0) \ + { \ + return(FX_IO_ERROR); \ + } \ + } + +#define FX_UTILITY_LOGICAL_SECTOR_FLUSH_EXTENSION _fx_utility_logical_sector_flush_count++; \ + if (_fx_utility_logical_sector_flush_error_request) \ + { \ + _fx_utility_logical_sector_flush_error_request--; \ + if (_fx_utility_logical_sector_flush_error_request == 0) \ + { \ + return(FX_IO_ERROR); \ + } \ + } + +#define FX_UTILITY_LOGICAL_SECTOR_READ_EXTENSION _fx_utility_logical_sector_read_count++; \ + if (_fx_utility_logical_sector_read_error_request) \ + { \ + _fx_utility_logical_sector_read_error_request--; \ + if (_fx_utility_logical_sector_read_error_request == 0) \ + { \ + return(FX_IO_ERROR); \ + } \ + } + +#define FX_UTILITY_LOGICAL_SECTOR_READ_EXTENSION_1 _fx_utility_logical_sector_read_1_count++; \ + if (_fx_utility_logical_sector_read_1_error_request) \ + { \ + _fx_utility_logical_sector_read_1_error_request--; \ + if (_fx_utility_logical_sector_read_1_error_request == 0) \ + { \ + cache_entry = FX_NULL; \ + } \ + } + +#define FX_UTILITY_LOGICAL_SECTOR_WRITE_EXTENSION _fx_utility_logical_sector_write_count++; \ + if (_fx_utility_logical_sector_write_error_request) \ + { \ + _fx_utility_logical_sector_write_error_request--; \ + if (_fx_utility_logical_sector_write_error_request == 0) \ + { \ + return(FX_IO_ERROR); \ + } \ + } + +#define FX_FAULT_TOLERANT_ENABLE_EXTENSION fault_tolerant_enable_callback(media_ptr, media_ptr -> fx_media_fault_tolerant_memory_buffer, total_size); +#define FX_FAULT_TOLERANT_APPLY_LOGS_EXTENSION fault_tolerant_apply_log_callback(media_ptr, media_ptr -> fx_media_fault_tolerant_memory_buffer, size); +#endif /* FX_REGRESSION_TEST */ + + + +/* Define FileX internal protection macros. If FX_SINGLE_THREAD is defined, + these protection macros are effectively disabled. However, for multi-thread + uses, the macros are setup to utilize a ThreadX mutex for multiple thread + access control into an open media. */ + +/* Reduce the mutex error checking for testing purpose. */ + +#if defined(FX_SINGLE_THREAD) || defined(FX_STANDALONE_ENABLE) +#define FX_PROTECT +#define FX_UNPROTECT +#else +#define FX_PROTECT tx_mutex_get(&(media_ptr -> fx_media_protect), TX_WAIT_FOREVER); +#define FX_UNPROTECT tx_mutex_put(&(media_ptr -> fx_media_protect)); +#endif + + +/* Define interrupt lockout constructs to protect the system date/time from being updated + while they are being read. */ + +#ifndef FX_STANDALONE_ENABLE +#define FX_INT_SAVE_AREA unsigned int old_interrupt_posture; +#define FX_DISABLE_INTS old_interrupt_posture = tx_interrupt_control(TX_INT_DISABLE); +#define FX_RESTORE_INTS tx_interrupt_control(old_interrupt_posture); +#else +/* Disable use of ThreadX protection in standalone mode for FileX */ +#ifndef FX_LEGACY_INTERRUPT_PROTECTION +#define FX_LEGACY_INTERRUPT_PROTECTION +#endif +#define FX_INT_SAVE_AREA +#define FX_DISABLE_INTS +#define FX_RESTORE_INTS +#endif + +/* Define the error checking logic to determine if there is a caller error in the FileX API. + The default definitions assume ThreadX is being used. This code can be completely turned + off by just defining these macros to white space. */ + +#ifndef FX_STANDALONE_ENABLE +#ifndef TX_TIMER_PROCESS_IN_ISR + +#define FX_CALLER_CHECKING_EXTERNS extern TX_THREAD *_tx_thread_current_ptr; \ + extern TX_THREAD _tx_timer_thread; \ + extern volatile ULONG _tx_thread_system_state; + +#define FX_CALLER_CHECKING_CODE if ((_tx_thread_system_state) || \ + (_tx_thread_current_ptr == TX_NULL) || \ + (_tx_thread_current_ptr == &_tx_timer_thread)) \ + return(FX_CALLER_ERROR); + +#else +#define FX_CALLER_CHECKING_EXTERNS extern TX_THREAD *_tx_thread_current_ptr; \ + extern volatile ULONG _tx_thread_system_state; + +#define FX_CALLER_CHECKING_CODE if ((_tx_thread_system_state) || \ + (_tx_thread_current_ptr == TX_NULL)) \ + return(FX_CALLER_ERROR); +#endif +#else +#define FX_CALLER_CHECKING_EXTERNS +#define FX_CALLER_CHECKING_CODE +#endif + +/* Define the update rate of the system timer. These values may also be defined at the command + line when compiling the fx_system_initialize.c module in the FileX library build. Alternatively, they can + be modified in this file. Note: the update rate must be an even number of seconds greater + than or equal to 2, which is the minimal update rate for FAT time. */ + +#ifndef FX_UPDATE_RATE_IN_SECONDS +#define FX_UPDATE_RATE_IN_SECONDS 10 /* Update time at 10 second intervals */ +#endif + +#ifndef FX_UPDATE_RATE_IN_TICKS +#define FX_UPDATE_RATE_IN_TICKS 1000 /* Same update rate, but in ticks */ +#endif + + +/* Define the version ID of FileX. This may be utilized by the application. */ + +#ifdef FX_SYSTEM_INIT +CHAR _fx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * FileX Linux/GCC Version 6.4.0 *"; +#else +extern CHAR _fx_version_id[]; +#endif + +#endif + diff --git a/ports/mips/gnu/inc/fx_port.h b/ports/mips/gnu/inc/fx_port.h new file mode 100644 index 0000000..ad09054 --- /dev/null +++ b/ports/mips/gnu/inc/fx_port.h @@ -0,0 +1,25 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +/* Include the generic version of fx_port.h. */ +#include "../../../generic/inc/fx_port.h" + diff --git a/ports/win32/vs_2019/inc/fx_port.h b/ports/win32/vs_2019/inc/fx_port.h new file mode 100644 index 0000000..6e07897 --- /dev/null +++ b/ports/win32/vs_2019/inc/fx_port.h @@ -0,0 +1,219 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* fx_port.h Win32/Visual */ +/* 6.3.0 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the FileX FAT */ +/* compatible file system function identically on a variety of */ +/* different processor architectures. For example, the byte offset of */ +/* various entries in the boot record, and directory entries are */ +/* defined in this file. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 03-02-2021 William E. Lamie Modified comment(s), and */ +/* added standalone support, */ +/* resulting in version 6.1.5 */ +/* 08-02-2021 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1.8 */ +/* 10-31-2023 Xiuwen Cai Modified comment(s), */ +/* added basic types guards, */ +/* resulting in version 6.3.0 */ +/* */ +/**************************************************************************/ + +#ifndef FX_PORT_H +#define FX_PORT_H + + +/* Determine if the optional FileX user define file should be used. */ + +#ifdef FX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in fx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "fx_user.h" +#endif + + +/* Include the ThreadX api file. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" + + +/* Define ULONG64 typedef, if not already defined. */ + +#ifndef ULONG64_DEFINED +#define ULONG64_DEFINED +typedef unsigned long long ULONG64; +#endif + +#else + +/* Define compiler library include files. */ + +#include +#include + +#ifndef VOID +#define VOID void +typedef char CHAR; +typedef char BOOL; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef long LONG; +typedef unsigned long ULONG; +typedef short SHORT; +typedef unsigned short USHORT; +#endif + +#ifndef ULONG64_DEFINED +#define ULONG64_DEFINED +typedef unsigned long long ULONG64; +#endif + + +/* Define basic alignment type used in block and byte pool operations. This data type must + be at least 32-bits in size and also be large enough to hold a pointer type. */ + +#ifndef ALIGN_TYPE_DEFINED +#define ALIGN_TYPE_DEFINED +#define ALIGN_TYPE ULONG +#endif + + +#endif + +/* Define FileX internal protection macros. If FX_SINGLE_THREAD is defined, + these protection macros are effectively disabled. However, for multi-thread + uses, the macros are setup to utilize a ThreadX mutex for multiple thread + access control into an open media. */ + +#if defined(FX_SINGLE_THREAD) || defined(FX_STANDALONE_ENABLE) +#define FX_PROTECT +#define FX_UNPROTECT +#else +#define FX_PROTECT if (media_ptr -> fx_media_id != FX_MEDIA_ID) return(FX_MEDIA_NOT_OPEN); \ + else if (tx_mutex_get(&(media_ptr -> fx_media_protect), TX_WAIT_FOREVER) != TX_SUCCESS) return(FX_MEDIA_NOT_OPEN); +#define FX_UNPROTECT tx_mutex_put(&(media_ptr -> fx_media_protect)); +#endif + + +/* Define interrupt lockout constructs to protect the system date/time from being updated + while they are being read. */ + +#ifndef FX_STANDALONE_ENABLE +#define FX_INT_SAVE_AREA unsigned int old_interrupt_posture; +#define FX_DISABLE_INTS old_interrupt_posture = tx_interrupt_control(TX_INT_DISABLE); +#define FX_RESTORE_INTS tx_interrupt_control(old_interrupt_posture); +#else +#define FX_INT_SAVE_AREA +#define FX_DISABLE_INTS +#define FX_RESTORE_INTS +#endif + +/* Define the error checking logic to determine if there is a caller error in the FileX API. + The default definitions assume ThreadX is being used. This code can be completely turned + off by just defining these macros to white space. */ + +#ifndef FX_STANDALONE_ENABLE +#ifndef TX_TIMER_PROCESS_IN_ISR + +#define FX_CALLER_CHECKING_EXTERNS extern TX_THREAD *_tx_thread_current_ptr; \ + extern TX_THREAD _tx_timer_thread; \ + extern volatile ULONG _tx_thread_system_state; + +#define FX_CALLER_CHECKING_CODE if ((_tx_thread_system_state) || \ + (_tx_thread_current_ptr == TX_NULL) || \ + (_tx_thread_current_ptr == &_tx_timer_thread)) \ + return(FX_CALLER_ERROR); + +#else +#define FX_CALLER_CHECKING_EXTERNS extern TX_THREAD *_tx_thread_current_ptr; \ + extern volatile ULONG _tx_thread_system_state; + +#define FX_CALLER_CHECKING_CODE if ((_tx_thread_system_state) || \ + (_tx_thread_current_ptr == TX_NULL)) \ + return(FX_CALLER_ERROR); +#endif +#else +#define FX_CALLER_CHECKING_EXTERNS +#define FX_CALLER_CHECKING_CODE +#endif + +/* Define the update rate of the system timer. These values may also be defined at the command + line when compiling the fx_system_initialize.c module in the FileX library build. Alternatively, they can + be modified in this file or fx_user.h. Note: the update rate must be an even number of seconds greater + than or equal to 2, which is the minimal update rate for FAT time. */ + +/* Define the number of seconds the timer parameters are updated in FileX. The default + value is 10 seconds. This value can be overwritten externally. */ + +#ifndef FX_UPDATE_RATE_IN_SECONDS +#define FX_UPDATE_RATE_IN_SECONDS 10 +#endif + + +/* Defines the number of ThreadX timer ticks required to achieve the update rate specified by + FX_UPDATE_RATE_IN_SECONDS defined previously. By default, the ThreadX timer tick is 10ms, + so the default value for this constant is 1000. If TX_TIMER_TICKS_PER_SECOND is defined, + this value is derived from TX_TIMER_TICKS_PER_SECOND. */ + +#ifndef FX_UPDATE_RATE_IN_TICKS +#if (defined(TX_TIMER_TICKS_PER_SECOND) && (!defined(FX_STANDALONE_ENABLE))) +#define FX_UPDATE_RATE_IN_TICKS (TX_TIMER_TICKS_PER_SECOND * FX_UPDATE_RATE_IN_SECONDS) +#else +#define FX_UPDATE_RATE_IN_TICKS 1000 +#endif +#endif + + +/* Define the version ID of FileX. This may be utilized by the application. */ + +#ifdef FX_SYSTEM_INIT +CHAR _fx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * FileX Win32/Version 6.4.0 *"; +#else +extern CHAR _fx_version_id[]; +#endif + +#endif + diff --git a/samples/demo_filex.c b/samples/demo_filex.c new file mode 100644 index 0000000..b7649ba --- /dev/null +++ b/samples/demo_filex.c @@ -0,0 +1,251 @@ +/* This is a small demo of the high-performance FileX FAT file system. It includes setup for + a small 34KB RAM disk and a loop that writes and reads a small file. */ +#include "fx_api.h" +#ifdef FX_ENABLE_FAULT_TOLERANT +#include "fx_fault_tolerant.h" +#endif /* FX_ENABLE_FAULT_TOLERANT */ + +#define DEMO_STACK_SIZE 2048 + + +/* Buffer for FileX FX_MEDIA sector cache. This must be large enough for at least one + sector, which are typically 512 bytes in size. */ + +unsigned char media_memory[512]; + +#ifdef FX_ENABLE_FAULT_TOLERANT +UCHAR fault_tolerant_memory[FX_FAULT_TOLERANT_MAXIMUM_LOG_FILE_SIZE]; +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/* Define RAM device driver entry. */ + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); + + +/* Define thread prototypes. */ + +void thread_0_entry(ULONG thread_input); + + +/* Define FileX global data structures. */ + +FX_MEDIA ram_disk; +FX_FILE my_file; + +#ifndef FX_STANDALONE_ENABLE +CHAR *ram_disk_memory; +#else +unsigned char ram_disk_memory[256*512]; +#endif + +/* Define ThreadX global data structures. */ + +#ifndef FX_STANDALONE_ENABLE +TX_THREAD thread_0; +#endif +ULONG thread_0_counter; + + +void main(void) +{ + +#ifdef FX_STANDALONE_ENABLE + + /* Initialize FileX. */ + fx_system_initialize(); + + thread_0_entry(0); +#else + /* Enter the ThreadX kernel. */ + tx_kernel_enter(); +#endif + +} + + +/* Define what the initial system looks like. */ + +#ifndef FX_STANDALONE_ENABLE +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer; + + /* Put first available memory address into a character pointer. */ + pointer = (CHAR *) first_unused_memory; + + /* Put system definition stuff in here, e.g. thread creates and other assorted + create information. */ + + /* Create the main thread. */ + tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 1, 1, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; + + /* Save the memory pointer for the RAM disk. */ + ram_disk_memory = pointer; + + /* Initialize FileX. */ + fx_system_initialize(); +} + +#endif + +void thread_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +CHAR local_buffer[30]; + + + /* Format the RAM disk - the memory for the RAM disk was setup in + tx_application_define above. */ + fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + media_memory, // Media buffer pointer + sizeof(media_memory), // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 512, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + + /* Loop to repeat the demo over and over! */ + do + { + + /* Open the RAM disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, media_memory, sizeof(media_memory)); + + /* Check the media open status. */ + if (status != FX_SUCCESS) + { + + /* Error, break the loop! */ + break; + } + +#ifdef FX_ENABLE_FAULT_TOLERANT + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_memory, sizeof(fault_tolerant_memory)); + + if (status != FX_SUCCESS) + { + + /* Fault tolerant enable error, break the loop. */ + break; + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + /* Check for an already created status. This is expected on the + second pass of this loop! */ + if (status != FX_ALREADY_CREATED) + { + + /* Create error, break the loop. */ + break; + } + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + /* Error opening file, break the loop. */ + break; + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + /* Error performing file seek, break the loop. */ + break; + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + /* Error writing to a file, break the loop. */ + break; + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + /* Error performing file seek, break the loop. */ + break; + } + + /* Read the first 28 bytes of the test file. */ + status = fx_file_read(&my_file, local_buffer, 28, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + /* Error reading file, break the loop. */ + break; + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + /* Error closing the file, break the loop. */ + break; + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Check the media close status. */ + if (status != FX_SUCCESS) + { + + /* Error closing the media, break the loop. */ + break; + } + + /* Increment the thread counter, which represents the number + of successful passes through this loop. */ + thread_0_counter++; + + } while (1); + + /* If we get here the FileX test failed! */ + return; +} + diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 0000000..48ee9a5 --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,2 @@ +#!/bin/bash +$(dirname `realpath $0`)/../test/cmake/run.sh build all diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 0000000..d38a477 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# + +# Remove large folder +rm -rf /opt/hostedtoolcache + +# Install necessary softwares for Ubuntu. + +sudo apt-get update +sudo apt-get install -y \ + gcc-multilib \ + git \ + g++ \ + python3-pip \ + ninja-build \ + unifdef \ + p7zip-full \ + tofrodos \ + gawk \ + software-properties-common + +wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | sudo apt-key add - +CODENAME=$(lsb_release -c | cut -f2 -d':' | sed 's/\t//') +apt-add-repository "deb https://apt.kitware.com/ubuntu/ $CODENAME main" + +python3 -m pip install --upgrade pip +pip3 install gcovr==4.1 +pip install --upgrade cmake \ No newline at end of file diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 0000000..54b0e34 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,2 @@ +#!/bin/bash +$(dirname `realpath $0`)/../test/cmake/run.sh test all diff --git a/test/cmake/.gitignore b/test/cmake/.gitignore new file mode 100644 index 0000000..9766ef7 --- /dev/null +++ b/test/cmake/.gitignore @@ -0,0 +1 @@ +threadx \ No newline at end of file diff --git a/test/cmake/CMakeLists.txt b/test/cmake/CMakeLists.txt new file mode 100644 index 0000000..a81cc17 --- /dev/null +++ b/test/cmake/CMakeLists.txt @@ -0,0 +1,105 @@ +cmake_minimum_required(VERSION 3.13 FATAL_ERROR) +cmake_policy(SET CMP0054 NEW) +cmake_policy(SET CMP0057 NEW) + +project(threadx_test LANGUAGES C) + +# Set build configurations +set(BUILD_CONFIGURATIONS + default_build_coverage no_cache_build no_cache_standalone_build + fault_tolerant_build_coverage no_check_build no_cache_fault_tolerant_build + standalone_build_coverage standalone_fault_tolerant_build_coverage + standalone_no_cache_fault_tolerant_build) +set(CMAKE_CONFIGURATION_TYPES + ${BUILD_CONFIGURATIONS} + CACHE STRING "list of supported configuration types" FORCE) +set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + ${CMAKE_CONFIGURATION_TYPES}) +list(GET CMAKE_CONFIGURATION_TYPES 0 BUILD_TYPE) +if((NOT CMAKE_BUILD_TYPE) OR (NOT ("${CMAKE_BUILD_TYPE}" IN_LIST + CMAKE_CONFIGURATION_TYPES))) + set(CMAKE_BUILD_TYPE + "${BUILD_TYPE}" + CACHE STRING "Build Type of the project" FORCE) +endif() + +message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") +message(STATUS "Using toolchain file: ${CMAKE_TOOLCHAIN_FILE}.") +set(FX_FAULT_TOLERANT_DEFINITIONS + -DFX_ENABLE_FAULT_TOLERANT -DFX_UPDATE_FILE_SIZE_ON_ALLOCATE + -DFX_FAULT_TOLERANT_TRANSACTION_FAIL_FUNCTION) +set(default_build_coverage "") +set(no_cache_build -DFX_DISABLE_CACHE -DFX_DISABLE_FAT_ENTRY_REFRESH) +set(fault_tolerant_build_coverage ${FX_FAULT_TOLERANT_DEFINITIONS}) +set(standalone_build_coverage -DFX_STANDALONE_ENABLE) +set(standalone_fault_tolerant_build_coverage ${FX_FAULT_TOLERANT_DEFINITIONS} + -DFX_STANDALONE_ENABLE) +set(no_cache_standalone_build -DFX_DISABLE_CACHE -DFX_STANDALONE_ENABLE) +set(no_check_build ${FX_COMPILE_DEFINITIONS} -DFX_DISABLE_ERROR_CHECKING) +set(no_cache_fault_tolerant_build ${no_cache_build} ${FX_FAULT_TOLERANT_DEFINITIONS}) +set(standalone_no_cache_fault_tolerant_build ${no_cache_build} ${FX_FAULT_TOLERANT_DEFINITIONS} -DFX_STANDALONE_ENABLE) + +add_compile_options( + -m32 + -std=c99 + -ggdb + -g3 + -gdwarf-2 + -fdiagnostics-color + -Werror + -DFX_REGRESSION_TEST + ${${CMAKE_BUILD_TYPE}}) +add_link_options(-m32) + +if(CMAKE_BUILD_TYPE MATCHES ".*standalone.*") + set(FX_STANDALONE_ENABLE + ON + CACHE BOOL "Enable Filex in standalone mode") +endif() + +enable_testing() + +add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../.. filex) +add_subdirectory(regression) +add_subdirectory(samples) + +# Coverage +if(CMAKE_BUILD_TYPE MATCHES ".*_coverage") + target_compile_options(filex PRIVATE -fprofile-arcs -ftest-coverage) + target_link_options(filex PRIVATE -fprofile-arcs -ftest-coverage) +endif() + +# Build ThreadX library once +if(NOT FX_STANDALONE_ENABLE) + execute_process(COMMAND ${CMAKE_CURRENT_LIST_DIR}/run.sh build_libs) + add_custom_target(build_libs ALL COMMAND ${CMAKE_CURRENT_LIST_DIR}/run.sh + build_libs) + add_dependencies(filex build_libs) + target_include_directories(filex PUBLIC ${CMAKE_BINARY_DIR}/../libs/inc) + add_library(threadx SHARED IMPORTED GLOBAL) + add_library("azrtos::threadx" ALIAS threadx) + set_target_properties( + threadx PROPERTIES IMPORTED_LOCATION + ${CMAKE_BINARY_DIR}/../libs/threadx/libthreadx.so) +endif() + +target_compile_options( + filex + PRIVATE -Werror + -Wall + -Wextra + -pedantic + -fmessage-length=0 + -fsigned-char + -ffunction-sections + -fdata-sections + -Wunused + -Wuninitialized + -Wmissing-declarations + -Wconversion + -Wpointer-arith + -Wshadow + -Wlogical-op + -Waggregate-return + -Wfloat-equal) + diff --git a/test/cmake/coverage.sh b/test/cmake/coverage.sh new file mode 100755 index 0000000..48f3a73 --- /dev/null +++ b/test/cmake/coverage.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +set -e + +cd $(dirname $0) +mkdir -p coverage_report/$1 +gcovr --object-directory=build/$1/filex/CMakeFiles/filex.dir/common/src -r ../../common/src -e ".*driver.*" --xml-pretty --output coverage_report/$1.xml +gcovr --object-directory=build/$1/filex/CMakeFiles/filex.dir/common/src -r ../../common/src -e ".*driver.*" --html --html-details --output coverage_report/$1/index.html diff --git a/test/cmake/libs/CMakeLists.txt b/test/cmake/libs/CMakeLists.txt new file mode 100644 index 0000000..60a73e8 --- /dev/null +++ b/test/cmake/libs/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.13 FATAL_ERROR) + +project(libs LANGUAGES C) + +if(ENABLE_64) + message(STATUS "Building for 64bit") +else() + add_compile_options(-m32) + add_link_options(-m32) + message(STATUS "Building for 32bit") +endif() +message(STATUS "Using toolchain file: ${CMAKE_TOOLCHAIN_FILE}.") + +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../threadx threadx) +target_compile_options(threadx PRIVATE -DTX_ENABLE_EVENT_TRACE -DTX_LINUX_NO_IDLE_ENABLE) + +get_target_property(dirs threadx INCLUDE_DIRECTORIES) +execute_process(COMMAND mkdir -p ${CMAKE_BINARY_DIR}/inc) +foreach(dir ${dirs}) + file(GLOB header_files ${dir}/*.h) + foreach(header_file ${header_files}) + execute_process(COMMAND ln -sf ${header_file} ${CMAKE_BINARY_DIR}/inc) + endforeach() +endforeach() diff --git a/test/cmake/regression/CMakeLists.txt b/test/cmake/regression/CMakeLists.txt new file mode 100644 index 0000000..fe09c59 --- /dev/null +++ b/test/cmake/regression/CMakeLists.txt @@ -0,0 +1,156 @@ +cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR) +cmake_policy(SET CMP0057 NEW) + +project(regression_test LANGUAGES C) + +set(SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../../regression_test) + +set(regression_test_cases + ${SOURCE_DIR}/filex_directory_attributes_read_set_test.c + ${SOURCE_DIR}/filex_directory_create_delete_test.c + ${SOURCE_DIR}/filex_directory_default_path_get_set_test.c + ${SOURCE_DIR}/filex_directory_duplicate_entries_test.c + ${SOURCE_DIR}/filex_directory_first_next_find_test.c + ${SOURCE_DIR}/filex_directory_local_path_test.c + ${SOURCE_DIR}/filex_directory_long_short_get_test.c + ${SOURCE_DIR}/filex_directory_naming_test.c + ${SOURCE_DIR}/filex_directory_rename_test.c + ${SOURCE_DIR}/filex_fault_tolerant_corrupted_log_file_test.c + ${SOURCE_DIR}/filex_fault_tolerant_delete_large_data_test.c + ${SOURCE_DIR}/filex_fault_tolerant_directory_attributes_set_directory_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_directory_attributes_set_redo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_directory_create_directory_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_directory_create_redo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_directory_delete_directory_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_directory_delete_redo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_directory_rename_directory_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_directory_rename_redo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_enable_test.c + ${SOURCE_DIR}/filex_fault_tolerant_enable_1_test.c + ${SOURCE_DIR}/filex_fault_tolerant_enable_2_test.c + ${SOURCE_DIR}/filex_fault_tolerant_enable_3_test.c + ${SOURCE_DIR}/filex_fault_tolerant_enable_4_test.c + ${SOURCE_DIR}/filex_fault_tolerant_media_check_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_seek_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_random_seek_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_allocate_directory_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_allocate_fat_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_allocate_redo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_allocate_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_allocate_undo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_attributes_set_directory_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_attributes_set_redo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_best_allocate_directory_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_best_allocate_fat_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_best_allocate_redo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_best_allocate_undo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_create_directory_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_create_redo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_delete_directory_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_delete_fat_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_delete_fat_multiple_sectors_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_delete_redo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_delete_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_delete_undo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_rename_directory_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_rename_redo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_truncate_release_available_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_truncate_directory_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_truncate_redo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_truncate_release_directory_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_truncate_release_fat_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_truncate_release_redo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_truncate_release_undo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_write_available_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_write_data_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_write_directory_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_write_fat_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_write_redo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_write_undo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_corruption_test.c + ${SOURCE_DIR}/filex_fault_tolerant_log_full_test.c + ${SOURCE_DIR}/filex_fault_tolerant_log_recover_directory_test.c + ${SOURCE_DIR}/filex_fault_tolerant_log_recover_fat_test.c + ${SOURCE_DIR}/filex_fault_tolerant_long_directory_create_directory_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_long_directory_create_redo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_long_directory_rename_directory_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_long_directory_rename_redo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_long_directory_rename_io_error_test.c + ${SOURCE_DIR}/filex_fault_tolerant_long_file_create_directory_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_long_file_create_redo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_loop_write_data_test.c + ${SOURCE_DIR}/filex_fault_tolerant_media_full_test.c + ${SOURCE_DIR}/filex_fault_tolerant_write_large_data_directory_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_write_large_data_fat_chain_cleanup_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_write_large_data_fat_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_write_large_data_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_write_large_data_redo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_write_large_data_test.c + ${SOURCE_DIR}/filex_fault_tolerant_write_large_data_undo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_recover_fail_test.c + ${SOURCE_DIR}/filex_fault_tolerant_unicode_directory_create_directory_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_unicode_directory_create_redo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_unicode_directory_rename_directory_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_unicode_directory_rename_redo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_unicode_file_create_directory_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_unicode_file_create_redo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_unicode_file_rename_directory_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_unicode_file_rename_redo_log_interrupt_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_write_fat_crossover_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_write_fat_crossover_2_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_allocate_fat_crossover_test.c + ${SOURCE_DIR}/filex_fault_tolerant_file_best_effort_allocate_fat_crossover_test.c + ${SOURCE_DIR}/filex_file_allocate_truncate_test.c + ${SOURCE_DIR}/filex_file_allocate_test.c + ${SOURCE_DIR}/filex_file_attributes_read_set_test.c + ${SOURCE_DIR}/filex_file_create_delete_test.c + ${SOURCE_DIR}/filex_file_date_time_set_test.c + ${SOURCE_DIR}/filex_file_naming_test.c + ${SOURCE_DIR}/filex_file_read_write_test.c + ${SOURCE_DIR}/filex_file_rename_test.c + ${SOURCE_DIR}/filex_file_seek_test.c + ${SOURCE_DIR}/filex_file_name_test.c + ${SOURCE_DIR}/filex_media_abort_test.c + ${SOURCE_DIR}/filex_media_cache_invalidate_test.c + ${SOURCE_DIR}/filex_media_check_test.c + ${SOURCE_DIR}/filex_media_flush_test.c + ${SOURCE_DIR}/filex_media_format_open_close_test.c + ${SOURCE_DIR}/filex_media_multiple_open_close_test.c + ${SOURCE_DIR}/filex_media_read_write_sector_test.c + ${SOURCE_DIR}/filex_media_volume_directory_entry_test.c + ${SOURCE_DIR}/filex_media_volume_get_set_test.c + ${SOURCE_DIR}/filex_media_hidden_sectors_test.c + ${SOURCE_DIR}/filex_system_date_time_test.c + ${SOURCE_DIR}/filex_unicode_directory_rename_test.c + ${SOURCE_DIR}/filex_unicode_file_directory_rename_extra_test.c + ${SOURCE_DIR}/filex_unicode_file_directory_rename_extra_2_test.c + ${SOURCE_DIR}/filex_unicode_file_rename_test.c + ${SOURCE_DIR}/filex_unicode_test.c + ${SOURCE_DIR}/filex_unicode_2_test.c + ${SOURCE_DIR}/filex_unicode_3_test.c + ${SOURCE_DIR}/filex_unicode_4_test.c + ${SOURCE_DIR}/filex_unicode_name_string_test.c + ${SOURCE_DIR}/filex_unicode_directory_entry_test.c + ${SOURCE_DIR}/filex_unicode_directory_entry_2_test.c + ${SOURCE_DIR}/filex_unicode_directory_entry_change_test.c + ${SOURCE_DIR}/filex_unicode_fat_entry_test.c + ${SOURCE_DIR}/filex_unicode_fat_entry_1_test.c + ${SOURCE_DIR}/filex_unicode_fat_entry_2_test.c + ${SOURCE_DIR}/filex_unicode_fat_entry_3_test.c + ${SOURCE_DIR}/filex_file_write_seek_test.c + ${SOURCE_DIR}/filex_file_write_notify_test.c + ${SOURCE_DIR}/filex_file_write_available_cluster_test.c + ${SOURCE_DIR}/filex_utility_test.c + ${SOURCE_DIR}/filex_utility_fat_flush_test.c) + +add_library(test_utility ${SOURCE_DIR}/fx_ram_driver_test.c + ${SOURCE_DIR}/filextestcontrol.c) +target_link_libraries(test_utility PUBLIC azrtos::filex) +target_compile_definitions(test_utility PUBLIC BATCH_TEST CTEST) + +foreach(test_case ${regression_test_cases}) + get_filename_component(test_name ${test_case} NAME_WE) + add_executable(${test_name} ${test_case}) + target_link_libraries(${test_name} PRIVATE test_utility) + add_test(${CMAKE_BUILD_TYPE}::${test_name} ${test_name}) +endforeach() diff --git a/test/cmake/run.sh b/test/cmake/run.sh new file mode 100755 index 0000000..299230e --- /dev/null +++ b/test/cmake/run.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +cd $(dirname $0) + +# if threadx repo does not exist, clone it +[ -d threadx ] || git clone https://github.com/azure-rtos/threadx.git --depth 1 +[ -f .run.sh ] || ln -sf threadx/scripts/cmake_bootstrap.sh .run.sh +./.run.sh $* \ No newline at end of file diff --git a/test/cmake/samples/CMakeLists.txt b/test/cmake/samples/CMakeLists.txt new file mode 100644 index 0000000..d8d5759 --- /dev/null +++ b/test/cmake/samples/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR) +cmake_policy(SET CMP0057 NEW) + +project(samples LANGUAGES C) + +set(SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../samples) + +set(sample_files + ${SOURCE_DIR}/demo_filex.c) + +foreach(sample_file ${sample_files}) + get_filename_component(sample_file_name ${sample_file} NAME_WE) + add_executable(${sample_file_name} ${sample_file} + ${CMAKE_CURRENT_LIST_DIR}/../../regression_test/fx_ram_driver_test.c) + target_compile_options(${sample_file_name} PRIVATE -DSAMPLE_BUILD) + target_link_libraries(${sample_file_name} PRIVATE azrtos::filex) +endforeach() diff --git a/test/regression_test/filex_directory_attributes_read_set_test.c b/test/regression_test/filex_directory_attributes_read_set_test.c new file mode 100644 index 0000000..0eb4ebb --- /dev/null +++ b/test/regression_test/filex_directory_attributes_read_set_test.c @@ -0,0 +1,298 @@ +/* This FileX test concentrates on the file attributes read/set operation. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include +#include "fx_ram_driver_test.h" + +void test_control_return(UINT status); + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 128*128 + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + + +/* Define thread prototypes. */ + +void filex_directory_attributes_read_set_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_directory_attributes_read_set_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT attributes; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Directory attributes read/set test....................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(2); + } + +/* test error checking */ +#ifndef FX_DISABLE_ERROR_CHECKING + + /* check directory attributes read null pointer error */ + status = fx_directory_attributes_read(FX_NULL, "TEST_DIR", &attributes); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(99); + } + + /* check directory attributes set null pointer error */ + status = fx_directory_attributes_set(FX_NULL, "TEST_DIR", attributes); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(99); + } + + /* check directory attributes set null pointer error */ + status = fx_directory_attributes_set(&ram_disk, "TEST_DIR", 0x1111); + if (status != FX_INVALID_ATTR) + { + printf("ERROR!\n"); + test_control_return(99); + } + +#endif + + /* check directory attributes read to make sure it throws an error when the media hasnt been opened */ + status = fx_directory_attributes_read(&ram_disk, "TEST_DIR", &attributes); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(99); + } + + /* check directory attributes set to make sure it throws an error when the media hasnt been opened */ + status = fx_directory_attributes_set(&ram_disk, "TEST_DIR", 0x0000); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(99); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a directory called TEST_DIR in the root directory. */ + status = fx_directory_create(&ram_disk, "TEST_DIR"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Create a file called NOT_A_DIR in the root directory. */ + status = fx_file_create(&ram_disk, "NOT_A_DIR"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Invalidate the media cache. */ + fx_media_cache_invalidate(&ram_disk); + + /* Pickup the attributes of the directory. */ + status = fx_directory_attributes_read(&ram_disk, "TEST_DIR", &attributes); + + /* Check the attributes read status. */ + if ((status != FX_SUCCESS) || (attributes != FX_DIRECTORY)) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* check directory attributes set to make sure it throws an error when the directory is write protected */ + ram_disk.fx_media_driver_write_protect = FX_TRUE; + status = fx_directory_attributes_set(&ram_disk, "TEST_DIR", attributes); + if (status == FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(99); + } + ram_disk.fx_media_driver_write_protect = FX_FALSE; + + /* check directory attributes read to make sure it throws an error when it isnt a directory */ + status = fx_directory_attributes_read(&ram_disk, "NOT_A_DIR", &attributes); + if (status != FX_NOT_DIRECTORY) + { + printf("ERROR!\n"); + test_control_return(99); + } + + /* check directory attributes read to make sure it throws an error when the directory isnt found */ + status = fx_directory_attributes_read(&ram_disk, "DOES_NOT_EXIST", &attributes); + if (status == FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(99); + } + + /* check directory attributes set to make sure it throws an error when it isnt a directory */ + status = fx_directory_attributes_set(&ram_disk, "NOT_A_DIR", attributes); + if (status != FX_NOT_DIRECTORY) + { + printf("ERROR!\n"); + test_control_return(99); + } + + /* check directory attributes set to make sure it throws an error when the directory isnt found */ + status = fx_directory_attributes_set(&ram_disk, "DOES_NOT_EXIST", attributes); + if (status == FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(99); + } + + /* Invalidate the media cache. */ + fx_media_cache_invalidate(&ram_disk); + + /* Now write the attributes out for the directory. */ + status = fx_directory_attributes_set(&ram_disk, "TEST_DIR", attributes | FX_ARCHIVE | FX_SYSTEM | FX_READ_ONLY | FX_HIDDEN); + + /* Check the attributes set status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Invalidate the media cache. */ + fx_media_cache_invalidate(&ram_disk); + + /* Pickup the attributes of the directory again. */ + status = fx_directory_attributes_read(&ram_disk, "TEST_DIR", &attributes); + + /* Check the attributes read status. */ + if ((status != FX_SUCCESS) || (attributes != (FX_ARCHIVE | FX_READ_ONLY | FX_DIRECTORY | FX_SYSTEM | FX_HIDDEN))) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(8); + } + else + { + + printf("SUCCESS!\n"); + test_control_return(0); + } +} + diff --git a/test/regression_test/filex_directory_create_delete_test.c b/test/regression_test/filex_directory_create_delete_test.c new file mode 100644 index 0000000..32eca61 --- /dev/null +++ b/test/regression_test/filex_directory_create_delete_test.c @@ -0,0 +1,3056 @@ +/* This FileX test concentrates on the basic directory create/delete operations. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_directory.h" +#include "fx_ram_driver_test.h" +#include "fx_fault_tolerant.h" +#include "fx_utility.h" +#include + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 16*128 +#ifdef FX_ENABLE_FAULT_TOLERANT +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE +#else +#define FAULT_TOLERANT_SIZE 0 +#endif + + +#define DIR_PATH "MYDIR\\" +#define DIR_PATH_SIZE (sizeof(DIR_PATH) - 1) + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static CHAR name[FX_MAX_LONG_NAME_LEN+50]; +static CHAR volume_name[FX_MAX_LONG_NAME_LEN+1]; +static FX_FILE file_1; +static FX_FILE file_2; +static FX_FILE file_3; +static FX_FILE file_4; +static FX_FILE file_5; +static FX_FILE file_6; +static FX_FILE file_7; +static FX_FILE file_8; +static FX_FILE file_9; +static FX_FILE file_10; +static FX_FILE file_11; +static FX_FILE file_12; +static FX_FILE file_13; +static FX_FILE file_14; +static FX_FILE file_15; +static FX_FILE file_16; +static FX_FILE file_17; +static FX_FILE file_18; +static FX_FILE file_19; +static FX_FILE file_20; +static FX_FILE file_21; +static FX_FILE file_22; +static FX_FILE file_23; +static FX_FILE file_24; +static FX_FILE file_25; +static FX_FILE file_26; +static FX_FILE file_27; +static FX_FILE file_28; +static FX_FILE file_29; +static FX_FILE file_30; +static FX_FILE file_31; +static FX_FILE file_32; +static FX_FILE file_33; +static FX_FILE file_34; +static FX_FILE file_35; +static FX_FILE file_36; +static FX_FILE file_37; +static FX_FILE file_38; +static FX_FILE file_39; +static FX_FILE file_40; +static FX_FILE file_41; +static FX_FILE file_42; +static FX_FILE file_43; +static FX_FILE file_44; +static UCHAR buffer[128]; + + +static UCHAR my_unicode_name[] = +{0xFF,0xC1, 0xFF,0x00, 0xFF,0x00, 0xFF,0x02, +0xFF,0xD6, 0xFF,0xC7, 0xFF,0x00, 0xFF,0xC7, +0xFF,0xB9, 0xFF,0x00, 0xFF,0xC7, 0xFF,0xB2, +0xFF,0xD6, 0xFF,0xC7, 0xFF,0x00, 0x00,0x00}; + +static UCHAR my_unicode_name1[] = +{0x00,0xFF, 0x00,0xFF, 0x00,0xFF, 0x00,0xFF, +0x00,0xFF, 0x00,0xFF, 0x00,0xFF, 0x00,0xFF, +0x00,0xFF, 0x00,0xFF, 0x00,0xFF, 0x00,0xFF, +0x00,0xFF, 0x00,0xFF, 0x00,0xFF, 0x00,0x00}; + + + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif + + +extern ULONG _fx_ram_driver_copy_default_format; + + +/* Define thread prototypes. */ + +void filex_directory_create_delete_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_directory_create_delete_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG temp; +UINT attributes; +UINT i; +#ifdef EXTENDED_NAME_MANGLE_TEST +UINT j, k, l; +#endif +CHAR *name_ptr; +FX_DIR_ENTRY dir_entry; +FX_DIR_ENTRY search_directory; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Directory create/delete test..........................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + +/* We need a larger disk to test the feature of fault tolerant. */ +#ifdef FX_ENABLE_FAULT_TOLERANT + 512 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster +#else + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster +#endif + + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Attempt to create a directory before the media is opened to generate an error */ + status = fx_directory_create(&ram_disk, "/A0"); + return_if_fail( status == FX_MEDIA_NOT_OPEN); + + /* Attempt to delete a directory before the media is opened to generate an error */ + status = fx_directory_delete(&ram_disk, "/A0"); + return_if_fail( status == FX_MEDIA_NOT_OPEN); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + +/* We need a larger disk to test the feature of fault tolerant. */ +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Enable fault tolerant if FX_ENABLE_FAULT_TOLERANT is defined. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); +#endif + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + + /* send null pointer to directory create to generate an error */ + status = fx_directory_create(FX_NULL, "/A0"); + return_if_fail( status == FX_PTR_ERROR); + + /* directory delete */ + status = fx_directory_delete(FX_NULL, "/A0"); + return_if_fail( status == FX_PTR_ERROR); + +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Attempt to create a directory with an invalid name to generate an error */ + status = fx_directory_create(&ram_disk, ""); + return_if_fail( status == FX_INVALID_NAME); + + /* Attempt to create a directory with an invalid name to generate an error */ + status = fx_directory_create(&ram_disk, "/A0/error\\error"); + return_if_fail( status == FX_INVALID_PATH); + + /* Attempt to create a directory while the media is write protected to generate an error */ + ram_disk.fx_media_driver_write_protect = FX_TRUE; + status = fx_directory_create(&ram_disk, "/A0"); + return_if_fail( status == FX_WRITE_PROTECT); + + /* try to create a directory while the media is write protected to generate an error */ + status = fx_directory_delete(&ram_disk, "/A0"); + return_if_fail( status == FX_WRITE_PROTECT); + ram_disk.fx_media_driver_write_protect = FX_FALSE; + + /* Attempt to create a directory when there is no space to generate an error */ + temp = ram_disk.fx_media_available_clusters; + ram_disk.fx_media_available_clusters = 0; + status = fx_directory_create(&ram_disk, "/A0"); + return_if_fail( status == FX_NO_MORE_SPACE); + ram_disk.fx_media_available_clusters = temp; + + /* Create a series of directories... */ + status = fx_directory_create(&ram_disk, "/A0"); + status += fx_directory_create(&ram_disk, "/B0"); + status += fx_directory_create(&ram_disk, "/C0"); + status += fx_directory_create(&ram_disk, "/D0"); + status += fx_directory_create(&ram_disk, "/E0"); + status += fx_directory_create(&ram_disk, "/F0"); + status += fx_directory_create(&ram_disk, "/G0"); + status += fx_directory_create(&ram_disk, "/H0"); + status += fx_directory_create(&ram_disk, "/I0"); + status += fx_file_create(&ram_disk, "not_a_dir"); + return_if_fail( status == FX_SUCCESS); + + /* Attempt to create the same directory again. */ + status = fx_directory_create(&ram_disk, "/A0"); + return_if_fail( status == FX_ALREADY_CREATED); + + /* Create the next level of sub-directories. */ + status = fx_directory_create(&ram_disk, "/A0/A1"); + status += fx_directory_create(&ram_disk, "/A0/A2"); + status += fx_directory_create(&ram_disk, "/A0/A3"); + status += fx_directory_create(&ram_disk, "/A0/A4"); + status += fx_directory_create(&ram_disk, "/A0/A5"); + + status += fx_directory_create(&ram_disk, "/B0/B1"); + status += fx_directory_create(&ram_disk, "/B0/B2"); + status += fx_directory_create(&ram_disk, "/B0/B3"); + status += fx_directory_create(&ram_disk, "/B0/B4"); + status += fx_directory_create(&ram_disk, "/B0/B5"); + + status += fx_directory_create(&ram_disk, "/C0/C1"); + status += fx_directory_create(&ram_disk, "/C0/C2"); + status += fx_directory_create(&ram_disk, "/C0/C3"); + status += fx_directory_create(&ram_disk, "/C0/C4"); + status += fx_directory_create(&ram_disk, "/C0/C5"); + + status += fx_directory_create(&ram_disk, "/D0/D1"); + status += fx_directory_create(&ram_disk, "/D0/D2"); + status += fx_directory_create(&ram_disk, "/D0/D3"); + status += fx_directory_create(&ram_disk, "/D0/D4"); + status += fx_directory_create(&ram_disk, "/D0/D5"); + + status += fx_directory_create(&ram_disk, "/E0/E1"); + status += fx_directory_create(&ram_disk, "/E0/E2"); + status += fx_directory_create(&ram_disk, "/E0/E3"); + status += fx_directory_create(&ram_disk, "/E0/E4"); + status += fx_directory_create(&ram_disk, "/E0/E5"); + + status += fx_directory_create(&ram_disk, "/F0/F1"); + status += fx_directory_create(&ram_disk, "/F0/F2"); + status += fx_directory_create(&ram_disk, "/F0/F3"); + status += fx_directory_create(&ram_disk, "/F0/F4"); + status += fx_directory_create(&ram_disk, "/F0/F5"); + + status += fx_directory_create(&ram_disk, "/G0/G1"); + status += fx_directory_create(&ram_disk, "/G0/G2"); + status += fx_directory_create(&ram_disk, "/G0/G3"); + status += fx_directory_create(&ram_disk, "/G0/G4"); + status += fx_directory_create(&ram_disk, "/G0/G5"); + + status += fx_directory_create(&ram_disk, "/H0/H1"); + status += fx_directory_create(&ram_disk, "/H0/H2"); + status += fx_directory_create(&ram_disk, "/H0/H3"); + status += fx_directory_create(&ram_disk, "/H0/H4"); + status += fx_directory_create(&ram_disk, "/H0/H5"); + + status += fx_directory_create(&ram_disk, "/I0/I1"); + status += fx_directory_create(&ram_disk, "/I0/I2"); + status += fx_directory_create(&ram_disk, "/I0/I3"); + status += fx_directory_create(&ram_disk, "/I0/I4"); + status += fx_directory_create(&ram_disk, "/I0/I5"); + return_if_fail( status == FX_SUCCESS); + + /* Now create the third level of sub-directories... */ + status = fx_directory_create(&ram_disk, "/A0/A1/A00"); + status += fx_directory_create(&ram_disk, "/B0/B2/B00"); + status += fx_directory_create(&ram_disk, "/C0/C3/C00"); + status += fx_directory_create(&ram_disk, "/D0/D4/D00"); + status += fx_directory_create(&ram_disk, "/E0/E5/E00"); + status += fx_directory_create(&ram_disk, "/F0/F1/F00"); + status += fx_directory_create(&ram_disk, "/G0/G1/G00"); + status += fx_directory_create(&ram_disk, "/H0/H2/H00"); + status += fx_directory_create(&ram_disk, "/I0/I3/I00"); + return_if_fail( status == FX_SUCCESS); + + /* Attempt to delete something that is not a directory */ + status = fx_directory_delete(&ram_disk, "not_a_dir"); + return_if_fail( status == FX_NOT_DIRECTORY); + + /* Attempt to delete a directory that is not empty */ + status = fx_directory_delete(&ram_disk, "/A0"); + return_if_fail( status == FX_DIR_NOT_EMPTY); + + /* Attempt to delete a directory that is read only */ + status = fx_directory_attributes_read(&ram_disk, "/A0/A1/A00", &attributes); + status += fx_directory_attributes_set(&ram_disk, "/A0/A1/A00", FX_READ_ONLY); + status += fx_directory_delete(&ram_disk, "/A0/A1/A00"); + return_if_fail( status == FX_WRITE_PROTECT); + status += fx_directory_attributes_set(&ram_disk, "/A0/A1/A00", attributes); + + /* Now delete all the directories. */ + status = fx_directory_delete(&ram_disk, "/A0/A1/A00"); + status += fx_directory_delete(&ram_disk, "/B0/B2/B00"); + status += fx_directory_delete(&ram_disk, "/C0/C3/C00"); + status += fx_directory_delete(&ram_disk, "/D0/D4/D00"); + status += fx_directory_delete(&ram_disk, "/E0/E5/E00"); + status += fx_directory_delete(&ram_disk, "/F0/F1/F00"); + status += fx_directory_delete(&ram_disk, "/G0/G1/G00"); + status += fx_directory_delete(&ram_disk, "/H0/H2/H00"); + status += fx_directory_delete(&ram_disk, "/I0/I3/I00"); + return_if_fail( status == FX_SUCCESS); + + /* Delete the next level of sub-directories. */ + status += fx_directory_delete(&ram_disk, "/A0/A1"); + status += fx_directory_delete(&ram_disk, "/A0/A2"); + status += fx_directory_delete(&ram_disk, "/A0/A3"); + status += fx_directory_delete(&ram_disk, "/A0/A4"); + status += fx_directory_delete(&ram_disk, "/A0/A5"); + + status += fx_directory_delete(&ram_disk, "/B0/B1"); + status += fx_directory_delete(&ram_disk, "/B0/B2"); + status += fx_directory_delete(&ram_disk, "/B0/B3"); + status += fx_directory_delete(&ram_disk, "/B0/B4"); + status += fx_directory_delete(&ram_disk, "/B0/B5"); + + status += fx_directory_delete(&ram_disk, "/C0/C1"); + status += fx_directory_delete(&ram_disk, "/C0/C2"); + status += fx_directory_delete(&ram_disk, "/C0/C3"); + status += fx_directory_delete(&ram_disk, "/C0/C4"); + status += fx_directory_delete(&ram_disk, "/C0/C5"); + + status += fx_directory_delete(&ram_disk, "/D0/D1"); + status += fx_directory_delete(&ram_disk, "/D0/D2"); + status += fx_directory_delete(&ram_disk, "/D0/D3"); + status += fx_directory_delete(&ram_disk, "/D0/D4"); + status += fx_directory_delete(&ram_disk, "/D0/D5"); + + status += fx_directory_delete(&ram_disk, "/E0/E1"); + status += fx_directory_delete(&ram_disk, "/E0/E2"); + status += fx_directory_delete(&ram_disk, "/E0/E3"); + status += fx_directory_delete(&ram_disk, "/E0/E4"); + status += fx_directory_delete(&ram_disk, "/E0/E5"); + + status += fx_directory_delete(&ram_disk, "/F0/F1"); + status += fx_directory_delete(&ram_disk, "/F0/F2"); + status += fx_directory_delete(&ram_disk, "/F0/F3"); + status += fx_directory_delete(&ram_disk, "/F0/F4"); + status += fx_directory_delete(&ram_disk, "/F0/F5"); + + status += fx_directory_delete(&ram_disk, "/G0/G1"); + status += fx_directory_delete(&ram_disk, "/G0/G2"); + status += fx_directory_delete(&ram_disk, "/G0/G3"); + status += fx_directory_delete(&ram_disk, "/G0/G4"); + status += fx_directory_delete(&ram_disk, "/G0/G5"); + + status += fx_directory_delete(&ram_disk, "/H0/H1"); + status += fx_directory_delete(&ram_disk, "/H0/H2"); + status += fx_directory_delete(&ram_disk, "/H0/H3"); + status += fx_directory_delete(&ram_disk, "/H0/H4"); + status += fx_directory_delete(&ram_disk, "/H0/H5"); + + status += fx_directory_delete(&ram_disk, "/I0/I1"); + status += fx_directory_delete(&ram_disk, "/I0/I2"); + status += fx_directory_delete(&ram_disk, "/I0/I3"); + status += fx_directory_delete(&ram_disk, "/I0/I4"); + status += fx_directory_delete(&ram_disk, "/I0/I5"); + return_if_fail( status == FX_SUCCESS); + + /* Delete a series of directories... */ + status = fx_directory_delete(&ram_disk, "/A0"); + status += fx_directory_delete(&ram_disk, "/B0"); + status += fx_directory_delete(&ram_disk, "/C0"); + status += fx_directory_delete(&ram_disk, "/D0"); + status += fx_directory_delete(&ram_disk, "/E0"); + status += fx_directory_delete(&ram_disk, "/F0"); + status += fx_directory_delete(&ram_disk, "/G0"); + status += fx_directory_delete(&ram_disk, "/H0"); + status += fx_directory_delete(&ram_disk, "/I0"); + return_if_fail( status == FX_SUCCESS); + + /* Attempt to delete a directory again. */ + status = fx_directory_delete(&ram_disk, "/A0"); + return_if_fail( status == FX_NOT_FOUND); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4096, // Total sectors + 128, // Sector size + 2, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + return_if_fail( status == FX_SUCCESS); + + /* Create a directory name that is too large. */ + for (i = 0; i < FX_MAX_LONG_NAME_LEN+1; i++) + { + + name[i] = 'a'; + } + name[FX_MAX_LONG_NAME_LEN + 1] = 0; + + /* Attempt to create a directory with too long of a name. */ + status = fx_directory_create(&ram_disk, name); + return_if_fail( status == FX_INVALID_NAME); + + /* Attempt to create a directory with no more clusters. */ + temp = ram_disk.fx_media_total_clusters; + ram_disk.fx_media_total_clusters = 0; + status = fx_directory_create(&ram_disk, "sub1"); + ram_disk.fx_media_total_clusters = temp; + return_if_fail( status == FX_NO_MORE_SPACE); + + /* Attempt to create a directory with a FAT read error. */ + _fx_utility_fat_entry_read_error_request = 1; + status = fx_directory_create(&ram_disk, "sub1"); + _fx_utility_fat_entry_read_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Create a subdirectory with a search pointer that must wrap after the allocation. */ + ram_disk.fx_media_cluster_search_start = ram_disk.fx_media_total_clusters + (FX_FAT_ENTRY_START - 1); + status = fx_directory_create(&ram_disk, "sub1"); + return_if_fail( status == FX_SUCCESS); + + /* Create a subdirectory with a search pointer that must wrap after the allocation. */ + ram_disk.fx_media_cluster_search_start = ram_disk.fx_media_total_clusters + (FX_FAT_ENTRY_START - 1); + status = fx_directory_create(&ram_disk, "sub2"); + return_if_fail( status == FX_SUCCESS); + + /* Create a subdirectory with a search pointer that starts on a non-free cluster. */ + ram_disk.fx_media_cluster_search_start = FX_FAT_ENTRY_START; + status = fx_directory_create(&ram_disk, "sub3"); + return_if_fail( status == FX_SUCCESS); + + /* Create a subdirectory with a logical sector read error. */ + _fx_utility_logical_sector_read_error_request = 13; + status = fx_directory_create(&ram_disk, "sub4"); + _fx_utility_logical_sector_read_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + +#ifndef FX_DISABLE_CACHE + /* Create a subdirectory with a logical flush error. */ + _fx_utility_logical_sector_flush_error_request = 1; + status = fx_directory_create(&ram_disk, "sub5"); + _fx_utility_logical_sector_flush_error_request = 0; + return_if_fail( status == FX_IO_ERROR); +#endif + + /* Create a subdirectory with an error clearing the additional sectors of a cluster. */ + _fx_ram_driver_io_error_request = 8; + status = fx_directory_create(&ram_disk, "sub6"); + _fx_ram_driver_io_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Create a subdirectory with an error on writing the FAT entry. */ + _fx_utility_fat_entry_write_error_request = 1; + status = fx_directory_create(&ram_disk, "sub7"); + _fx_utility_fat_entry_write_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Create a subdirectory with an error on writing the first director entry. */ + _fx_directory_entry_write_error_request = 1; + status = fx_directory_create(&ram_disk, "sub8"); + _fx_directory_entry_write_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Create a subdirectory with an error on writing the second director entry. */ + _fx_directory_entry_write_error_request = 2; + status = fx_directory_create(&ram_disk, "sub9"); + _fx_directory_entry_write_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4096, // Total sectors + 128, // Sector size + 2, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + return_if_fail( status == FX_SUCCESS); + + /* Create a series of sub-directories... in preparation for our directory delete checking. */ + status = fx_directory_create(&ram_disk, "SUB1"); + status += fx_directory_create(&ram_disk, "SUB2"); + status += fx_directory_create(&ram_disk, "SUB3"); + status += fx_directory_create(&ram_disk, "SUB4"); + status += fx_directory_create(&ram_disk, "SUB5"); + status += fx_directory_create(&ram_disk, "SUB6"); + status += fx_directory_create(&ram_disk, "SUB7"); + status += fx_directory_create(&ram_disk, "SUB8"); + return_if_fail( status == FX_SUCCESS); + + /* Delete a directory but with a FAT read error on the first cluster. */ + _fx_utility_fat_entry_read_error_request = 1; + status = fx_directory_delete(&ram_disk, "SUB8"); + _fx_utility_fat_entry_read_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Delete a directory but with a cluster error - bad small value. */ + _fx_utility_fat_entry_read_error_request = 10001; + status = fx_directory_delete(&ram_disk, "SUB8"); + _fx_utility_fat_entry_read_error_request = 0; + return_if_fail( status == FX_FAT_READ_ERROR); + + /* Delete a directory but with a cluster error - bad total clusters. */ + temp = ram_disk.fx_media_total_clusters; + ram_disk.fx_media_total_clusters = 0; + status = fx_directory_delete(&ram_disk, "SUB8"); + ram_disk.fx_media_total_clusters = temp; + return_if_fail( status == FX_FAT_READ_ERROR); + + /* Delete a directory but with a cluster error - same value. */ + _fx_utility_fat_entry_read_error_request = 30001; + status = fx_directory_delete(&ram_disk, "SUB8"); + _fx_utility_fat_entry_read_error_request = 0; + return_if_fail( status == FX_FAT_READ_ERROR); + + /* Delete a directory but with a directory entry read error. */ + _fx_directory_entry_read_error_request = 1; + status = fx_directory_delete(&ram_disk, "SUB8"); + _fx_directory_entry_read_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Delete a directory but with a directory entry write error. */ + _fx_directory_entry_write_error_request = 1; + status = fx_directory_delete(&ram_disk, "SUB8"); + _fx_directory_entry_write_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Delete a directory wtih large error request values just to exercise those paths - should be successful. */ + _fx_directory_entry_read_error_request = 10000; + _fx_directory_entry_write_error_request = 10000; + status = fx_directory_delete(&ram_disk, "SUB8"); + _fx_directory_entry_read_error_request = 0; + _fx_directory_entry_write_error_request = 0; + return_if_fail( status == FX_SUCCESS); + + /* Delete a directory with an error in the freeing of the cluster traversal FAT read. */ + _fx_utility_fat_entry_read_error_request = 2; + status = fx_directory_delete(&ram_disk, "SUB7"); + _fx_utility_fat_entry_read_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Delete a directory but with a cluster error in releasing cluster - bad small value. */ + _fx_utility_fat_entry_read_error_request = 10002; + status = fx_directory_delete(&ram_disk, "SUB6"); + _fx_utility_fat_entry_read_error_request = 0; + return_if_fail( status == FX_FAT_READ_ERROR); + + /* Delete a directory but with a cluster error in releasing cluster - bad total clusters. */ + temp = ram_disk.fx_media_total_clusters; + _fx_utility_fat_entry_read_error_request = 40002; + status = fx_directory_delete(&ram_disk, "SUB5"); + ram_disk.fx_media_total_clusters = temp; + return_if_fail( status == FX_FAT_READ_ERROR); + + /* Delete a directory but with a cluster error in releasing cluster - same value. */ + _fx_utility_fat_entry_read_error_request = 30002; + status = fx_directory_delete(&ram_disk, "SUB4"); + _fx_utility_fat_entry_read_error_request = 0; + return_if_fail( status == FX_FAT_READ_ERROR); + + /* Delete a directory but with a FAT write error in releasing cluster. */ + _fx_utility_fat_entry_write_error_request = 1; + status = fx_directory_delete(&ram_disk, "SUB3"); + _fx_utility_fat_entry_write_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 14000, // Total sectors + 128, // Sector size + 2, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + return_if_fail( status == FX_SUCCESS); + + /* Create a sub-directory to work from. */ + status = fx_directory_create(&ram_disk, "SUB1"); + + /* Set the default path here. */ + status += fx_directory_default_set(&ram_disk, "SUB1"); + + /* Now try to create some unusual file name to test the internal free search logic. */ + status += fx_file_create(&ram_disk, "..test"); + return_if_fail( status == FX_INVALID_NAME); + + /* Create a name with a character greater than 127. */ + name[0] = 'n'; + name[1] = 'a'; + name[2] = 'm'; + name[3] = 'e'; + name[4] = '\''; + name[5] = ')'; + name[6] = '('; + name[7] = '`'; + name[8] = (CHAR) 128; + name[9] = 0; + + status = fx_file_create(&ram_disk, name); + status += fx_file_open(&ram_disk, &file_1, name, FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + /* Create a name with a bunch of special characters. */ + status = fx_file_create(&ram_disk, "name-a_b}{()'!#$&@^%+,;=[]"); + status += fx_file_open(&ram_disk, &file_2, "name-a_b}{()'!#$&@^%+,;=[]", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + /* Create a name with an invalid character. */ + name[0] = 'n'; + name[1] = 'a'; + name[2] = 'm'; + name[3] = 'e'; + name[4] = (CHAR) 5; + name[5] = '\''; + name[6] = 0; + + status = fx_file_create(&ram_disk, name); + return_if_fail( status == FX_INVALID_NAME); + + /* Create a long file name by position of the dot. */ + status = fx_file_create(&ram_disk, "MYLONGERN.AM"); + status += fx_file_create(&ram_disk, "MY.TXT"); + status += fx_file_open(&ram_disk, &file_3, "MYLONGERN.AM", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_4, "MY.TXT", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + /* Create a set of very short long file name. */ + status = fx_file_create(&ram_disk, "n"); + status += fx_file_create(&ram_disk, "nW"); + status += fx_file_create(&ram_disk, "nWW"); + status += fx_file_create(&ram_disk, "nWWW"); + status += fx_file_create(&ram_disk, "nWWWW"); + status += fx_file_create(&ram_disk, "nWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWWWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWWWWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWWWWWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWWWWWWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"); + status += fx_file_create(&ram_disk, "nWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"); + status += fx_file_open(&ram_disk, &file_5, "n", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_6, "nW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_7, "nWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_8, "nWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_9, "nWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_10, "nWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_11, "nWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_12, "nWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_13, "nWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_14, "nWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_15, "nWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_16, "nWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_17, "nWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_18, "nWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_19, "nWWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_20, "nWWWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_21, "nWWWWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_22, "nWWWWWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_23, "nWWWWWWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_24, "nWWWWWWWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_25, "nWWWWWWWWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_26, "nWWWWWWWWWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_27, "nWWWWWWWWWWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_28, "nWWWWWWWWWWWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_29, "nWWWWWWWWWWWWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_30, "nWWWWWWWWWWWWWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_31, "nWWWWWWWWWWWWWWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_32, "nWWWWWWWWWWWWWWWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_33, "nWWWWWWWWWWWWWWWWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_34, "nWWWWWWWWWWWWWWWWWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_35, "nWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_36, "nWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_37, "nWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_38, "nWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_39, "nWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_40, "nWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_41, "nWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_42, "nWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + /* Build a super long file name that will require additional cluster allocation in the sub-directory. */ + for (i = 0; i < FX_MAX_LONG_NAME_LEN; i++) + { + name[i] = 'a'; + } + name[FX_MAX_LONG_NAME_LEN-2] = 0; + + /* Test the wrap of the FAT table by allocating at the end. */ + ram_disk.fx_media_cluster_search_start = ram_disk.fx_media_total_clusters + (FX_FAT_ENTRY_START-1); + status = fx_file_create(&ram_disk, name); + status += fx_file_open(&ram_disk, &file_43, name, FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + /* Change the name to create a new file name. */ + name[FX_MAX_LONG_NAME_LEN-3] = '0'; + + /* Test the wrap of the FAT table by allocating at the end. */ + ram_disk.fx_media_cluster_search_start = ram_disk.fx_media_total_clusters + (FX_FAT_ENTRY_START-1); + status += fx_file_create(&ram_disk, name); + status += fx_file_open(&ram_disk, &file_44, name, FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + name[FX_MAX_LONG_NAME_LEN-3] = '1'; + + /* Now try to allocate an entry with no more available clusters. */ + temp = ram_disk.fx_media_available_clusters; + ram_disk.fx_media_available_clusters = 0; + status += fx_file_create(&ram_disk, name); + ram_disk.fx_media_available_clusters = temp; + return_if_fail( status == FX_NO_MORE_SPACE); + + name[FX_MAX_LONG_NAME_LEN-3] = '2'; + + /* Now try to allocate an entry with no more total clusters. */ + temp = ram_disk.fx_media_total_clusters; + _fx_utility_fat_entry_read_error_request = 40064; + ram_disk.fx_media_cluster_search_start = ram_disk.fx_media_total_clusters + (FX_FAT_ENTRY_START-1); + status = fx_file_create(&ram_disk, name); + _fx_utility_fat_entry_read_error_request = 0; + ram_disk.fx_media_total_clusters = temp; + return_if_fail( status == FX_NO_MORE_SPACE); + + /* Now try to allocate a new cluster with a FAT read error in fx_directory_free_search. */ + temp = ram_disk.fx_media_total_clusters; + _fx_utility_fat_entry_read_error_request = 65; + ram_disk.fx_media_cluster_search_start = ram_disk.fx_media_total_clusters + (FX_FAT_ENTRY_START-1); + status = fx_file_create(&ram_disk, name); + _fx_utility_fat_entry_read_error_request = 0; + ram_disk.fx_media_total_clusters = temp; + return_if_fail( status == FX_IO_ERROR); + + /* Now try to allocate a new cluster with a logical sector read error in fx_directory_free_search. */ + _fx_utility_logical_sector_read_error_request = 172; + ram_disk.fx_media_cluster_search_start = ram_disk.fx_media_total_clusters + (FX_FAT_ENTRY_START-1); + status = fx_file_create(&ram_disk, name); + _fx_utility_logical_sector_read_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + +#ifndef FX_DISABLE_CACHE + /* Now try to allocate a new cluster with a logical sector flush error in fx_directory_free_search. */ + _fx_utility_logical_sector_flush_error_request = 1; + ram_disk.fx_media_cluster_search_start = ram_disk.fx_media_total_clusters + (FX_FAT_ENTRY_START-1); + status = fx_file_create(&ram_disk, name); + _fx_utility_logical_sector_flush_error_request = 0; + return_if_fail( status == FX_IO_ERROR); +#endif + + /* Now try to allocate a new cluster with a write error when clearing additional sector clusters from fx_directory_free_search. */ + _fx_ram_driver_io_error_request = 91; + ram_disk.fx_media_cluster_search_start = ram_disk.fx_media_total_clusters + (FX_FAT_ENTRY_START-1); + status = fx_file_create(&ram_disk, name); + _fx_ram_driver_io_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Now try to allocate a new cluster with a write error when writing the FAT link from fx_directory_free_search. */ + _fx_utility_fat_entry_write_error_request = 1; + ram_disk.fx_media_cluster_search_start = ram_disk.fx_media_total_clusters + (FX_FAT_ENTRY_START-1); + status = fx_file_create(&ram_disk, name); + _fx_utility_fat_entry_write_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Now try to allocate a new cluster with a write error when writing the EOF FAT link from fx_directory_free_search. */ + _fx_utility_fat_entry_write_error_request = 3; + ram_disk.fx_media_cluster_search_start = ram_disk.fx_media_total_clusters + (FX_FAT_ENTRY_START-1); + status = fx_file_create(&ram_disk, name); + _fx_utility_fat_entry_write_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Now try to allocate a new cluster with a logical sector write error when writing the new sub-directory sector in fx_directory_free_search. */ + _fx_utility_logical_sector_write_error_request = 1; + ram_disk.fx_media_cluster_search_start = ram_disk.fx_media_total_clusters + (FX_FAT_ENTRY_START-1); + status = fx_file_create(&ram_disk, name); + _fx_utility_logical_sector_write_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Now try to allocate a new cluster with a total cluster of 0 in fx_directory_free_search. */ + _fx_utility_fat_entry_read_error_request = 92; + ram_disk.fx_media_cluster_search_start = ram_disk.fx_media_total_clusters + (FX_FAT_ENTRY_START-1); + status = fx_file_create(&ram_disk, name); + _fx_utility_fat_entry_read_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Now try to allocate a new cluster with a bad read value (1) of intial cluster in sub-directory in fx_directory_free_search. */ + _fx_utility_fat_entry_read_error_request = 10095; + ram_disk.fx_media_cluster_search_start = ram_disk.fx_media_total_clusters + (FX_FAT_ENTRY_START-1); + status = fx_file_create(&ram_disk, name); + _fx_utility_fat_entry_read_error_request = 0; + return_if_fail( status == FX_FAT_READ_ERROR); + + /* Now try to allocate a new cluster with a bad read value (same as initial cluster) of intial cluster in sub-directory in fx_directory_free_search. */ + _fx_utility_fat_entry_read_error_request = 30098; + ram_disk.fx_media_cluster_search_start = ram_disk.fx_media_total_clusters + (FX_FAT_ENTRY_START-1); + status = fx_file_create(&ram_disk, name); + _fx_utility_fat_entry_read_error_request = 0; + return_if_fail( status == FX_FAT_READ_ERROR); + + /* Now try to allocate a new cluster with a bad read value (greater than max) of intial cluster in sub-directory in fx_directory_free_search. */ + temp = ram_disk.fx_media_total_clusters; + _fx_utility_fat_entry_read_error_request = 40101; + ram_disk.fx_media_cluster_search_start = ram_disk.fx_media_total_clusters + (FX_FAT_ENTRY_START-1); + status = fx_file_create(&ram_disk, name); + _fx_utility_fat_entry_read_error_request = 0; + ram_disk.fx_media_total_clusters = temp; + return_if_fail( status == FX_FAT_READ_ERROR); + + /* Now try to allocate a new cluster with an error linking the new cluster chain with the old one in sub-directory in fx_directory_free_search. */ + _fx_utility_fat_entry_write_error_request = 4; + ram_disk.fx_media_cluster_search_start = ram_disk.fx_media_total_clusters + (FX_FAT_ENTRY_START-1); + status = fx_file_create(&ram_disk, name); + _fx_utility_fat_entry_write_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Now write to each file before we close them. */ + status = fx_file_write(&file_1, "1", 1); + status += fx_file_write(&file_2, "1", 1); + status += fx_file_write(&file_3, "1", 1); + status += fx_file_write(&file_4, "1", 1); + status += fx_file_write(&file_5, "1", 1); + status += fx_file_write(&file_6, "1", 1); + status += fx_file_write(&file_7, "1", 1); + status += fx_file_write(&file_8, "1", 1); + status += fx_file_write(&file_9, "1", 1); + status += fx_file_write(&file_10, "1", 1); + status += fx_file_write(&file_11, "1", 1); + status += fx_file_write(&file_12, "1", 1); + status += fx_file_write(&file_13, "1", 1); + status += fx_file_write(&file_14, "1", 1); + status += fx_file_write(&file_15, "1", 1); + status += fx_file_write(&file_16, "1", 1); + status += fx_file_write(&file_17, "1", 1); + status += fx_file_write(&file_18, "1", 1); + status += fx_file_write(&file_19, "1", 1); + status += fx_file_write(&file_20, "1", 1); + status += fx_file_write(&file_21, "1", 1); + status += fx_file_write(&file_22, "1", 1); + status += fx_file_write(&file_23, "1", 1); + status += fx_file_write(&file_24, "1", 1); + status += fx_file_write(&file_25, "1", 1); + status += fx_file_write(&file_26, "1", 1); + status += fx_file_write(&file_27, "1", 1); + status += fx_file_write(&file_28, "1", 1); + status += fx_file_write(&file_29, "1", 1); + status += fx_file_write(&file_30, "1", 1); + status += fx_file_write(&file_31, "1", 1); + status += fx_file_write(&file_32, "1", 1); + status += fx_file_write(&file_33, "1", 1); + status += fx_file_write(&file_34, "1", 1); + status += fx_file_write(&file_35, "1", 1); + status += fx_file_write(&file_36, "1", 1); + status += fx_file_write(&file_37, "1", 1); + status += fx_file_write(&file_38, "1", 1); + status += fx_file_write(&file_39, "1", 1); + status += fx_file_write(&file_40, "1", 1); + status += fx_file_write(&file_41, "1", 1); + status += fx_file_write(&file_42, "1", 1); + status += fx_file_write(&file_43, "1", 1); + status += fx_file_write(&file_44, "1", 1); + status += fx_file_close(&file_1); + status += fx_file_close(&file_2); + status += fx_file_close(&file_3); + status += fx_file_close(&file_4); + status += fx_file_close(&file_5); + status += fx_file_close(&file_6); + status += fx_file_close(&file_7); + status += fx_file_close(&file_8); + status += fx_file_close(&file_9); + status += fx_file_close(&file_10); + status += fx_file_close(&file_11); + status += fx_file_close(&file_12); + status += fx_file_close(&file_13); + status += fx_file_close(&file_14); + status += fx_file_close(&file_15); + status += fx_file_close(&file_16); + status += fx_file_close(&file_17); + status += fx_file_close(&file_18); + status += fx_file_close(&file_19); + status += fx_file_close(&file_20); + status += fx_file_close(&file_21); + status += fx_file_close(&file_22); + status += fx_file_close(&file_23); + status += fx_file_close(&file_24); + status += fx_file_close(&file_25); + status += fx_file_close(&file_26); + status += fx_file_close(&file_27); + status += fx_file_close(&file_28); + status += fx_file_close(&file_29); + status += fx_file_close(&file_30); + status += fx_file_close(&file_31); + status += fx_file_close(&file_32); + status += fx_file_close(&file_33); + status += fx_file_close(&file_34); + status += fx_file_close(&file_35); + status += fx_file_close(&file_36); + status += fx_file_close(&file_37); + status += fx_file_close(&file_38); + status += fx_file_close(&file_39); + status += fx_file_close(&file_40); + status += fx_file_close(&file_41); + status += fx_file_close(&file_42); + status += fx_file_close(&file_43); + status += fx_file_close(&file_44); + return_if_fail( status == FX_SUCCESS); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Set the flag to copy the default format to get NT file name, orphan long file name, and super long file names. */ + _fx_ram_driver_copy_default_format = 1; + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + _fx_ram_driver_copy_default_format = 0; + return_if_fail( status == FX_SUCCESS); + + /* Get the first file name, this should be "ntfile" for exercising the NT file name capability. */ + status = fx_directory_first_entry_find(&ram_disk, name); + status += fx_file_open(&ram_disk, &file_1, name, FX_OPEN_FOR_WRITE); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_file_open(&ram_disk, &file_2, name, FX_OPEN_FOR_WRITE); + status += fx_directory_next_entry_find(&ram_disk, name); + + /* Write to both files, write to them and close them! */ + status += fx_file_write(&file_1, "abcd", 4); + status += fx_file_write(&file_2, "abcd", 4); + status += fx_file_close(&file_1); + status += fx_file_close(&file_2); + return_if_fail( status == FX_SUCCESS); + + /* Now look move to the first sub-directory. */ + status = fx_directory_default_set(&ram_disk, name); + status += fx_directory_first_entry_find(&ram_disk, name); /* . */ + status += fx_directory_next_entry_find(&ram_disk, name); /* .. */ + status += fx_directory_next_entry_find(&ram_disk, name); /* Too long of a file name. */ + status += fx_file_delete(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); /* Next too long sub-directory. */ + status += fx_directory_default_set(&ram_disk, name); /* Move to next sub-directory. */ + return_if_fail( status == FX_SUCCESS); + + /* Move back to the root directory. */ + status += fx_directory_default_set(&ram_disk, "\\"); + + /* Set an all blank volume name. */ + status += fx_media_volume_set(&ram_disk, " "); + status += fx_media_volume_get(&ram_disk, volume_name, FX_DIRECTORY_SECTOR); + status += fx_directory_first_entry_find(&ram_disk, name); /* ntfile */ + status += fx_directory_next_entry_find(&ram_disk, name); /* nt-file~.txt */ + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Set the flag to copy the default format to get NT file name, orphan long file name, and super long file names. */ + _fx_ram_driver_copy_default_format = 1; + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + _fx_ram_driver_copy_default_format = 0; + return_if_fail( status == FX_SUCCESS); + + /* Get the first shorted file name. */ + status = fx_directory_first_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_file_open(&ram_disk, &file_2, name, FX_OPEN_FOR_WRITE); + + /* Write to the shorted file. */ + status += fx_file_write(&file_2, "abcd", 4); + + /* Now close the file, which will write the shorted file entry out. */ + _fx_utility_logical_sector_write_error_request = 1; + status += fx_file_close(&file_2); + _fx_utility_logical_sector_write_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Set the flag to copy the default format to get NT file name, orphan long file name, and super long file names. */ + _fx_ram_driver_copy_default_format = 1; + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + _fx_ram_driver_copy_default_format = 0; + return_if_fail( status == FX_SUCCESS); + + /* Get the first shorted file name. */ + status = fx_directory_first_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_file_open(&ram_disk, &file_2, name, FX_OPEN_FOR_WRITE); + + /* Write to the shorted file. */ + status += fx_file_write(&file_2, "abcd", 4); + + /* Now close the file, which will write the shorted file entry out. */ + temp = ram_disk.fx_media_data_sector_start; + ram_disk.fx_media_data_sector_start = ram_disk.fx_media_root_sector_start+2; + status += fx_file_close(&file_2); + ram_disk.fx_media_data_sector_start = temp; + return_if_fail( status == FX_FILE_CORRUPT); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Set the flag to copy the default format to get NT file name, orphan long file name, and super long file names. */ + _fx_ram_driver_copy_default_format = 1; + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + _fx_ram_driver_copy_default_format = 0; + return_if_fail( status == FX_SUCCESS); + + /* Get the first shorted file name. */ + status = fx_directory_first_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_file_open(&ram_disk, &file_2, name, FX_OPEN_FOR_WRITE); + status += fx_directory_next_entry_find(&ram_disk, name); + + /* Write to the shorted file. */ + status += fx_file_write(&file_2, "abcd", 4); + + /* Now close the file, which will write the shorted file entry out. */ + _fx_utility_logical_sector_read_error_request = 2; + status += fx_file_close(&file_2); + _fx_utility_logical_sector_read_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Set the flag to copy the default format to get NT file name, orphan long file name, and super long file names. */ + _fx_ram_driver_copy_default_format = 1; + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + _fx_ram_driver_copy_default_format = 0; + return_if_fail( status == FX_SUCCESS); + + /* Get the first shorted file name. */ + status = fx_directory_first_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_file_open(&ram_disk, &file_2, name, FX_OPEN_FOR_WRITE); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_media_flush(&ram_disk); + status += fx_media_read(&ram_disk, ram_disk.fx_media_root_sector_start+1, buffer); + buffer[0x40] = 0x1f; + status += fx_media_write(&ram_disk, ram_disk.fx_media_root_sector_start+1, buffer); + + /* Write to the shorted file. */ + status += fx_file_write(&file_2, "abcd", 4); + + /* Now close the file, which will write the shorted file entry out.... not that the shorted flag is not in the actual directory entry. */ + status += fx_file_close(&file_2); + return_if_fail( status == FX_SUCCESS); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Set the flag to copy the default format to get NT file name, orphan long file name, and super long file names. */ + _fx_ram_driver_copy_default_format = 1; + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + _fx_ram_driver_copy_default_format = 0; + return_if_fail( status == FX_SUCCESS); + + /* Get the first shorted file name. */ + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + + /* Switch to this directory. */ + status += fx_directory_default_set(&ram_disk, name); + status = fx_directory_first_entry_find(&ram_disk, name); /* . */ + status += fx_directory_next_entry_find(&ram_disk, name); /* .. */ + status += fx_directory_next_entry_find(&ram_disk, name); /* shorted long file name! */ + status += fx_file_open(&ram_disk, &file_2, name, FX_OPEN_FOR_WRITE); + + /* Write to the shorted file. */ + status += fx_file_write(&file_2, "abcd", 4); + + /* Now close the file, which will write the shorted file entry out. */ + _fx_utility_fat_entry_read_error_request = 1; + status += fx_file_close(&file_2); + _fx_utility_fat_entry_read_error_request = 0; + + /* Check the status - should be an error here. */ + if (status != FX_IO_ERROR) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(97); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(98); + } + + + /* Set the flag to copy the default format to get NT file name, orphan long file name, and super long file names. */ + _fx_ram_driver_copy_default_format = 1; + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + _fx_ram_driver_copy_default_format = 0; + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(99); + } + + /* Get the first shorted file name. */ + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + + /* Switch to this directory. */ + status += fx_directory_default_set(&ram_disk, name); + status = fx_directory_first_entry_find(&ram_disk, name); /* . */ + status += fx_directory_next_entry_find(&ram_disk, name); /* .. */ + status += fx_directory_next_entry_find(&ram_disk, name); /* shorted long file name! */ + status += fx_file_open(&ram_disk, &file_2, name, FX_OPEN_FOR_WRITE); + + /* Write to the shorted file. */ + status += fx_file_write(&file_2, "abcd", 4); + + /* Now close the file, which will write the shorted file entry out. */ + _fx_utility_fat_entry_read_error_request = 10001; + status += fx_file_close(&file_2); + _fx_utility_fat_entry_read_error_request = 0; + + /* Check the status - should be an error here. */ + if (status != FX_FILE_CORRUPT) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(100); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(101); + } + + /* Set the flag to copy the default format to get NT file name, orphan long file name, and super long file names. */ + _fx_ram_driver_copy_default_format = 1; + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + _fx_ram_driver_copy_default_format = 0; + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(102); + } + + /* Get the first shorted file name. */ + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + + /* Switch to this directory. */ + status += fx_directory_default_set(&ram_disk, name); + status = fx_directory_first_entry_find(&ram_disk, name); /* . */ + status += fx_directory_next_entry_find(&ram_disk, name); /* .. */ + status += fx_directory_next_entry_find(&ram_disk, name); /* shorted long file name! */ + status += fx_file_open(&ram_disk, &file_2, name, FX_OPEN_FOR_WRITE); + + /* Write to the shorted file. */ + status += fx_file_write(&file_2, "abcd", 4); + + /* Now close the file, which will write the shorted file entry out. */ + _fx_utility_fat_entry_read_error_request = 20001; + status += fx_file_close(&file_2); + _fx_utility_fat_entry_read_error_request = 0; + + /* Check the status - should be an error here. */ + if (status != FX_FILE_CORRUPT) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(103); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(104); + } + + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 14000, // Total sectors + 128, // Sector size + 2, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(105); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(106); + } + + /* Build a sub-directory structure to further test diretory entry read. */ + status = fx_directory_create(&ram_disk, "SUB1"); + status += fx_directory_default_set(&ram_disk, "SUB1"); + status += fx_file_create(&ram_disk, "S1_FL1.TXT"); + status += fx_file_create(&ram_disk, "S1_FL2.TXT"); + status += fx_file_create(&ram_disk, "S1_FL3.TXT"); + status += fx_file_create(&ram_disk, "S1_FL4.TXT"); + status += fx_file_create(&ram_disk, "S1_FL5.TXT"); + status += fx_file_create(&ram_disk, "S1_FL6.TXT"); + status += fx_file_create(&ram_disk, "S1_FL7.TXT"); + status += fx_file_create(&ram_disk, "S1_FL8.TXT"); + status += fx_file_create(&ram_disk, "S1_FL9.TXT"); + status += fx_file_create(&ram_disk, "S1_FL10.TXT"); + status += fx_file_create(&ram_disk, "S1_FL11.TXT"); + status += fx_file_create(&ram_disk, "S1_FL12.TXT"); + status += fx_file_create(&ram_disk, "S1_FL13.TXT"); + status += fx_file_create(&ram_disk, "S1_FL14.TXT"); + status += fx_file_create(&ram_disk, "S1_FL15.TXT"); + status += fx_file_create(&ram_disk, "S1_FL16.TXT"); + status += fx_file_create(&ram_disk, "S1_FL17.TXT"); + status += fx_file_create(&ram_disk, "S1_FL18.TXT"); + status += fx_file_create(&ram_disk, "S1_FL19.TXT"); + status += fx_file_create(&ram_disk, "S1_FL20.TXT"); + status += fx_file_create(&ram_disk, "S1_FL21.TXT"); + status += fx_file_create(&ram_disk, "S1_FL22.TXT"); + status += fx_file_create(&ram_disk, "S1_FL23.TXT"); + status += fx_file_create(&ram_disk, "S1_FL24.TXT"); + status += fx_file_create(&ram_disk, "S1_FL25.TXT"); + status += fx_file_create(&ram_disk, "S1_FL26.TXT"); + status += fx_file_create(&ram_disk, "S1_FL27.TXT"); + status += fx_file_create(&ram_disk, "S1_FL28.TXT"); + status += fx_file_create(&ram_disk, "S1_FL29.TXT"); + status += fx_file_create(&ram_disk, "S1_FL30.TXT"); + status += fx_file_create(&ram_disk, "S1_FL31.TXT"); + status += fx_file_create(&ram_disk, "S1_FL32.TXT"); + + /* Now taverse the directory tree and open files. */ + status += fx_directory_first_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + ram_disk.fx_media_default_path.fx_path_directory.fx_dir_entry_last_search_relative_cluster = 100; + status += fx_directory_next_entry_find(&ram_disk, name); + ram_disk.fx_media_default_path.fx_path_directory.fx_dir_entry_last_search_log_sector = 1000; + status += fx_directory_next_entry_find(&ram_disk, name); + ram_disk.fx_media_default_path.fx_path_directory.fx_dir_entry_last_search_byte_offset = 10000; + status += fx_directory_next_entry_find(&ram_disk, name); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(107); + } + + /* Now go back to the beginning of the directory. */ + status += fx_directory_first_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + + /* Search with I/O read error. */ + ram_disk.fx_media_default_path.fx_path_directory.fx_dir_entry_last_search_cluster = 0; /* Cause cluster traversal. */ + _fx_utility_fat_entry_read_error_request = 1; + status += fx_directory_next_entry_find(&ram_disk, name); + _fx_utility_fat_entry_read_error_request = 0; + + /* Determine if the expected I/O error is present. */ + if (status != FX_IO_ERROR) + { + + printf("ERROR!\n"); + test_control_return(108); + } + + /* Search with bad FAT error - too small. */ + ram_disk.fx_media_default_path.fx_path_directory.fx_dir_entry_last_search_cluster = 0; /* Cause cluster traversal. */ + _fx_utility_fat_entry_read_error_request = 10001; + status = fx_directory_next_entry_find(&ram_disk, name); + _fx_utility_fat_entry_read_error_request = 0; + + /* Determine if the expected I/O error is present. */ + if (status != FX_FILE_CORRUPT) + { + + printf("ERROR!\n"); + test_control_return(109); + } + + /* Search with bad FAT error - too small. */ + ram_disk.fx_media_default_path.fx_path_directory.fx_dir_entry_last_search_cluster = 0; /* Cause cluster traversal. */ + _fx_utility_fat_entry_read_error_request = 20001; + status = fx_directory_next_entry_find(&ram_disk, name); + _fx_utility_fat_entry_read_error_request = 0; + + /* Determine if the expected I/O error is present. */ + if (status != FX_FILE_CORRUPT) + { + + printf("ERROR!\n"); + test_control_return(110); + } + + /* Corrupt the media to test zero divisor checking. */ + ram_disk.fx_media_bytes_per_sector = 0; + status = fx_directory_next_entry_find(&ram_disk, name); + + /* Determine if the expected error is present. */ + if (status != FX_MEDIA_INVALID) + { + + printf("ERROR!\n"); + test_control_return(110); + } + ram_disk.fx_media_bytes_per_sector = 128; + + /* Build a large file name. */ + for (i = 0; i < 255; i++) + { + name[i] = 'a'; + } + name[255] = 0; + + /* Move back to the root directory. */ + status = fx_directory_default_set(&ram_disk, "\\"); + status += fx_unicode_file_create(&ram_disk, my_unicode_name, 15, (CHAR *)buffer); + status += fx_unicode_file_create(&ram_disk, my_unicode_name1, 15, (CHAR *)buffer); + status += fx_file_create(&ram_disk, name); + + /* Set the search to the beginning of the root directory. */ + status += fx_directory_first_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(111); + } + + /* Now generate an error on the root directory size. */ + temp = ram_disk.fx_media_root_sectors; + ram_disk.fx_media_root_sectors = 1; + status = fx_directory_next_entry_find(&ram_disk, name); + ram_disk.fx_media_root_sectors = temp; + + /* Determine if the expected I/O error is present. */ + if (status != FX_FILE_CORRUPT) + { + + printf("ERROR!\n"); + test_control_return(112); + } + + /* Now generate an error with a logical sector read error. */ + _fx_utility_logical_sector_read_error_request = 5; + status = fx_directory_next_entry_find(&ram_disk, name); + _fx_utility_logical_sector_read_error_request = 0; + + /* Determine if the expected I/O error is present. */ + if (status != FX_IO_ERROR) + { + + printf("ERROR!\n"); + test_control_return(113); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(114); + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 14000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(115); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(116); + } + + /* Build a large file name. */ + for (i = 0; i < 255; i++) + { + name[i] = 'a'; + } + name[255] = 0; + + /* Create a sub-directory. */ + status = fx_directory_create(&ram_disk, "SUB1"); + status += fx_directory_default_set(&ram_disk, "SUB1"); + status += fx_file_create(&ram_disk, name); + status += fx_directory_first_entry_find(&ram_disk, (CHAR *)buffer); + status += fx_directory_next_entry_find(&ram_disk, (CHAR *)buffer); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(117); + } + + /* Read the first entry, but with a FAT entry error. */ + _fx_utility_fat_entry_read_error_request = 1; + status = fx_directory_next_entry_find(&ram_disk, (CHAR *)buffer); + _fx_utility_fat_entry_read_error_request = 0; + + /* Determine if the expected I/O error is present. */ + if (status != FX_IO_ERROR) + { + + printf("ERROR!\n"); + test_control_return(118); + } + + /* Read the first entry, but with a FAT entry error - too small. */ + _fx_utility_fat_entry_read_error_request = 10001; + status = fx_directory_next_entry_find(&ram_disk, (CHAR *)buffer); + _fx_utility_fat_entry_read_error_request = 0; + + /* Determine if the expected I/O error is present. */ + if (status != FX_FILE_CORRUPT) + { + + printf("ERROR!\n"); + test_control_return(119); + } + + /* Read the first entry, but with a FAT entry error - too big. */ + _fx_utility_fat_entry_read_error_request = 20001; + status = fx_directory_next_entry_find(&ram_disk, (CHAR *)buffer); + _fx_utility_fat_entry_read_error_request = 0; + + /* Determine if the expected I/O error is present. */ + if (status != FX_FILE_CORRUPT) + { + + printf("ERROR!\n"); + test_control_return(120); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(121); + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 512, // Directory Entries + 0, // Hidden sectors + 14000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(122); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(123); + } + + /* Build a large file name. */ + for (i = 0; i < 255; i++) + { + name[i] = 'a'; + } + name[255] = 0; + + /* Create a large file name in the root directory. */ + _fx_utility_logical_sector_write_error_request = 22; + status = fx_file_create(&ram_disk, name); + _fx_utility_logical_sector_write_error_request = 0; + + /* Setup an I/O error on the first logical sectory write of the directory entry. */ + status += fx_media_flush(&ram_disk); + + /* Determine if have the expected I/O error. */ + if (status != FX_IO_ERROR) + { + + printf("ERROR!\n"); + test_control_return(124); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(125); + } + + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 512, // Directory Entries + 0, // Hidden sectors + 14000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(126); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(127); + } + + /* Build a large file name. */ + for (i = 0; i < 255; i++) + { + name[i] = 'a'; + } + name[255] = 0; + + /* Create a large file name in the root directory. */ + _fx_utility_logical_sector_read_error_request = 45; + status = fx_file_create(&ram_disk, name); + _fx_utility_logical_sector_read_error_request = 0; + + /* Setup an I/O error on the first logical sectory write of the directory entry. */ + status += fx_media_flush(&ram_disk); + + /* Determine if have the expected I/O error. */ + if (status != FX_IO_ERROR) + { + + printf("ERROR!\n"); + test_control_return(128); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(129); + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 512, // Directory Entries + 0, // Hidden sectors + 14000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(130); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(131); + } + + /* Build a large file name. */ + for (i = 0; i < 255; i++) + { + name[i] = 'a'; + } + name[255] = 0; + + /* Create a large file name in the root directory. */ + temp = ram_disk.fx_media_data_sector_start; + ram_disk.fx_media_data_sector_start = ram_disk.fx_media_root_sector_start+1; + status = fx_file_create(&ram_disk, name); + ram_disk.fx_media_data_sector_start = temp; + + /* Setup an I/O error on the first logical sectory write of the directory entry. */ + status += fx_media_flush(&ram_disk); + + /* Determine if have the expected error. */ + if (status != FX_FILE_CORRUPT) + { + + printf("ERROR!\n"); + test_control_return(132); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(133); + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 512, // Directory Entries + 0, // Hidden sectors + 14000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(134); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(135); + } + + /* Create a sub-directory so we can generate errors writing to the sub-directory file entry. */ + status = fx_directory_create(&ram_disk, "SUB1"); + status += fx_directory_default_set(&ram_disk, "SUB1"); + + /* Setup an I/O error on the first logical sectory write of the directory entry. */ + status += fx_media_flush(&ram_disk); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(136); + } + + /* Build a large file name. */ + for (i = 0; i < 255; i++) + { + name[i] = 'a'; + } + name[255] = 0; + + /* Create a large file name in the sub-directory with a FAT entry read error. */ + _fx_utility_fat_entry_read_error_request = 14; + status = fx_file_create(&ram_disk, name); + _fx_utility_fat_entry_read_error_request = 0; + + /* Setup an I/O error on the first logical sectory write of the directory entry. */ + status += fx_media_flush(&ram_disk); + + /* Determine if have the expected error. */ + if (status != FX_IO_ERROR) + { + + printf("ERROR!\n"); + test_control_return(137); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(138); + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 512, // Directory Entries + 0, // Hidden sectors + 14000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(139); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(140); + } + + /* Create a sub-directory so we can generate errors writing to the sub-directory file entry. */ + status = fx_directory_create(&ram_disk, "SUB1"); + status += fx_directory_default_set(&ram_disk, "SUB1"); + + /* Setup an I/O error on the first logical sectory write of the directory entry. */ + status += fx_media_flush(&ram_disk); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(141); + } + + /* Build a large file name. */ + for (i = 0; i < 255; i++) + { + name[i] = 'a'; + } + name[255] = 0; + + /* Create a large file name in the sub-directory with a FAT entry read error. */ + _fx_utility_fat_entry_read_error_request = 10014; + status = fx_file_create(&ram_disk, name); + _fx_utility_fat_entry_read_error_request = 0; + + /* Setup an I/O error on the first logical sectory write of the directory entry. */ + status += fx_media_flush(&ram_disk); + + /* Determine if have the expected error. */ + if (status != FX_FILE_CORRUPT) + { + + printf("ERROR!\n"); + test_control_return(142); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(143); + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 512, // Directory Entries + 0, // Hidden sectors + 14000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(144); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(145); + } + + /* Create a sub-directory so we can generate errors writing to the sub-directory file entry. */ + status = fx_directory_create(&ram_disk, "SUB1"); + status += fx_directory_default_set(&ram_disk, "SUB1"); + + /* Setup an I/O error on the first logical sectory write of the directory entry. */ + status += fx_media_flush(&ram_disk); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(146); + } + + /* Build a large file name. */ + for (i = 0; i < 255; i++) + { + name[i] = 'a'; + } + name[255] = 0; + + /* Create a large file name in the sub-directory with a FAT entry read error. */ + _fx_utility_fat_entry_read_error_request = 20014; + status = fx_file_create(&ram_disk, name); + _fx_utility_fat_entry_read_error_request = 0; + + /* Setup an I/O error on the first logical sectory write of the directory entry. */ + status += fx_media_flush(&ram_disk); + + /* Determine if have the expected error. */ + if (status != FX_FILE_CORRUPT) + { + + printf("ERROR!\n"); + test_control_return(147); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(148); + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 10000, // Directory Entries + 0, // Hidden sectors + 64000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(149); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(150); + } + + /* Create a file name that execises the short name with a 0xE5 and a dot less than the eighth character. */ + status = fx_file_create(&ram_disk, "a.a"); + status += fx_media_flush(&ram_disk); + status += fx_media_read(&ram_disk, ram_disk.fx_media_root_sector_start, buffer); + buffer[0x20] = 0x8f; + buffer[0x21] = 0xe5; + buffer[0x22] = '.'; + buffer[0x23] = 'a'; + buffer[0x24] = '~'; + status += fx_media_write(&ram_disk, ram_disk.fx_media_root_sector_start, buffer); + status += fx_media_flush(&ram_disk); + status += fx_directory_short_name_get(&ram_disk, "a.a", name); + status += fx_file_open(&ram_disk, &file_1, "a.a", FX_OPEN_FOR_WRITE); + status += fx_file_write(&file_1, "a", 1); + file_1.fx_file_dir_entry.fx_dir_entry_short_name[0] = (CHAR)0xE5; + status += fx_media_flush(&ram_disk); + status += fx_file_close(&file_1); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(151); + } + + /* Create some files to test the logic in fx_directory_entry_read */ + status = fx_file_create(&ram_disk, "a"); + status += fx_directory_short_name_get(&ram_disk, "a", name); + status += fx_file_create(&ram_disk, "aa"); + status += fx_directory_short_name_get(&ram_disk, "aa", name); + status += fx_file_create(&ram_disk, "aaa"); + status += fx_directory_short_name_get(&ram_disk, "aaa", name); + status += fx_file_create(&ram_disk, "aaaa"); + status += fx_directory_short_name_get(&ram_disk, "aaaa", name); + status += fx_file_create(&ram_disk, "aaaaa"); + status += fx_directory_short_name_get(&ram_disk, "aaaaa", name); + status += fx_file_create(&ram_disk, "aaaaaa"); + status += fx_directory_short_name_get(&ram_disk, "aaaaaa", name); + status += fx_file_create(&ram_disk, "aaaaaaa"); + status += fx_directory_short_name_get(&ram_disk, "aaaaaaa", name); + status += fx_file_create(&ram_disk, "aaaaaaaa"); + status += fx_directory_short_name_get(&ram_disk, "aaaaaaaa", name); + status += fx_file_create(&ram_disk, "aaaaaaaa.a"); + status += fx_directory_short_name_get(&ram_disk, "aaaaaaaa.a", name); + status += fx_file_create(&ram_disk, "aaaaaaaa.aa"); + status += fx_directory_short_name_get(&ram_disk, "aaaaaaaa.aa", name); + status += fx_file_create(&ram_disk, "aaaaaaaa.aaa"); + status += fx_directory_short_name_get(&ram_disk, "aaaaaaaa.aaa", name); + status += fx_file_create(&ram_disk, "a."); + status += fx_directory_short_name_get(&ram_disk, "a.", name); + status += fx_file_create(&ram_disk, "aa."); + status += fx_directory_short_name_get(&ram_disk, "aa.", name); + status += fx_file_create(&ram_disk, "aaa."); + status += fx_directory_short_name_get(&ram_disk, "aaa.", name); + status += fx_file_create(&ram_disk, "aaaa."); + status += fx_directory_short_name_get(&ram_disk, "aaaa.", name); + status += fx_file_create(&ram_disk, "aaaaa."); + status += fx_directory_short_name_get(&ram_disk, "aaaaa.", name); + status += fx_file_create(&ram_disk, "aaaaaa."); + status += fx_directory_short_name_get(&ram_disk, "aaaaaa.", name); + status += fx_file_create(&ram_disk, "aaaaaaa."); + status += fx_directory_short_name_get(&ram_disk, "aaaaaaa.", name); + status += fx_file_create(&ram_disk, "aaaaaaaa."); + status += fx_directory_short_name_get(&ram_disk, "aaaaaaaa.", name); + status += fx_file_delete(&ram_disk, "aaaaaa."); + status += fx_file_delete(&ram_disk, "aaaaaaa."); + status += fx_file_delete(&ram_disk, "aaaaaaaa."); + + /* Create a name with an 0xe5 in the front. */ + name[0] = (CHAR)0xE5; + name[1] = 'a'; + name[2] = 'b'; + name[3] = '.'; + name[4] = 't'; + name[5] = 'x'; + name[6] = 't'; + name[7] = 0; + status += fx_file_create(&ram_disk, name); + status += fx_directory_short_name_get(&ram_disk, name, (CHAR *)buffer); + status += fx_file_open(&ram_disk, &file_1, name, FX_OPEN_FOR_WRITE); + status += fx_file_write(&file_1, "1", 1); + status += fx_file_close(&file_1); + + status += fx_file_create(&ram_disk, "abcdefgh.txt"); + status += fx_directory_short_name_get(&ram_disk, "abcdefgh.txt", (CHAR *)buffer); + status += fx_file_open(&ram_disk, &file_1, "abcdefgh.txt", FX_OPEN_FOR_WRITE); + status += fx_file_write(&file_1, "1", 1); + status += fx_file_close(&file_1); + +#if EXTENDED_NAME_MANGLE_TEST + name[0] = 'a'; + name[1] = 'B'; + name[2] = 'C'; + name[3] = 'D'; + name[4] = 'E'; + name[5] = 'F'; + name[6] = 'G'; + name[7] = '0'; + name[8] = '0'; + name[9] = '0'; + name[10] = '0'; + name[11] = 0; + for (i = 0; i < 3; i++) + { + + name[7] = '0' + i; + + for (j = 0; j < 10; j++) + { + + name[8] = '0' + j; + + for (k = 0; k < 10; k++) + { + + name[9] = '0' + k; + + for (l = 0; l < 10; l++) + { + if (status) + break; + name[10] = '0' + l; + status += fx_file_create(&ram_disk, name); + if (status) + break; + status += fx_directory_short_name_get(&ram_disk, name, buffer); + if (status) + break; + } + } + } + } +#endif + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(152); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(153); + } + + + /* Test corner cases on fx_directory_search. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 256, // Directory Entries + 0, // Hidden sectors + 14000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(154); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(155); + } + + /* Build a long directory structure. */ + status = fx_directory_create(&ram_disk, "/sub-directory_depth_00001"); + status += fx_directory_create(&ram_disk, "sub-directory_depth_00001/sub-directory_depth_00002"); + status += fx_directory_create(&ram_disk, "sub-directory_depth_00001/sub-directory_depth_00002/sub-directory_depth_00003"); + status += fx_directory_create(&ram_disk, "sub-directory_depth_00001/sub-directory_depth_00002/sub-directory_depth_00003/sub-directory_depth_00004"); + status += fx_directory_create(&ram_disk, "sub-directory_depth_00001/sub-directory_depth_00002/sub-directory_depth_00003/sub-directory_depth_00004/sub-directory_depth_00005"); + status += fx_directory_create(&ram_disk, "sub-directory_depth_00001/sub-directory_depth_00002/sub-directory_depth_00003/sub-directory_depth_00004/sub-directory_depth_00005/sub-directory_depth_00006"); + status += fx_directory_create(&ram_disk, "sub-directory_depth_00001/sub-directory_depth_00002/sub-directory_depth_00003/sub-directory_depth_00004/sub-directory_depth_00005/sub-directory_depth_00006/sub-directory_depth_00007"); + status += fx_directory_create(&ram_disk, "sub-directory_depth_00001/sub-directory_depth_00002/sub-directory_depth_00003/sub-directory_depth_00004/sub-directory_depth_00005/sub-directory_depth_00006/sub-directory_depth_00007/sub-directory_depth_00008"); + status += fx_directory_create(&ram_disk, "sub-directory_depth_00001/sub-directory_depth_00002/sub-directory_depth_00003/sub-directory_depth_00004/sub-directory_depth_00005/sub-directory_depth_00006/sub-directory_depth_00007/sub-directory_depth_00008/sub-directory_depth_00009"); + status += fx_directory_create(&ram_disk, "sub-directory_depth_00001/sub-directory_depth_00002/sub-directory_depth_00003/sub-directory_depth_00004/sub-directory_depth_00005/sub-directory_depth_00006/sub-directory_depth_00007/sub-directory_depth_00008/sub-directory_depth_00009/sub-directory_depth_00010"); + status += fx_directory_create(&ram_disk, "sub-directory_depth_00001/sub-directory_depth_00002/sub-directory_depth_00003/sub-directory_depth_00004/sub-directory_depth_00005/sub-directory_depth_00006/sub-directory_depth_00007/sub-directory_depth_00008/sub-directory_depth_00009/sub-directory_depth_00010/sub-directory_depth_00011"); + status += fx_file_create(&ram_disk, "sub-directory_depth_00001/sub-directory_depth_00002/sub-directory_depth_00003/sub-directory_depth_00004/sub-directory_depth_00005/sub-directory_depth_00006/sub-directory_depth_00007/sub-directory_depth_00008/sub-directory_depth_00009/sub-directory_depth_00010/sub-directory_depth_00011/file1.txt"); + status += fx_file_open(&ram_disk, &file_1, "sub-directory_depth_00001/sub-directory_depth_00002/sub-directory_depth_00003/sub-directory_depth_00004/sub-directory_depth_00005/sub-directory_depth_00006/sub-directory_depth_00007/sub-directory_depth_00008/sub-directory_depth_00009/sub-directory_depth_00010/sub-directory_depth_00011/file1.txt", FX_OPEN_FOR_WRITE); + + /* Now create an invalid path by using a file as a directory entry. */ + status += fx_file_create(&ram_disk, "sub-directory_depth_00001/sub-directory_depth_00002/sub-directory_depth_00003/sub-directory_depth_00004/sub-directory_depth_00005/sub-directory_depth_00006/sub-directory_depth_00007/sub-directory_depth_00008/sub-directory_depth_00009/sub-directory_depth_00010/sub-directory_depth_00011/file1.txt/baddir"); + + /* Determine if the test was successful. */ + if (status != FX_INVALID_PATH) + { + + printf("ERROR!\n"); + test_control_return(156); + } + + status = fx_file_create(&ram_disk, "/sub-directory_depth_00001/sub-directory_depth_00002/sub-directory_depth_00003/sub-directory_depth_00004/sub-directory_depth_00005/sub-directory_depth_00006/sub-directory_depth_00007/sub-directory_depth_00008/sub-directory_depth_00009/sub-directory_depth_00010/sub-directory_depth_00011/file2.txt"); + status += fx_file_open(&ram_disk, &file_2, "/sub-directory_depth_00001/sub-directory_depth_00002/sub-directory_depth_00003/sub-directory_depth_00004/sub-directory_depth_00005/sub-directory_depth_00006/sub-directory_depth_00007/sub-directory_depth_00008/sub-directory_depth_00009/sub-directory_depth_00010/sub-directory_depth_00011/file2.txt", FX_OPEN_FOR_WRITE); + status += fx_directory_default_set(&ram_disk, "sub-directory_depth_00001/sub-directory_depth_00002/sub-directory_depth_00003/sub-directory_depth_00004/sub-directory_depth_00005/sub-directory_depth_00006/sub-directory_depth_00007/sub-directory_depth_00008/sub-directory_depth_00009/sub-directory_depth_00010/sub-directory_depth_00011"); + status += fx_directory_first_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + status += fx_directory_next_entry_find(&ram_disk, name); + + /* Close the media. */ + status += fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(157); + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 256, // Directory Entries + 0, // Hidden sectors + 14000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(158); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(159); + } + + /* Build a long sub-directory name... This name will be too long to cache in fx_directory_search. */ + for (i = 0; i < FX_MAX_LONG_NAME_LEN; i++) + { + name[i] = 'a'; + } + name[0] = '/'; + name[256] = 0; + + status = fx_directory_create(&ram_disk, name); + + name[256] = '/'; + name[257] = 'f'; + name[258] = 'i'; + name[259] = 'l'; + name[260] = 'e'; + name[261] = 0; + + status += fx_file_create(&ram_disk, name); + status += fx_file_open(&ram_disk, &file_1, name, FX_OPEN_FOR_WRITE); + status += fx_directory_short_name_get(&ram_disk, name, (CHAR *)buffer); + status += fx_directory_short_name_get(&ram_disk, name, (CHAR *)buffer); + status += fx_directory_short_name_get(&ram_disk, name, (CHAR *)buffer); + status += fx_directory_short_name_get(&ram_disk, name, (CHAR *)buffer); + status += fx_directory_short_name_get(&ram_disk, name, (CHAR *)buffer); + status += fx_file_close(&file_1); + status += fx_file_open(&ram_disk, &file_1, &name[1], FX_OPEN_FOR_WRITE); + status += fx_file_close(&file_1); + status += fx_file_open(&ram_disk, &file_1, name, FX_OPEN_FOR_WRITE); + status += fx_file_close(&file_1); + status += fx_file_open(&ram_disk, &file_1, &name[1], FX_OPEN_FOR_WRITE); + status += fx_file_close(&file_1); + status += fx_file_delete(&ram_disk, name); + + /* Build a perfect fit long sub-directory name and file name... This name will fit exactly into the fx_directory_search cache. */ + for (i = 0; i < FX_MAX_LONG_NAME_LEN; i++) + { + name[i] = 'a'; + } + name[0] = '/'; + name[250] = 0; + + status += fx_directory_create(&ram_disk, name); + + name[250] = '/'; + name[251] = 'f'; + name[252] = 'i'; + name[253] = 'l'; + name[254] = 'e'; + name[255] = 0; + + status = fx_file_create(&ram_disk, name); + status += fx_file_open(&ram_disk, &file_1, name, FX_OPEN_FOR_WRITE); + status += fx_directory_short_name_get(&ram_disk, name, (CHAR *)buffer); + status += fx_directory_short_name_get(&ram_disk, name, (CHAR *)buffer); + status += fx_directory_short_name_get(&ram_disk, name, (CHAR *)buffer); + status += fx_directory_short_name_get(&ram_disk, name, (CHAR *)buffer); + status += fx_directory_short_name_get(&ram_disk, name, (CHAR *)buffer); + status += fx_file_close(&file_1); + status += fx_file_open(&ram_disk, &file_1, &name[1], FX_OPEN_FOR_WRITE); + status += fx_file_close(&file_1); + status += fx_file_open(&ram_disk, &file_1, name, FX_OPEN_FOR_WRITE); + status += fx_file_close(&file_1); + status += fx_file_open(&ram_disk, &file_1, &name[1], FX_OPEN_FOR_WRITE); + status += fx_file_close(&file_1); + status = fx_file_delete(&ram_disk, name); + + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(160); + } + + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 256, // Directory Entries + 0, // Hidden sectors + 14000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(161); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(162); + } + + + /* Build a perfect fit long sub-directory name and file name... This name will fit exactly into the fx_directory_search cache. */ + for (i = 0; i < FX_MAX_LONG_NAME_LEN; i++) + { + name[i] = 'a'; + } + name[0] = '/'; + name[255] = 0; + status += fx_directory_create(&ram_disk, name); + + + name[253] = 0; + + status += fx_directory_create(&ram_disk, name); + + name[253] = '/'; + name[254] = 'a'; + name[255] = 0; + + status = fx_file_create(&ram_disk, name); + status += fx_file_open(&ram_disk, &file_1, name, FX_OPEN_FOR_WRITE); + status += fx_directory_short_name_get(&ram_disk, name, (CHAR *)buffer); + status += fx_file_close(&file_1); + + /* Setup info for direct call to directory search. */ + dir_entry.fx_dir_entry_name = ram_disk.fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + search_directory.fx_dir_entry_name = ram_disk.fx_media_name_buffer + FX_MAX_LONG_NAME_LEN * 2; + dir_entry.fx_dir_entry_short_name[0] = 0; + search_directory.fx_dir_entry_short_name[0] = 0; + + /* Lets make the name bigger than the maximun name. */ + name[255] = 'a'; + name[256] = 'a'; + name[257] = 'a'; + name[258] = 'a'; + name[259] = 0; + _fx_directory_search(&ram_disk, name, &dir_entry, &search_directory, &name_ptr); + + + /* Set the name back to the original size. */ + name[255] = 0; + status += fx_file_open(&ram_disk, &file_1, name, FX_OPEN_FOR_WRITE); + status += fx_directory_short_name_get(&ram_disk, name, (CHAR *)buffer); + status += fx_file_close(&file_1); + + /* Now make the name long again to test relative, no path. */ + name[255] = 'a'; + + /* Now let's perform a maximum relative search. */ + _fx_directory_search(&ram_disk, &name[1], &dir_entry, &search_directory, &name_ptr); + + + /* Now lets set a path and test that out. */ + name[253] = 0; + status += fx_directory_default_set(&ram_disk, name); + + /* Now set the name back to the original size. */ + name[253] = '/'; + name[255] = 0; + + status += fx_file_open(&ram_disk, &file_1, name, FX_OPEN_FOR_WRITE); + status += fx_directory_short_name_get(&ram_disk, name, (CHAR *)buffer); + status += fx_file_close(&file_1); + + /* Now make the name long again to test relative, with path. */ + name[255] = 'a'; + + /* Now let's perform a maximum relative search. */ + _fx_directory_search(&ram_disk, &name[1], &dir_entry, &search_directory, &name_ptr); + + /* Now lets set a path, modify the found path and test that out. */ + name[253] = 'a'; + name[254] = 'a'; + name[255] = 0; + status += fx_directory_default_set(&ram_disk, name); + + /* Now set the name back to the original size. */ + name[253] = '/'; + name[255] = 0; + + status += fx_file_open(&ram_disk, &file_1, name, FX_OPEN_FOR_WRITE); + status += fx_directory_short_name_get(&ram_disk, name, (CHAR *)buffer); + status += fx_file_close(&file_1); + + /* Test the maximum size of the default path lookup. */ + for (i = 0; i < 256; i++) + { + ram_disk.fx_media_last_found_name[i] = ram_disk.fx_media_default_path.fx_path_name_buffer[i]; + } + ram_disk.fx_media_last_found_name[0] = '/'; + name[255] = 'a'; + name[256] = 0; + + /* Now let's perform a maximum relative search. */ + _fx_directory_search(&ram_disk, &name[1], &dir_entry, &search_directory, &name_ptr); + + /* Now let's setup a directory search with a NULL path, a maximum found file name, and alternate directory separators. */ + ram_disk.fx_media_default_path.fx_path_name_buffer[0] = 0; + strcpy(&ram_disk.fx_media_last_found_name[0], "\\abc\\def\\ghi\\abc"); + for (i = 0; i < 256; i++) + { + ram_disk.fx_media_last_found_file_name[i] = 'a'; + ram_disk.fx_media_last_found_directory.fx_dir_entry_name[i] = 'a'; + } + ram_disk.fx_media_last_found_file_name[255] = 0; + ram_disk.fx_media_last_found_directory.fx_dir_entry_name[255] = 0; + ram_disk.fx_media_last_found_directory_valid = FX_TRUE; + _fx_directory_search(&ram_disk, "abc\\def\\ghi\\abc", &dir_entry, &search_directory, &name_ptr); + + /* Now let's setup a directory search with a NULL path, a medium sized found file name, and alternate directory separators. */ + ram_disk.fx_media_default_path.fx_path_name_buffer[0] = 0; + strcpy(&ram_disk.fx_media_last_found_name[0], "\\abc\\def\\ghi\\abc"); + for (i = 0; i < 256; i++) + { + ram_disk.fx_media_last_found_file_name[i] = 'a'; + ram_disk.fx_media_last_found_directory.fx_dir_entry_name[i] = 'a'; + } + ram_disk.fx_media_last_found_file_name[255] = 0; + ram_disk.fx_media_last_found_directory.fx_dir_entry_name[25] = 0; + ram_disk.fx_media_last_found_directory_valid = FX_TRUE; + _fx_directory_search(&ram_disk, "abc\\def\\ghi\\abc", &dir_entry, &search_directory, &name_ptr); + + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(163); + } + + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 256, // Directory Entries + 0, // Hidden sectors + 14000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(164); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(165); + } + + /* Create a directory path. */ + status = fx_directory_create(&ram_disk, "/abc"); + status += fx_directory_create(&ram_disk, "/abc/def"); + status += fx_directory_create(&ram_disk, "/abc/def/ghi"); + status += fx_directory_create(&ram_disk, "/abc/def/ghi/jkl"); + status += fx_file_create(&ram_disk, "/abc/def/ghi/jkl/file.txt"); + status += fx_file_create(&ram_disk, "/abc/def/ghi/jkl/file2.txt"); + status += fx_file_create(&ram_disk, "/abc/def/ghi/jkl/file3.txt"); + status += fx_file_create(&ram_disk, "/abc/def/ghi/jkl/file4.txt"); + status += fx_file_create(&ram_disk, "/abc/def/ghi/jkl/file5.txt"); + status += fx_file_create(&ram_disk, "/abc/def/ghi/jkl/file6.txt"); + status += fx_file_create(&ram_disk, "/abc/def/ghi/jkl/file7.txt"); + status += fx_file_create(&ram_disk, "/abc/def/ghi/jkl/file8.txt"); + status += fx_file_create(&ram_disk, "/abc/def/ghi/jkl/file9.txt"); + status += fx_file_create(&ram_disk, "/abc/def/ghi/jkl/file10.txt"); + status += fx_file_create(&ram_disk, "/abc/def/ghi/jkl/file11.txt"); + status += fx_directory_default_set(&ram_disk, "/abc"); + status += fx_directory_first_entry_find(&ram_disk, name); + + /* Now open the file to get it into the search cache. */ + status += fx_file_open(&ram_disk, &file_1, "/abc/def/ghi/jkl/file.txt", FX_OPEN_FOR_WRITE); + status += fx_file_close(&file_1); + status += fx_file_open(&ram_disk, &file_1, "/abc/def/ghi/jkl/file.txt", FX_OPEN_FOR_WRITE); + status += fx_file_close(&file_1); + status += fx_file_open(&ram_disk, &file_1, "def/ghi/jkl/file.txt", FX_OPEN_FOR_WRITE); + status += fx_file_close(&file_1); + status += fx_directory_default_set(&ram_disk, "/"); + status += fx_file_open(&ram_disk, &file_1, "abc/def/ghi/jkl/file.txt", FX_OPEN_FOR_WRITE); + status += fx_file_close(&file_1); + status += fx_directory_next_entry_find(&ram_disk, name); + + /* Now test for FAT read errors. */ + _fx_utility_fat_entry_read_error_request = 1; + status += fx_file_open(&ram_disk, &file_2, "abc/def/ghi/jkl/file2.txt", FX_OPEN_FOR_WRITE); + _fx_utility_fat_entry_read_error_request = 0; + + /* Check the status... should be an I/O error at this point. */ + if (status != FX_IO_ERROR) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(166); + } + + /* Now test a FAT entry value that is too small. */ + _fx_utility_fat_entry_read_error_request = 10001; + status = fx_file_open(&ram_disk, &file_2, "abc/def/ghi/jkl/file2.txt", FX_OPEN_FOR_WRITE); + _fx_utility_fat_entry_read_error_request = 0; + + /* Check the status... should be a FAT read error. */ + if (status != FX_FAT_READ_ERROR) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(167); + } + + /* Now test a FAT entry value that is too large. */ + temp = ram_disk.fx_media_total_clusters; + _fx_utility_fat_entry_read_error_request = 40001; + status = fx_file_open(&ram_disk, &file_2, "abc/def/ghi/jkl/file2.txt", FX_OPEN_FOR_WRITE); + _fx_utility_fat_entry_read_error_request = 0; + ram_disk.fx_media_total_clusters = temp; + + /* Check the status... should be a FAT read error. */ + if (status != FX_FAT_READ_ERROR) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(168); + } + + /* Now test a FAT entry value that is the same as the cluster itself. */ + _fx_utility_fat_entry_read_error_request = 30001; + status = fx_file_open(&ram_disk, &file_2, "abc/def/ghi/jkl/file2.txt", FX_OPEN_FOR_WRITE); + _fx_utility_fat_entry_read_error_request = 0; + + /* Check the status... should be a FAT read error. */ + if (status != FX_FAT_READ_ERROR) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(169); + } + + /* Now perform a good open. */ + status = fx_file_open(&ram_disk, &file_2, "abc/def/ghi/jkl/file2.txt", FX_OPEN_FOR_WRITE); + status += fx_file_close(&file_2); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(166); + } + + /* Setup info for direct call to directory search. */ + dir_entry.fx_dir_entry_name = ram_disk.fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + search_directory.fx_dir_entry_name = ram_disk.fx_media_name_buffer + FX_MAX_LONG_NAME_LEN * 2; + dir_entry.fx_dir_entry_short_name[0] = 0; + search_directory.fx_dir_entry_short_name[0] = 0; + + /* Lets perform a search that from the root directory. */ + _fx_directory_search(&ram_disk, "/abc/..", &dir_entry, &search_directory, &name_ptr); + + /* Now setup a maximum default path. */ + for (i = 0; i < FX_MAX_LAST_NAME_LEN; i++) + { + name[i] = 'a'; + } + name[255] = 0; + + /* Create a sub-directory of maximum lenght. */ + status = fx_directory_create(&ram_disk, name); + + /* Set the default path to this name. */ + status += fx_directory_default_set(&ram_disk, name); + + /* Create a file in the sub-directory path. */ + status += fx_file_create(&ram_disk, "file1.txt"); + + /* Open the file to test the directory search cache name build. */ + status += fx_file_open(&ram_disk, &file_1, "file1.txt", FX_OPEN_FOR_WRITE); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(167); + } + + + /* Close the media. */ + status = fx_media_close(&ram_disk); + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(168); + } + + /* Test for https://github.com/azure-rtos/filex/issues/26 */ + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 4, // Directory Entries + 0, // Hidden sectors + 128, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + return_if_fail( status == FX_SUCCESS); + + FX_FILE my_file; + ULONG available_clusters; + uint8_t buff[128]; + int num_of_files = 0; + char dummy_file_path[DIR_PATH_SIZE + 3]; + memcpy(dummy_file_path, DIR_PATH, DIR_PATH_SIZE); + /* Create a file called TEST.TXT.*/ + + status += fx_directory_create(&ram_disk, "/MYDIR"); + status += fx_directory_create(&ram_disk, "MYDIR/MYDIR1"); + status += fx_directory_create(&ram_disk, "MYDIR/MYDIR1/MYDIR2"); + status += fx_directory_create(&ram_disk, "MYDIR/MYDIR1/MYDIR2/MYDIR3"); + return_if_fail( status == FX_SUCCESS); + + status = fx_file_create(&ram_disk, "MYDIR/MYDIR1/MYDIR2/MYDIR3/TEST.TXT"); + return_if_fail( status == FX_SUCCESS); + + available_clusters = ram_disk.fx_media_available_clusters; + /* + * Ensure there is no spare space in the cluster used for the directory + */ + dummy_file_path[sizeof(DIR_PATH) - 1] = 'A'; + dummy_file_path[sizeof(DIR_PATH)] = '\0'; + for (num_of_files = 0; available_clusters == ram_disk.fx_media_available_clusters; num_of_files ++) + { + dummy_file_path[sizeof(DIR_PATH) - 1] ++; + status = fx_file_create(&ram_disk, dummy_file_path); + return_if_fail( status == FX_SUCCESS); + } + // remove the file that allocated new cluster + status = fx_file_delete(&ram_disk, dummy_file_path); + return_if_fail( status == FX_SUCCESS); + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "MYDIR/MYDIR1/MYDIR2/MYDIR3/TEST.TXT", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + available_clusters = ram_disk.fx_media_available_clusters; + for (int i = 0; i < available_clusters - 1; i++) + { + /* Write a one cluster size to the test file. */ + status = fx_file_write(&my_file, buff, sizeof(buff)); + return_if_fail( status == FX_SUCCESS); + } + + /* The dir entry should be created on new cluster */ + status =fx_file_rename(&ram_disk, "MYDIR/MYDIR1/MYDIR2/MYDIR3/TEST.TXT", "MYDIR/MYDIR1/MYDIR2/MYDIR3/TEST_RENAME.TXT"); + return_if_fail (status == FX_SUCCESS); + + return_if_fail(ram_disk.fx_media_available_clusters == 0); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(169); + } + else + { + + printf("SUCCESS!\n"); + test_control_return(0); + } +} + diff --git a/test/regression_test/filex_directory_default_path_get_set_test.c b/test/regression_test/filex_directory_default_path_get_set_test.c new file mode 100644 index 0000000..485e8c7 --- /dev/null +++ b/test/regression_test/filex_directory_default_path_get_set_test.c @@ -0,0 +1,1273 @@ +/* This FileX test concentrates on the basic directory default path get/set operations. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_directory.h" +#include "fx_utility.h" +#include +#include "fx_ram_driver_test.h" + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 16*128 + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static CHAR name[256]; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + + +/* Define thread prototypes. */ + +void filex_directory_default_get_set_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_directory_default_get_set_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +CHAR *path_ptr; +CHAR path_name_buffer[32]; +CHAR test_path_name_buffer[1]; +ULONG temp; +FX_LOCAL_PATH local_path; +FX_LOCAL_PATH local_path1; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Directory default get/set test........................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Attempt to set the default directory before the media has been opened to generate an error */ + status = fx_directory_default_set(&ram_disk, "/A0"); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(2); + } + + /* Attempt to get the default directory before the media has been opened to generate an error */ + status = fx_directory_default_get(&ram_disk, &path_ptr); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(3); + } + + /* Attempt to get copy of the default directory before the media has been opened to generate an error */ + status = fx_directory_default_get_copy(&ram_disk, test_path_name_buffer, sizeof(test_path_name_buffer)); + + /* Determine if the test was successful. */ + if (status != FX_MEDIA_NOT_OPEN) + { + + printf("ERROR!\n"); + test_control_return(52); + } + +#ifndef FX_NO_LOCAL_PATH + /* Attempt to get copy of the directory local path before the media has been opened to generate an error */ + status = _fx_directory_local_path_get_copy(&ram_disk, test_path_name_buffer, sizeof(test_path_name_buffer)); + + /* Determine if the test was successful. */ + if (status != FX_MEDIA_NOT_OPEN) + { + + printf("ERROR!\n"); + test_control_return(53); + } +#endif /* FX_NO_LOCAL_PATH */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(4); + } + + /* Get the current path. This should be / or NULL. */ + status = fx_directory_default_get(&ram_disk, &path_ptr); + + /* Check the status. */ + if ((status != FX_SUCCESS) || (path_ptr[0])) + { + + /* Error getting the path. Return to caller. */ + printf("ERROR!\n"); + test_control_return(5); + } + + temp = ram_disk.fx_media_id; + ram_disk.fx_media_id = 0; + + /* Give a media pointer with bad data to cause an error */ + status = fx_directory_default_get(&ram_disk, &path_ptr); + + /* Check the status */ + if (status != FX_MEDIA_NOT_OPEN) + { + + /* Error opening the media */ + printf("ERROR!\n"); + test_control_return(6); + } + + ram_disk.fx_media_id = temp; + +#ifndef FX_DISABLE_ERROR_CHECKING + /* Give a bad ram pointer to cause an error */ + status = fx_directory_default_get(NULL, &path_ptr); + + /* Check the status. */ + if (status != FX_PTR_ERROR) + { + + /* Error getting the path. Return to caller. */ + printf("ERROR!\n"); + test_control_return(7); + } + + /* Give a bad path pointer to cause an error */ + status = fx_directory_default_get(&ram_disk, NULL); + + /* Check the status. */ + if (status != FX_PTR_ERROR) + { + + /* Error getting the path. Return to caller. */ + printf("ERROR!\n"); + test_control_return(8); + } +#endif + + /* Create a series of directories... */ + status = fx_directory_create(&ram_disk, "/A0"); + status += fx_directory_create(&ram_disk, "/B0"); + status += fx_directory_create(&ram_disk, "/C0"); + status += fx_directory_create(&ram_disk, "/D0"); + status += fx_directory_create(&ram_disk, "/E0"); + status += fx_directory_create(&ram_disk, "/F0"); + status += fx_directory_create(&ram_disk, "/G0"); + status += fx_directory_create(&ram_disk, "/H0"); + status += fx_directory_create(&ram_disk, "/I0"); + status += fx_file_create(&ram_disk, "NOT_A_DIR"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(9); + } + + /* Attempt to set the default directory to something that is not a directory to generate an error */ + status = fx_directory_default_set(&ram_disk, "NOT_A_DIR"); + if (status != FX_INVALID_PATH) + { + printf("ERROR!\n"); + test_control_return(10); + } + status = fx_file_delete(&ram_disk, "NOT_A_DIR"); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(11); + } + + /* Attempt to create the same directory again. */ + status = fx_directory_create(&ram_disk, "/A0"); + + /* Check for errors... */ + if (status != FX_ALREADY_CREATED) + { + + /* Error creating same directory twice. Return to caller. */ + printf("ERROR!\n"); + test_control_return(12); + } + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + /* send null pointer to default directory set to generate an error */ + status = fx_directory_default_set(FX_NULL, "/A0"); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(13); + } +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Create the next level of sub-directories. */ + status = fx_directory_default_set(&ram_disk, "/A0"); + status += fx_directory_create(&ram_disk, "A1"); + status += fx_directory_create(&ram_disk, "A2"); + status += fx_directory_create(&ram_disk, "A3"); + status += fx_directory_create(&ram_disk, "A4"); + status += fx_directory_create(&ram_disk, "A5"); + status += fx_directory_default_get(&ram_disk, &path_ptr); + + + status += fx_directory_default_set(&ram_disk, "/B0"); + status += fx_directory_create(&ram_disk, "B1"); + status += fx_directory_create(&ram_disk, "B2"); + status += fx_directory_create(&ram_disk, "B3"); + status += fx_directory_create(&ram_disk, "B4"); + status += fx_directory_create(&ram_disk, "B5"); + status += fx_directory_default_get(&ram_disk, &path_ptr); + + status += fx_directory_default_set(&ram_disk, "/C0"); + status += fx_directory_create(&ram_disk, "C1"); + status += fx_directory_create(&ram_disk, "C2"); + status += fx_directory_create(&ram_disk, "C3"); + status += fx_directory_create(&ram_disk, "C4"); + status += fx_directory_create(&ram_disk, "C5"); + status += fx_directory_default_get(&ram_disk, &path_ptr); + + status += fx_directory_default_set(&ram_disk, "/D0"); + status += fx_directory_create(&ram_disk, "D1"); + status += fx_directory_create(&ram_disk, "D2"); + status += fx_directory_create(&ram_disk, "D3"); + status += fx_directory_create(&ram_disk, "D4"); + status += fx_directory_create(&ram_disk, "D5"); + status += fx_directory_default_get(&ram_disk, &path_ptr); + + status += fx_directory_default_set(&ram_disk, "/E0"); + status += fx_directory_create(&ram_disk, "E1"); + status += fx_directory_create(&ram_disk, "E2"); + status += fx_directory_create(&ram_disk, "E3"); + status += fx_directory_create(&ram_disk, "E4"); + status += fx_directory_create(&ram_disk, "E5"); + status += fx_directory_default_get(&ram_disk, &path_ptr); + + status += fx_directory_default_set(&ram_disk, "/F0"); + status += fx_directory_create(&ram_disk, "F1"); + status += fx_directory_create(&ram_disk, "F2"); + status += fx_directory_create(&ram_disk, "F3"); + status += fx_directory_create(&ram_disk, "F4"); + status += fx_directory_create(&ram_disk, "F5"); + status += fx_directory_default_get(&ram_disk, &path_ptr); + + status += fx_directory_default_set(&ram_disk, "/G0"); + status += fx_directory_create(&ram_disk, "G1"); + status += fx_directory_create(&ram_disk, "G2"); + status += fx_directory_create(&ram_disk, "G3"); + status += fx_directory_create(&ram_disk, "G4"); + status += fx_directory_create(&ram_disk, "G5"); + status += fx_directory_default_get(&ram_disk, &path_ptr); + + status += fx_directory_default_set(&ram_disk, "/H0"); + status += fx_directory_create(&ram_disk, "H1"); + status += fx_directory_create(&ram_disk, "H2"); + status += fx_directory_create(&ram_disk, "H3"); + status += fx_directory_create(&ram_disk, "H4"); + status += fx_directory_create(&ram_disk, "H5"); + status += fx_directory_default_get(&ram_disk, &path_ptr); + + status += fx_directory_default_set(&ram_disk, "/I0"); + status += fx_directory_create(&ram_disk, "I1"); + status += fx_directory_create(&ram_disk, "I2"); + status += fx_directory_create(&ram_disk, "I3"); + status += fx_directory_create(&ram_disk, "I4"); + status += fx_directory_create(&ram_disk, "I5"); + status += fx_directory_default_get(&ram_disk, &path_ptr); + status += fx_directory_default_get_copy(&ram_disk, path_name_buffer, sizeof(path_name_buffer)); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(14); + } + + if (strcmp(path_name_buffer, "/I0")) + { + printf("ERROR!\n"); + test_control_return(233); + } + + /* Now create the third level of sub-directories... */ + +#ifndef FX_NO_LOCAL_PATH + /* create these with a local path defined in threadx */ + status = fx_directory_local_path_set(&ram_disk, &local_path, "/A0"); + status += fx_directory_default_set(&ram_disk, "/A0"); + status += fx_directory_create(&ram_disk, "test"); + status += fx_directory_delete(&ram_disk, "test"); + status += fx_directory_default_set(&ram_disk, "A1"); + status += fx_directory_create(&ram_disk, "test"); + status += fx_directory_delete(&ram_disk, "test"); + status += fx_directory_local_path_clear(&ram_disk); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(15); + } +#endif + + /* create the rest as normal */ + status = fx_directory_default_set(&ram_disk, "/A0/A1"); + status += fx_directory_create(&ram_disk, "A00"); + status += fx_directory_default_set(&ram_disk, "/B0"); + status += fx_directory_create(&ram_disk, "B2/B00"); + status += fx_directory_create(&ram_disk, "/C0/C3/C00"); + status += fx_directory_default_set(&ram_disk, "/D0/D4"); + status += fx_directory_create(&ram_disk, "D00"); + status += fx_directory_default_set(&ram_disk, "/E0"); + status += fx_directory_create(&ram_disk, "E5/E00"); + status += fx_directory_create(&ram_disk, "/F0/F1/F00"); + status += fx_directory_default_set(&ram_disk, "/G0/G1"); + status += fx_directory_create(&ram_disk, "G00"); + status += fx_directory_default_set(&ram_disk, "/H0"); + status += fx_directory_create(&ram_disk, "H2/H00"); + status += fx_directory_create(&ram_disk, "/I0/I3/I00"); + status += fx_directory_default_get(&ram_disk, &path_ptr); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(16); + } + + /* Now delete all the directories. */ + status = fx_directory_delete(&ram_disk, "/A0/A1/A00"); + status += fx_directory_delete(&ram_disk, "/B0/B2/B00"); + status += fx_directory_delete(&ram_disk, "/C0/C3/C00"); + status += fx_directory_delete(&ram_disk, "/D0/D4/D00"); + status += fx_directory_delete(&ram_disk, "/E0/E5/E00"); + status += fx_directory_delete(&ram_disk, "/F0/F1/F00"); + status += fx_directory_delete(&ram_disk, "/G0/G1/G00"); + status += fx_directory_delete(&ram_disk, "/H0/H2/H00"); + status += fx_directory_delete(&ram_disk, "/I0/I3/I00"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error deleting sub-sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(17); + } + + /* Delete the next level of sub-directories. */ + status += fx_directory_delete(&ram_disk, "/A0/A1"); + status += fx_directory_delete(&ram_disk, "/A0/A2"); + status += fx_directory_delete(&ram_disk, "/A0/A3"); + status += fx_directory_delete(&ram_disk, "/A0/A4"); + status += fx_directory_delete(&ram_disk, "/A0/A5"); + + status += fx_directory_delete(&ram_disk, "/B0/B1"); + status += fx_directory_delete(&ram_disk, "/B0/B2"); + status += fx_directory_delete(&ram_disk, "/B0/B3"); + status += fx_directory_delete(&ram_disk, "/B0/B4"); + status += fx_directory_delete(&ram_disk, "/B0/B5"); + + status += fx_directory_delete(&ram_disk, "/C0/C1"); + status += fx_directory_delete(&ram_disk, "/C0/C2"); + status += fx_directory_delete(&ram_disk, "/C0/C3"); + status += fx_directory_delete(&ram_disk, "/C0/C4"); + status += fx_directory_delete(&ram_disk, "/C0/C5"); + + status += fx_directory_delete(&ram_disk, "/D0/D1"); + status += fx_directory_delete(&ram_disk, "/D0/D2"); + status += fx_directory_delete(&ram_disk, "/D0/D3"); + status += fx_directory_delete(&ram_disk, "/D0/D4"); + status += fx_directory_delete(&ram_disk, "/D0/D5"); + + status += fx_directory_delete(&ram_disk, "/E0/E1"); + status += fx_directory_delete(&ram_disk, "/E0/E2"); + status += fx_directory_delete(&ram_disk, "/E0/E3"); + status += fx_directory_delete(&ram_disk, "/E0/E4"); + status += fx_directory_delete(&ram_disk, "/E0/E5"); + + status += fx_directory_delete(&ram_disk, "/F0/F1"); + status += fx_directory_delete(&ram_disk, "/F0/F2"); + status += fx_directory_delete(&ram_disk, "/F0/F3"); + status += fx_directory_delete(&ram_disk, "/F0/F4"); + status += fx_directory_delete(&ram_disk, "/F0/F5"); + + status += fx_directory_delete(&ram_disk, "/G0/G1"); + status += fx_directory_delete(&ram_disk, "/G0/G2"); + status += fx_directory_delete(&ram_disk, "/G0/G3"); + status += fx_directory_delete(&ram_disk, "/G0/G4"); + status += fx_directory_delete(&ram_disk, "/G0/G5"); + + status += fx_directory_delete(&ram_disk, "/H0/H1"); + status += fx_directory_delete(&ram_disk, "/H0/H2"); + status += fx_directory_delete(&ram_disk, "/H0/H3"); + status += fx_directory_delete(&ram_disk, "/H0/H4"); + status += fx_directory_delete(&ram_disk, "/H0/H5"); + + status += fx_directory_delete(&ram_disk, "/I0/I1"); + status += fx_directory_delete(&ram_disk, "/I0/I2"); + status += fx_directory_delete(&ram_disk, "/I0/I3"); + status += fx_directory_delete(&ram_disk, "/I0/I4"); + status += fx_directory_delete(&ram_disk, "/I0/I5"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error deleting sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(18); + } + + /* Delete a series of directories... */ + status = fx_directory_delete(&ram_disk, "/A0"); + status += fx_directory_delete(&ram_disk, "/B0"); + status += fx_directory_delete(&ram_disk, "/C0"); + status += fx_directory_delete(&ram_disk, "/D0"); + status += fx_directory_delete(&ram_disk, "/E0"); + status += fx_directory_delete(&ram_disk, "/F0"); + status += fx_directory_delete(&ram_disk, "/G0"); + status += fx_directory_delete(&ram_disk, "/H0"); + status += fx_directory_delete(&ram_disk, "/I0"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error deleting directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(19); + } + + /* Attempt to delete a directory again. */ + status = fx_directory_delete(&ram_disk, "/A0"); + + /* This should be an error. */ + if (status != FX_NOT_FOUND) + { + + /* Error deleting same directory twice. Return to caller. */ + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4096, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(22); + } + + /* Open the ram_disk - with one logical sector cache! */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(23); + } + + /* Create a sub-directory. */ + status = fx_directory_create(&ram_disk, "sub-directory-level_001"); + + /* Attempt to set a path with a NULL path name. */ + status += _fx_directory_default_set(&ram_disk, FX_NULL); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(24); + } + + /* Flush and invalidate the caches. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Try to set the current path but with an I/O error coming from the directory search. */ + _fx_utility_logical_sector_read_error_request = 1; + status = fx_directory_default_set(&ram_disk, "sub-directory-level_001"); + _fx_utility_logical_sector_read_error_request = 0; + + /* Check the status - should be an I/O error! */ + if (status != FX_INVALID_PATH) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(25); + } + + /* Try to set the current path to the first sub-directory. */ + status = fx_directory_default_set(&ram_disk, "sub-directory-level_001"); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(26); + } + + /* Try to set the current path to the same relative sub-directory. */ + status = fx_directory_default_set(&ram_disk, "."); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(27); + } + + /* Try to set the current path back to root directory relative to the current directory. */ + status = fx_directory_default_set(&ram_disk, ".."); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(28); + } + + /* Create a set of sub-directories that exceeds the maximum path size. */ + status = fx_directory_default_set(&ram_disk, "sub-directory-level_001"); + status += fx_directory_create(&ram_disk, "sub-directory-level_002"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_002"); + status += fx_directory_create(&ram_disk, "sub-directory-level_003"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_003"); + status += fx_directory_create(&ram_disk, "sub-directory-level_004"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_004"); + status += fx_directory_create(&ram_disk, "sub-directory-level_005"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_005"); + status += fx_directory_create(&ram_disk, "sub-directory-level_006"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_006"); + status += fx_directory_create(&ram_disk, "sub-directory-level_007"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_007"); + status += fx_directory_create(&ram_disk, "sub-directory-level_008"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_008"); + status += fx_directory_create(&ram_disk, "sub-directory-level_009"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_009"); + status += fx_directory_create(&ram_disk, "sub-directory-level_010"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_010"); + status += fx_directory_create(&ram_disk, "sub-directory-level_011"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_011"); + status += fx_directory_create(&ram_disk, "sub-directory-level_012"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_012"); + status += fx_directory_create(&ram_disk, "sub-directory-level_013"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_013"); + status += fx_directory_create(&ram_disk, "sub-directory-level_014"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_014"); + status += fx_directory_create(&ram_disk, "sub-directory-level_015"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_015"); + + /* Now backup to the root directory. */ + status += fx_directory_default_set(&ram_disk, "../../../../../../../../../../../../../../.."); + status += fx_directory_first_entry_find(&ram_disk, name); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(29); + } + + /* Move back to a maximum path situation. */ + status = fx_directory_default_set(&ram_disk, "sub-directory-level_001"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_002"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_003"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_004"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_005"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_006"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_007"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_008"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_009"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_010"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_011"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_012"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_013"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_014"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_015"); + + /* Now backup to the root directory - one sub-directory at a time. */ + status += fx_directory_default_set(&ram_disk, ".."); + status += fx_directory_default_set(&ram_disk, ".."); + status += fx_directory_default_set(&ram_disk, ".."); + status += fx_directory_default_set(&ram_disk, ".."); + status += fx_directory_default_set(&ram_disk, ".."); + status += fx_directory_default_set(&ram_disk, ".."); + status += fx_directory_default_set(&ram_disk, ".."); + status += fx_directory_default_set(&ram_disk, ".."); + status += fx_directory_default_set(&ram_disk, ".."); + status += fx_directory_default_set(&ram_disk, ".."); + status += fx_directory_default_set(&ram_disk, ".."); + status += fx_directory_default_set(&ram_disk, ".."); + status += fx_directory_default_set(&ram_disk, ".."); + status += fx_directory_default_set(&ram_disk, ".."); + status += fx_directory_default_set(&ram_disk, ".."); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(30); + } + + /* Move back to a maximum path situation. */ + status = fx_directory_default_set(&ram_disk, "sub-directory-level_001"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_002"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_003"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_004"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_005"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_006"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_007"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_008"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_009"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_010"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_011"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_012"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_013"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_014"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_015"); + + /* Now backup to the root directory - one sub-directory at a time. */ + status += fx_directory_default_set(&ram_disk, "..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\.."); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(31); + } + + /* Move to an exact fit of the 256 path size situation. */ + status = fx_directory_default_set(&ram_disk, "sub-directory-level_001"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_002"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_003"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_004"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_005"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_006"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_007"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_008"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_009"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_010"); + status += fx_directory_create(&ram_disk, "perfect_fit_dr"); + status += fx_directory_default_set(&ram_disk, "perfect_fit_dr"); + status += fx_directory_create(&ram_disk, "perfect_fit_subdir"); + status += fx_directory_default_set(&ram_disk, "perfect_fit_subdir"); + + /* Now backup to the root directory - one sub-directory at a time. */ + status += fx_directory_default_set(&ram_disk, "..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\.."); + status += fx_directory_first_entry_find(&ram_disk, name); + status += fx_directory_first_entry_find(&ram_disk, name); + status += fx_directory_first_entry_find(&ram_disk, name); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(32); + } + + /* Now try to have a new path relative back to the root directory, and then back to itself. */ + status = fx_directory_default_set(&ram_disk, "sub-directory-level_001"); + + /* Update the directory symbol at the root of the path string. */ + ram_disk.fx_media_default_path.fx_path_string[0] = '/'; + status += fx_directory_default_set(&ram_disk, "../sub-directory-level_001"); + + /* Update the directory symbol at the root of the path string to cause a maximum path condition. */ + ram_disk.fx_media_default_path.fx_path_string[0] = ' '; + status += fx_directory_default_set(&ram_disk, "../sub-directory-level_001"); + + status += fx_directory_default_set(&ram_disk, ".."); + status += fx_directory_first_entry_find(&ram_disk, name); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(33); + } + + /* Test to return FX_BUFFER_ERROR from _fx_directory_default_get_copy() */ + status = fx_directory_default_get_copy(&ram_disk, test_path_name_buffer, sizeof(test_path_name_buffer)); + + /* Determine if the test was successful. */ + if (status != FX_BUFFER_ERROR) + { + + printf("ERROR!\n"); + test_control_return(52); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Now perform the same corner case testing with the local path set. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4096, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(22); + } + + /* Open the ram_disk - with one logical sector cache! */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(35); + } + + /* Create a sub-directory. */ + status = fx_directory_create(&ram_disk, "sub-directory-level_001"); + + /* Skip local_path test */ +#ifndef FX_NO_LOCAL_PATH + /* Attempt to set a path with a NULL path name. */ + status += _fx_directory_local_path_set(&ram_disk, &local_path, FX_NULL); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(36); + } + + /* Flush and invalidate the caches. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Try to set the current path but with an I/O error coming from the directory search. */ + _fx_utility_logical_sector_read_error_request = 1; + status = fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_001"); + _fx_utility_logical_sector_read_error_request = 0; + + /* Check the status - should be an I/O error! */ + if (status != FX_INVALID_PATH) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(37); + } + + /* Try to set the current path to the first sub-directory. */ + status = fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_001"); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(38); + } + + /* Try to set the current path to the same relative sub-directory. */ + status = fx_directory_local_path_set(&ram_disk, &local_path, "."); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(39); + } + + /* Try to set the current path back to root directory relative to the current directory. */ + status = fx_directory_local_path_set(&ram_disk, &local_path, ".."); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(40); + } + + /* Create a set of sub-directories that exceeds the maximum path size. */ + status = fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_001"); + status += fx_directory_create(&ram_disk, "sub-directory-level_002"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_002"); + status += fx_directory_create(&ram_disk, "sub-directory-level_003"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_003"); + status += fx_directory_create(&ram_disk, "sub-directory-level_004"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_004"); + status += fx_directory_create(&ram_disk, "sub-directory-level_005"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_005"); + status += fx_directory_create(&ram_disk, "sub-directory-level_006"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_006"); + status += fx_directory_create(&ram_disk, "sub-directory-level_007"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_007"); + status += fx_directory_create(&ram_disk, "sub-directory-level_008"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_008"); + status += fx_directory_create(&ram_disk, "sub-directory-level_009"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_009"); + status += fx_directory_create(&ram_disk, "sub-directory-level_010"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_010"); + status += fx_directory_create(&ram_disk, "sub-directory-level_011"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_011"); + status += fx_directory_create(&ram_disk, "sub-directory-level_012"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_012"); + status += fx_directory_create(&ram_disk, "sub-directory-level_013"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_013"); + status += fx_directory_create(&ram_disk, "sub-directory-level_014"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_014"); + status += fx_directory_create(&ram_disk, "sub-directory-level_015"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_015"); + + /* Now backup to the root directory. */ + status += fx_directory_local_path_set(&ram_disk, &local_path, "../../../../../../../../../../../../../../.."); + status += fx_directory_first_entry_find(&ram_disk, name); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(41); + } + + /* Move back to a maximum path situation. */ + status = fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_001"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_002"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_003"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_004"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_005"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_006"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_007"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_008"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_009"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_010"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_011"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_012"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_013"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_014"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_015"); + + /* Now backup to the root directory - one sub-directory at a time. */ + status += fx_directory_local_path_set(&ram_disk, &local_path, ".."); + status += fx_directory_local_path_set(&ram_disk, &local_path, ".."); + status += fx_directory_local_path_set(&ram_disk, &local_path, ".."); + status += fx_directory_local_path_set(&ram_disk, &local_path, ".."); + status += fx_directory_local_path_set(&ram_disk, &local_path, ".."); + status += fx_directory_local_path_set(&ram_disk, &local_path, ".."); + status += fx_directory_local_path_set(&ram_disk, &local_path, ".."); + status += fx_directory_local_path_set(&ram_disk, &local_path, ".."); + status += fx_directory_local_path_set(&ram_disk, &local_path, ".."); + status += fx_directory_local_path_set(&ram_disk, &local_path, ".."); + status += fx_directory_local_path_set(&ram_disk, &local_path, ".."); + status += fx_directory_local_path_set(&ram_disk, &local_path, ".."); + status += fx_directory_local_path_set(&ram_disk, &local_path, ".."); + status += fx_directory_local_path_set(&ram_disk, &local_path, ".."); + status += fx_directory_local_path_set(&ram_disk, &local_path, ".."); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(42); + } + + /* Move back to a maximum path situation. */ + status = fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_001"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_002"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_003"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_004"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_005"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_006"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_007"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_008"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_009"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_010"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_011"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_012"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_013"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_014"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_015"); + + /* Now backup to the root directory - one sub-directory at a time. */ + status += fx_directory_local_path_set(&ram_disk, &local_path, "..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\.."); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(43); + } + + /* Move to an exact fit of the 256 path size situation. */ + status = fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_001"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_002"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_003"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_004"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_005"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_006"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_007"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_008"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_009"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_010"); + status += fx_directory_create(&ram_disk, "perfect_fit_dr"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "perfect_fit_dr"); + status += fx_directory_create(&ram_disk, "perfect_fit_subdir"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "perfect_fit_subdir"); + + /* Now backup to the root directory - one sub-directory at a time. */ + status += fx_directory_local_path_set(&ram_disk, &local_path, "..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\.."); + status += fx_directory_first_entry_find(&ram_disk, name); + status += fx_directory_first_entry_find(&ram_disk, name); + status += fx_directory_first_entry_find(&ram_disk, name); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(44); + } + + /* Now try to have a new path relative back to the root directory, and then back to itself. */ + status = fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_001"); + + /* Update the directory symbol at the root of the path string. */ + local_path.fx_path_string[0] = '/'; + status += fx_directory_local_path_set(&ram_disk, &local_path, "../sub-directory-level_001"); + + /* Update the directory symbol at the root of the path string to cause a maximum path condition. */ + local_path.fx_path_string[0] = ' '; + status += fx_directory_local_path_set(&ram_disk, &local_path, "../sub-directory-level_001"); + + status += fx_directory_local_path_set(&ram_disk, &local_path, ".."); + status += fx_directory_first_entry_find(&ram_disk, name); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(45); + } + + /* Create a file at the root directory. */ + status += fx_file_create(&ram_disk, "TEST.TXT"); + + /* Attempt to set the default to a file name. */ + status += fx_directory_local_path_set(&ram_disk, &local_path, "TEST.TXT"); + + /* Check the status. */ + if (status != FX_INVALID_PATH) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(46); + } + + /* Build a maximum local path. */ + status = fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_001"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_002"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_003"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_004"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_005"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_006"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_007"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_008"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_009"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_010"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_011"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_012"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_013"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_014"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "sub-directory-level_015"); + + /* Now set a new local path that will inherit this local path. */ + status += fx_directory_local_path_set(&ram_disk, &local_path1, "..\\sub-directory-level_015"); + + /* Now set an absolute path. */ + status += fx_directory_local_path_set(&ram_disk, &local_path1, "\\sub-directory-level_001"); + status += fx_directory_local_path_set(&ram_disk, &local_path1, ".."); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(47); + } + + /* Clear the local path. */ + status += fx_directory_local_path_clear(&ram_disk); + + /* Build a maximum path default (global) path. */ + status = fx_directory_default_set(&ram_disk, "\\"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_001"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_002"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_003"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_004"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_005"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_006"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_007"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_008"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_009"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_010"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_011"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_012"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_013"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_014"); + status += fx_directory_default_set(&ram_disk, "sub-directory-level_015"); + + /* Now set the local path again, which will inherit the global path. */ + status += fx_directory_local_path_set(&ram_disk, &local_path, "../sub-directory-level_015"); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(48); + } + + /* Test to return FX_BUFFER_ERROR from _fx_directory_local_path_get_copy() */ + status = _fx_directory_local_path_get_copy(&ram_disk, test_path_name_buffer, sizeof(test_path_name_buffer)); + + /* Determine if the test was successful. */ + if (status != FX_BUFFER_ERROR) + { + + printf("ERROR!\n"); + test_control_return(49); + } + + /* Test to each (return_path_name_buffer[0] = '\0') line in _fx_directory_local_path_get_copy() */ + fx_directory_local_path_clear(&ram_disk); + path_name_buffer[0] = 'A'; + status = _fx_directory_local_path_get_copy(&ram_disk, path_name_buffer, sizeof(path_name_buffer)); + status += strcmp(&path_name_buffer[0], "\0"); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(50); + } + +#endif /* FX_NO_LOCAL_PATH */ + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(51); + } + else + { + + printf("SUCCESS!\n"); + test_control_return(0); + } +} + diff --git a/test/regression_test/filex_directory_duplicate_entries_test.c b/test/regression_test/filex_directory_duplicate_entries_test.c new file mode 100644 index 0000000..d022c32 --- /dev/null +++ b/test/regression_test/filex_directory_duplicate_entries_test.c @@ -0,0 +1,251 @@ +/* This FileX test concentrates on recovery operation when there are duplicate entries. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_directory.h" +#include "fx_ram_driver_test.h" +#include "fx_fault_tolerant.h" +#include "fx_utility.h" +#include + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 16*128 +#ifdef FX_ENABLE_FAULT_TOLERANT +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE +#else +#define FAULT_TOLERANT_SIZE 0 +#endif + +#define DIRECTORY_NAME "DUP_DIR" +#define FILE_NAME "abc" + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static CHAR name[256]; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR ram_disk_memory[1024 * 1024]; +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif + +extern ULONG _fx_ram_driver_copy_default_format; + + +/* Define thread prototypes. */ + +void filex_directory_duplicate_entries_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_directory_duplicate_entries_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + +static void delete_duplicate_entry(FX_MEDIA *media_ptr, + CHAR *parent_directory, + CHAR *duplicate_entry_name) +{ +FX_DIR_ENTRY dir_entry; + + fx_directory_default_set(media_ptr, parent_directory); + + /* Get the dir entry of DIRECTORY_NAME. */ + FX_PROTECT + + dir_entry.fx_dir_entry_name = media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + dir_entry.fx_dir_entry_short_name[0] = 0; + _fx_directory_search(media_ptr, duplicate_entry_name, &dir_entry, FX_NULL, FX_NULL); + + /* Now try to remove one of the duplicate dir entries. */ + dir_entry.fx_dir_entry_name[0] = (CHAR)FX_DIR_ENTRY_FREE; + dir_entry.fx_dir_entry_short_name[0] = (CHAR)FX_DIR_ENTRY_FREE; + { + _fx_directory_entry_write(media_ptr, &dir_entry); + } + FX_UNPROTECT +} + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +FX_DIR_ENTRY dir_entry; +FX_MEDIA *media_ptr = &ram_disk; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Directory duplicate entries test......................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + +/* We need a larger disk to test the feature of fault tolerant. */ +#ifdef FX_ENABLE_FAULT_TOLERANT + 512 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster +#else + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster +#endif + + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + +/* We need a larger disk to test the feature of fault tolerant. */ +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Enable fault tolerant if FX_ENABLE_FAULT_TOLERANT is defined. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); +#endif + + /* Create a directory. */ + status = fx_directory_create(&ram_disk, DIRECTORY_NAME); + return_if_fail( status == FX_SUCCESS); + + status = fx_directory_default_set(&ram_disk, DIRECTORY_NAME); + return_if_fail( status == FX_SUCCESS); + + /* Create a file in subdirectory. */ + status = fx_file_create(&ram_disk, FILE_NAME); + return_if_fail( status == FX_SUCCESS); + + status = fx_directory_default_set(&ram_disk, "/"); + return_if_fail( status == FX_SUCCESS); + + /* Get the dir entry of DIRECTORY_NAME. */ + FX_PROTECT + dir_entry.fx_dir_entry_name = ram_disk.fx_media_name_buffer + FX_MAX_LONG_NAME_LEN; + dir_entry.fx_dir_entry_short_name[0] = 0; + status = _fx_directory_search(&ram_disk, DIRECTORY_NAME, &dir_entry, FX_NULL, FX_NULL); + return_if_fail( status == FX_SUCCESS); + + /* Duplicate the dir entry. */ + dir_entry.fx_dir_entry_byte_offset += FX_DIR_ENTRY_SIZE; + { + status = _fx_directory_entry_write(&ram_disk, &dir_entry); + } + return_if_fail( status == FX_SUCCESS); + FX_UNPROTECT + + /* Make sure there are duplicate dir entries. */ + status = fx_directory_first_entry_find(&ram_disk, name); + return_if_fail( status == FX_SUCCESS); + return_if_fail( strcmp(name, DIRECTORY_NAME) == 0); + + status = fx_directory_next_entry_find(&ram_disk, name); + return_if_fail( status == FX_SUCCESS); + return_if_fail( strcmp(name, DIRECTORY_NAME) == 0); + + delete_duplicate_entry(&ram_disk, "/", DIRECTORY_NAME); + + /* Make sure there are no duplicate dir entries. */ + status = fx_directory_first_entry_find(&ram_disk, name); + return_if_fail( status == FX_SUCCESS); + return_if_fail( strcmp(name, DIRECTORY_NAME) == 0); + + status = fx_directory_next_entry_find(&ram_disk, name); + return_if_fail( status != FX_SUCCESS); + + /* Check the file in DIRECTORY_NAME is still valid. */ + status = fx_directory_default_set(&ram_disk, DIRECTORY_NAME); + return_if_fail( status == FX_SUCCESS); + + /* Skip '.' and '..'. */ + status = fx_directory_first_entry_find(&ram_disk, name); + return_if_fail( status == FX_SUCCESS); + status = fx_directory_next_entry_find(&ram_disk, name); + return_if_fail( status == FX_SUCCESS); + + /* Check the filename. */ + status = fx_directory_next_entry_find(&ram_disk, name); + return_if_fail( status == FX_SUCCESS); + return_if_fail( strcmp(name, FILE_NAME) == 0); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(254); + } + else + { + + printf("SUCCESS!\n"); + test_control_return(0); + } +} diff --git a/test/regression_test/filex_directory_first_next_find_test.c b/test/regression_test/filex_directory_first_next_find_test.c new file mode 100644 index 0000000..56f310f --- /dev/null +++ b/test/regression_test/filex_directory_first_next_find_test.c @@ -0,0 +1,1306 @@ +/* This FileX test concentrates on the basic first/next entry find operations. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_ram_driver_test.h" +#include + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 16*128 + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif +static CHAR name[256]; + + +/* Define thread prototypes. */ + +void filex_directory_first_next_find_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_directory_first_next_find_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +CHAR *path_ptr; +UINT attributes; +ULONG size; +ULONG temp; +UINT year; +UINT month; +UINT day; +UINT hour; +UINT minute; +UINT second; +FX_LOCAL_PATH local_path; +UINT i; +// A file name whose first byte is 0xe5. +UCHAR specified_ascii_name[] = { 0xe5, 'a', 'b', 'c', 0}; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Directory first/next entry find test..................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Attempt to get the next entry before the media is opened to generate an error */ + status = fx_directory_next_entry_find(&ram_disk, "/A0"); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(2); + } + + /* Attempt to get the next full entry before the media is opened to generate an error */ + status = fx_directory_next_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(3); + } + + /* Attempt to get the first entry before the media is opened to generate an error */ + status = fx_directory_first_entry_find(&ram_disk, "/A0"); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(4); + } + + /* Attempt to get the first full entry before the media is opened to generate an error */ + status = fx_directory_first_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(5); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(6); + } + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + /* send null pointer to generate an error */ + status = fx_directory_first_entry_find(FX_NULL, "/A0"); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(6); + } + + /* send null pointer to generate an error */ + status = fx_directory_first_full_entry_find(FX_NULL, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(7); + } +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Create a hiearchy of files and sub-directories. */ + status = fx_file_create(&ram_disk, "RootFile01"); + status += fx_file_create(&ram_disk, "RootFile02"); + status += fx_directory_create(&ram_disk, "RootDir03"); + status += fx_directory_create(&ram_disk, "RootDir04"); + status += fx_directory_create(&ram_disk, "RootDir05"); + status += fx_directory_create(&ram_disk, "RootDir06"); + status += fx_directory_create(&ram_disk, "RootDir07"); + status += fx_directory_create(&ram_disk, "RootDir08"); + status += fx_file_create(&ram_disk, "RootFile09"); + status += fx_file_create(&ram_disk, "RootFile10"); + status += fx_directory_create(&ram_disk, "RootDir11"); + status += fx_directory_create(&ram_disk, "RootDir12"); + status += fx_directory_create(&ram_disk, "RootDir13"); + status += fx_directory_create(&ram_disk, "RootDir14"); + status += fx_file_create(&ram_disk, "RootFile15"); + + /* Create the sub-directories for RootDir03. */ + status += fx_directory_create(&ram_disk, "/RootDir03/RootDir03_SubDir01"); + status += fx_file_create(&ram_disk, "/RootDir03/RootDir03_File02"); + status += fx_directory_create(&ram_disk, "/RootDir03/RootDir03_SubDir03"); + status += fx_file_create(&ram_disk, "/RootDir03/RootDir03_File04"); + status += fx_directory_create(&ram_disk, "/RootDir03/RootDir03_SubDir05"); + status += fx_file_create(&ram_disk, "/RootDir03/RootDir03_File06"); + status += fx_directory_create(&ram_disk, "/RootDir03/RootDir03_SubDir07"); + status += fx_file_create(&ram_disk, "/RootDir03/RootDir03_File08"); + + /* Create the sub-directories for RootDir04. */ + status += fx_file_create(&ram_disk, "/RootDir04/RootDir04_File01"); + status += fx_file_create(&ram_disk, "/RootDir04/RootDir04_File02"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir03"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir05"); + status += fx_file_create(&ram_disk, "/RootDir04/RootDir04_File06"); + status += fx_file_create(&ram_disk, "/RootDir04/RootDir04_File07"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir08"); + + /* Create the sub-directories for RootDir05. */ + status += fx_file_create(&ram_disk, "/RootDir05/RootDir05_File02"); + status += fx_file_create(&ram_disk, "/RootDir05/RootDir05_File03"); + status += fx_file_create(&ram_disk, "/RootDir05/RootDir05_File04"); + status += fx_directory_create(&ram_disk, "/RootDir05/RootDir05_SubDir05"); + status += fx_directory_create(&ram_disk, "/RootDir05/RootDir05_SubDir06"); + status += fx_directory_create(&ram_disk, "/RootDir05/RootDir05_SubDir07"); + status += fx_file_create(&ram_disk, "/RootDir05/RootDir05_File08"); + status += fx_directory_create(&ram_disk, "/RootDir05/RootDir05_SubDir09"); + + /* Create the sub-directories for RootDir06. */ + status += fx_directory_create(&ram_disk, "/RootDir06/RootDir06_SubDir01"); + status += fx_directory_create(&ram_disk, "/RootDir06/RootDir06_SubDir02"); + status += fx_directory_create(&ram_disk, "/RootDir06/RootDir06_SubDir03"); + status += fx_file_create(&ram_disk, "/RootDir06/RootDir06_File04"); + status += fx_file_create(&ram_disk, "/RootDir06/RootDir06_File05"); + status += fx_file_create(&ram_disk, "/RootDir06/RootDir06_File06"); + status += fx_file_create(&ram_disk, "/RootDir06/RootDir06_File07"); + status += fx_directory_create(&ram_disk, "/RootDir06/RootDir06_SubDir08"); + + /* Create the sub-directories for RootDir07. */ + status += fx_directory_create(&ram_disk, "/RootDir07/RootDir07_SubDir01"); + status += fx_directory_create(&ram_disk, "/RootDir07/RootDir07_SubDir02"); + status += fx_directory_create(&ram_disk, "/RootDir07/RootDir07_SubDir03"); + status += fx_directory_create(&ram_disk, "/RootDir07/RootDir07_SubDir04"); + status += fx_file_create(&ram_disk, "/RootDir07/RootDir07_File05"); + status += fx_file_create(&ram_disk, "/RootDir07/RootDir07_File06"); + status += fx_file_create(&ram_disk, "/RootDir07/RootDir07_File07"); + status += fx_file_create(&ram_disk, "/RootDir07/RootDir07_File08"); + + /* Create the sub-directories for RootDir08. */ + status += fx_file_create(&ram_disk, "/RootDir08/RootDir08_File01"); + status += fx_file_create(&ram_disk, "/RootDir08/RootDir08_File02"); + status += fx_file_create(&ram_disk, "/RootDir08/RootDir08_File03"); + status += fx_file_create(&ram_disk, "/RootDir08/RootDir08_File04"); + status += fx_directory_create(&ram_disk, "/RootDir08/RootDir08_SubDir05"); + status += fx_directory_create(&ram_disk, "/RootDir08/RootDir08_SubDir06"); + status += fx_directory_create(&ram_disk, "/RootDir08/RootDir08_SubDir07"); + status += fx_directory_create(&ram_disk, "/RootDir08/RootDir08_SubDir08"); + + /* Create the sub-directories for RootDir11. */ + status += fx_file_create(&ram_disk, "/RootDir11/RootDir11_File01"); + status += fx_file_create(&ram_disk, "/RootDir11/RootDir11_File02"); + status += fx_directory_create(&ram_disk, "/RootDir11/RootDir11_SubDir03"); + status += fx_directory_create(&ram_disk, "/RootDir11/RootDir11_SubDir04"); + status += fx_file_create(&ram_disk, "/RootDir11/RootDir11_File05"); + status += fx_file_create(&ram_disk, "/RootDir11/RootDir11_File06"); + status += fx_directory_create(&ram_disk, "/RootDir11/RootDir11_SubDir07"); + status += fx_directory_create(&ram_disk, "/RootDir11/RootDir11_SubDir08"); + + /* Create the sub-directories for RootDir12. */ + status += fx_file_create(&ram_disk, "/RootDir12/RootDir12_File01"); + status += fx_file_create(&ram_disk, "/RootDir12/RootDir12_File02"); + status += fx_directory_create(&ram_disk, "/RootDir12/RootDir12_SubDir03"); + status += fx_directory_create(&ram_disk, "/RootDir12/RootDir12_SubDir04"); + status += fx_file_create(&ram_disk, "/RootDir12/RootDir12_File05"); + status += fx_file_create(&ram_disk, "/RootDir12/RootDir12_File06"); + status += fx_directory_create(&ram_disk, "/RootDir12/RootDir12_SubDir07"); + status += fx_directory_create(&ram_disk, "/RootDir12/RootDir12_SubDir08"); + + /* Create the sub-directories for RootDir13. */ + status += fx_file_create(&ram_disk, "/RootDir13/RootDir13_File01"); + status += fx_file_create(&ram_disk, "/RootDir13/RootDir13_File02"); + status += fx_directory_create(&ram_disk, "/RootDir13/RootDir13_SubDir03"); + status += fx_directory_create(&ram_disk, "/RootDir13/RootDir13_SubDir04"); + status += fx_file_create(&ram_disk, "/RootDir13/RootDir13_File05"); + status += fx_file_create(&ram_disk, "/RootDir13/RootDir13_File06"); + status += fx_directory_create(&ram_disk, "/RootDir13/RootDir13_SubDir07"); + status += fx_directory_create(&ram_disk, "/RootDir13/RootDir13_SubDir08"); + + /* Create the Sub-sub directories under RootDir04. */ + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir03/RootDir04_SubDir03_SubSubDir01"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir03/RootDir04_SubDir03_SubSubDir02"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir03/RootDir04_SubDir03_SubSubDir03"); + status += fx_file_create(&ram_disk, "/RootDir04/RootDir04_SubDir03/RootDir04_SubDir03_SubSubFile04"); + + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir01"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir02"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir03"); + status += fx_file_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubFile04"); + + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir05/RootDir04_SubDir05_SubSubDir01"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir05/RootDir04_SubDir05_SubSubDir02"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir05/RootDir04_SubDir05_SubSubDir03"); + status += fx_file_create(&ram_disk, "/RootDir04/RootDir04_SubDir05/RootDir04_SubDir05_SubSubFile04"); + + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir08/RootDir04_SubDir08_SubSubDir01"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir08/RootDir04_SubDir08_SubSubDir02"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir08/RootDir04_SubDir08_SubSubDir03"); + status += fx_file_create(&ram_disk, "/RootDir04/RootDir04_SubDir05/RootDir04_SubDir08_SubSubFile04"); + + /* Create the sub-sub-sub directories under RootDir04. */ + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir01/RootDir04_SubDir04_SubSubDir01_SubSubSubDir01"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir01/RootDir04_SubDir04_SubSubDir01_SubSubSubDir02"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir01/RootDir04_SubDir04_SubSubDir01_SubSubSubDir03"); + status += fx_file_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir01/RootDir04_SubDir04_SubSubDir01_SubSubSubFile04"); + + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir02/RootDir04_SubDir04_SubSubDir01_SubSubSubDir01"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir02/RootDir04_SubDir04_SubSubDir01_SubSubSubDir02"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir02/RootDir04_SubDir04_SubSubDir01_SubSubSubDir03"); + status += fx_file_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir02/RootDir04_SubDir04_SubSubDir01_SubSubSubFile04"); + + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir03/RootDir04_SubDir04_SubSubDir01_SubSubSubDir01"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir03/RootDir04_SubDir04_SubSubDir01_SubSubSubDir02"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir03/RootDir04_SubDir04_SubSubDir01_SubSubSubDir03"); + status += fx_file_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir03/RootDir04_SubDir04_SubSubDir01_SubSubSubFile04"); + + /* Get the current path. This should be / or NULL. */ + status += fx_directory_default_get(&ram_disk, &path_ptr); + + /* Check the status. */ + if ((status != FX_SUCCESS) || (path_ptr[0])) + { + + /* Error getting the path. Return to caller. */ + printf("ERROR!\n"); + test_control_return(8); + } + + /* Set the current path. This should be / or NULL. */ + status = fx_directory_default_set(&ram_disk, "/"); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error setting the path. Return to caller. */ + printf("ERROR!\n"); + test_control_return(9); + } + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + /* send null pointer to generate an error */ + status = fx_directory_next_entry_find(FX_NULL, name); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(10); + } +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Pickup the first entry in the root directory. */ + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, "RootFile01")) + status++; + status += fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "RootFile02")) + status++; + status += fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "RootDir03")) + status++; + status += fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "RootDir04")) + status++; + status += fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "RootDir05")) + status++; + status += fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "RootDir06")) + status++; + status += fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "RootDir07")) + status++; + status += fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "RootDir08")) + status++; + status += fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "RootFile09")) + status++; + status += fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "RootFile10")) + status++; + status += fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "RootDir11")) + status++; + status += fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "RootDir12")) + status++; + status += fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "RootDir13")) + status++; + status += fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "RootDir14")) + status++; + status += fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "RootFile15")) + status++; + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Now read one past the number of entries in order to get an error condition. */ + status += fx_directory_next_entry_find(&ram_disk, name); + + /* Determine if the test was successful. */ + if (status != FX_NO_MORE_ENTRIES) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Now test the full directory entry services. */ + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + /* send null pointer to generate an error */ + status = fx_directory_next_full_entry_find(FX_NULL, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(13); + } +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Pickup the first entry in the root directory. */ + status = fx_directory_first_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (strcmp(name, "RootFile01")) + status++; + status += fx_directory_next_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (strcmp(name, "RootFile02")) + status++; + status += fx_directory_next_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (strcmp(name, "RootDir03")) + status++; + status += fx_directory_next_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (strcmp(name, "RootDir04")) + status++; + status += fx_directory_next_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (strcmp(name, "RootDir05")) + status++; + status += fx_directory_next_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (strcmp(name, "RootDir06")) + status++; + status += fx_directory_next_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (strcmp(name, "RootDir07")) + status++; + status += fx_directory_next_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (strcmp(name, "RootDir08")) + status++; + status += fx_directory_next_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (strcmp(name, "RootFile09")) + status++; + status += fx_directory_next_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (strcmp(name, "RootFile10")) + status++; + status += fx_directory_next_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (strcmp(name, "RootDir11")) + status++; + status += fx_directory_next_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (strcmp(name, "RootDir12")) + status++; + status += fx_directory_next_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (strcmp(name, "RootDir13")) + status++; + status += fx_directory_next_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (strcmp(name, "RootDir14")) + status++; + status += fx_directory_next_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (strcmp(name, "RootFile15")) + status++; + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Now read one past the number of entries in order to get an error condition. */ + status += fx_directory_next_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + + /* Determine if the test was successful. */ + if (status != FX_NO_MORE_ENTRIES) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Okay, now postion to the deepest sub-directory and ensure that the directory first/next + operations work properly there as well. */ + + /* Set the current path. This should be / or NULL. */ + status = fx_directory_default_set(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir02"); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error setting the path. Return to caller. */ + printf("ERROR!\n"); + test_control_return(16); + } + + /* Pickup the first entry in this sub-sub directory. */ + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, ".")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "..")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "RootDir04_SubDir04_SubSubDir01_SubSubSubDir01")) + status++; + status += fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "RootDir04_SubDir04_SubSubDir01_SubSubSubDir02")) + status++; + status += fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "RootDir04_SubDir04_SubSubDir01_SubSubSubDir03")) + status++; + status += fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "RootDir04_SubDir04_SubSubDir01_SubSubSubFile04")) + status++; + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Now read one past the number of entries in order to get an error condition. */ + status += fx_directory_next_entry_find(&ram_disk, name); + + /* Determine if the test was successful. */ + if (status != FX_NO_MORE_ENTRIES) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Now do the same thing but with the full find first/next. */ + +/* pick up the first full entry with a local path defined to get to all of the code */ +#ifndef FX_NO_LOCAL_PATH + status = fx_directory_local_path_set(&ram_disk, &local_path, "/RootDir04/RootDir04_SubDir03"); + status += fx_directory_first_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (strcmp(name, ".")) + status++; + status += fx_directory_local_path_clear(&ram_disk); + status += fx_directory_local_path_set(&ram_disk, &local_path, "/"); + status += fx_directory_first_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + status += fx_directory_local_path_clear(&ram_disk); + + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(19); + } +#endif + + /* Pickup the first entry in this sub-sub directory. */ + status = fx_directory_first_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (strcmp(name, ".")) + status++; + status = fx_directory_next_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (strcmp(name, "..")) + status++; + status = fx_directory_next_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (strcmp(name, "RootDir04_SubDir04_SubSubDir01_SubSubSubDir01")) + status++; + status += fx_directory_next_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (strcmp(name, "RootDir04_SubDir04_SubSubDir01_SubSubSubDir02")) + status++; + status += fx_directory_next_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (strcmp(name, "RootDir04_SubDir04_SubSubDir01_SubSubSubDir03")) + status++; + status += fx_directory_next_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + if (strcmp(name, "RootDir04_SubDir04_SubSubDir01_SubSubSubFile04")) + status++; + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Now read one past the number of entries in order to get an error condition. */ + status += fx_directory_next_full_entry_find(&ram_disk, name, &attributes, + &size, &year, &month, &day, &hour, &minute, &second); + + /* Determine if the test was successful. */ + if (status != FX_NO_MORE_ENTRIES) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(22); + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4096, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the ram_disk - 1 sector cache. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(24); + } + + /* Call first entry find with nothing in the root directory. */ + status = fx_directory_first_entry_find(&ram_disk, name); + + /* This should return no more entries. */ + if (status != FX_NO_MORE_ENTRIES) + { + printf("ERROR!\n"); + test_control_return(25); + } + + /* Create a directory structure that we can traverse. */ + status = fx_directory_create(&ram_disk, "\\SUB1"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST1.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST2.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST3.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST4.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST5.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST6.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST7.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST8.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST9.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST10.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST11.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST12.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST13.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST14.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST15.TXT"); + status += fx_file_delete(&ram_disk, "\\SUB1\\TEST7.TXT"); /* Leave a free hole in the directory. */ + status += fx_directory_default_set(&ram_disk, "\\SUB1"); + /* See if we can add a 255 character name. */ + for (i = 0; i < 256; i++) + { + name[i] = 'a'; + } + name[255] = 0; + + /* Create a max length named file in this sub-directory. */ + status += fx_file_create(&ram_disk, name); + + /* Check for any errors. */ + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(26); + } + + /* Now get the first entry... with a cluster of 0 error first. Note this calls next entry find inside. */ + _fx_utility_fat_entry_read_error_request = 10001; + status = fx_directory_first_entry_find(&ram_disk, name); + _fx_utility_fat_entry_read_error_request = 0; + + /* This should return no more entries. */ + if (status != FX_FAT_READ_ERROR) + { + printf("ERROR!\n"); + test_control_return(27); + } + + /* Now get the first entry... with a cluster equal to itself first. Note this calls next entry find inside. */ + _fx_utility_fat_entry_read_error_request = 30001; + status = fx_directory_first_entry_find(&ram_disk, name); + _fx_utility_fat_entry_read_error_request = 0; + + /* This should return no more entries. */ + if (status != FX_FAT_READ_ERROR) + { + printf("ERROR!\n"); + test_control_return(28); + } + + /* Now get the first entry... with an I/O error on the FAT read. Note this calls next entry find inside. */ + _fx_utility_fat_entry_read_error_request = 1; + status = fx_directory_first_entry_find(&ram_disk, name); + _fx_utility_fat_entry_read_error_request = 0; + + /* This should return no more entries. */ + if (status != FX_IO_ERROR) + { + printf("ERROR!\n"); + test_control_return(29); + } + + /* Now get the first entry... with a smaller value of total clusters. Note this calls next entry find inside. */ + temp = ram_disk.fx_media_total_clusters; + ram_disk.fx_media_total_clusters = 1; + status = fx_directory_first_entry_find(&ram_disk, name); + ram_disk.fx_media_total_clusters = temp; + + /* This should return no more entries. */ + if (status != FX_FAT_READ_ERROR) + { + printf("ERROR!\n"); + test_control_return(30); + } + + /* Now get the first entry... successfully. Note this calls next entry find inside. */ + status = fx_directory_first_entry_find(&ram_disk, name); /* TEST.TXT */ + + /* Check for any errors. */ + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(31); + } + + /* Now flush everything out. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Now get the first entry... with an I/O error on the directory read. Note this calls next entry find inside. */ + _fx_utility_logical_sector_read_error_request = 1; + status = fx_directory_next_entry_find(&ram_disk, name); + _fx_utility_logical_sector_read_error_request = 0; + + /* This should return no more entries. */ + if (status != FX_IO_ERROR) + { + printf("ERROR!\n"); + test_control_return(32); + } + + /* Now get all the directory entires. */ + status = fx_directory_next_entry_find(&ram_disk, name); /* . */ + status = fx_directory_next_entry_find(&ram_disk, name); /* .. */ + status = fx_directory_next_entry_find(&ram_disk, name); /* TEST1.TXT */ + status += fx_directory_next_entry_find(&ram_disk, name); /* TEST2.TXT */ + status += fx_directory_next_entry_find(&ram_disk, name); /* TEST3.TXT */ + status += fx_directory_next_entry_find(&ram_disk, name); /* TEST4.TXT */ + status += fx_directory_next_entry_find(&ram_disk, name); /* TEST5.TXT */ + status += fx_directory_next_entry_find(&ram_disk, name); /* TEST6.TXT */ + status += fx_directory_next_entry_find(&ram_disk, name); /* TEST8.TXT */ + status += fx_directory_next_entry_find(&ram_disk, name); /* TEST9.TXT */ + status += fx_directory_next_entry_find(&ram_disk, name); /* TEST10.TXT */ + status += fx_directory_next_entry_find(&ram_disk, name); /* TEST11.TXT */ + status += fx_directory_next_entry_find(&ram_disk, name); /* TEST12.TXT */ + status += fx_directory_next_entry_find(&ram_disk, name); /* TEST13.TXT */ + status += fx_directory_next_entry_find(&ram_disk, name); /* TEST14.TXT */ + status += fx_directory_next_entry_find(&ram_disk, name); /* TEST15.TXT */ + status += fx_directory_next_entry_find(&ram_disk, name); /* 255 character file name.... */ + + /* Check for any errors. */ + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(33); + } + + /* Now make another request to get the FX_NO_MORE_ENTRIES error. */ + status = fx_directory_next_entry_find(&ram_disk, name); /* END of Directory! */ + + /* This should return no more entries. */ + if (status != FX_NO_MORE_ENTRIES) + { + printf("ERROR!\n"); + test_control_return(34); + } + + /* Now make another request to get the FX_NO_MORE_ENTRIES error, but artifically move the current entry ahead. */ + temp = ram_disk.fx_media_default_path.fx_path_current_entry; + ram_disk.fx_media_default_path.fx_path_current_entry = 100; + status = fx_directory_next_entry_find(&ram_disk, name); /* END of Directory! */ + ram_disk.fx_media_default_path.fx_path_current_entry = temp; + + /* This should return no more entries. */ + if (status != FX_NO_MORE_ENTRIES) + { + printf("ERROR!\n"); + test_control_return(35); + } + + /* Now create enough sub-directories to exceed the maximum path. */ + status = fx_directory_create(&ram_disk, "subdirectory1"); + status += fx_directory_default_set(&ram_disk, "subdirectory1"); + status += fx_directory_create(&ram_disk, "subdirectory2"); + status += fx_directory_default_set(&ram_disk, "subdirectory2"); + status += fx_directory_create(&ram_disk, "subdirectory3"); + status += fx_directory_default_set(&ram_disk, "subdirectory3"); + status += fx_directory_create(&ram_disk, "subdirectory4"); + status += fx_directory_default_set(&ram_disk, "subdirectory4"); + status += fx_directory_create(&ram_disk, "subdirectory5"); + status += fx_directory_default_set(&ram_disk, "subdirectory5"); + status += fx_directory_create(&ram_disk, "subdirectory6"); + status += fx_directory_default_set(&ram_disk, "subdirectory6"); + status += fx_directory_create(&ram_disk, "subdirectory7"); + status += fx_directory_default_set(&ram_disk, "subdirectory7"); + status += fx_directory_create(&ram_disk, "subdirectory8"); + status += fx_directory_default_set(&ram_disk, "subdirectory8"); + status += fx_directory_create(&ram_disk, "subdirectory9"); + status += fx_directory_default_set(&ram_disk, "subdirectory9"); + status += fx_directory_create(&ram_disk, "subdirectory10"); + status += fx_directory_default_set(&ram_disk, "subdirectory10"); + status += fx_directory_create(&ram_disk, "subdirectory11"); + status += fx_directory_default_set(&ram_disk, "subdirectory11"); + status += fx_directory_create(&ram_disk, "subdirectory12"); + status += fx_directory_default_set(&ram_disk, "subdirectory12"); + status += fx_directory_create(&ram_disk, "subdirectory13"); + status += fx_directory_default_set(&ram_disk, "subdirectory13"); + status += fx_directory_create(&ram_disk, "subdirectory14"); + status += fx_directory_default_set(&ram_disk, "subdirectory14"); + status += fx_directory_create(&ram_disk, "subdirectory15"); + status += fx_directory_default_set(&ram_disk, "subdirectory15"); + status += fx_directory_create(&ram_disk, "subdirectory16"); + status += fx_directory_default_set(&ram_disk, "subdirectory16"); + status += fx_directory_create(&ram_disk, "subdirectory17"); + status += fx_directory_default_set(&ram_disk, "subdirectory17"); + status += fx_directory_create(&ram_disk, "subdirectory18"); + status += fx_directory_default_set(&ram_disk, "subdirectory18"); + status += fx_directory_create(&ram_disk, "subdirectory19"); + status += fx_directory_default_set(&ram_disk, "subdirectory19"); + status += fx_directory_create(&ram_disk, "subdirectory20"); + status += fx_directory_default_set(&ram_disk, "subdirectory20"); + status += fx_directory_create(&ram_disk, "subdirectory21"); + status += fx_directory_default_set(&ram_disk, "subdirectory21"); + status += fx_directory_create(&ram_disk, "subdirectory22"); + status += fx_directory_default_set(&ram_disk, "subdirectory22"); + status += fx_directory_create(&ram_disk, "subdirectory23"); + status += fx_directory_default_set(&ram_disk, "subdirectory23"); + status += fx_directory_create(&ram_disk, "subdirectory24"); + status += fx_directory_default_set(&ram_disk, "subdirectory24"); + status += fx_directory_create(&ram_disk, "subdirectory25"); + status += fx_directory_default_set(&ram_disk, "subdirectory25"); + status += fx_directory_create(&ram_disk, "subdirectory26"); + status += fx_directory_default_set(&ram_disk, "subdirectory26"); + status += fx_directory_create(&ram_disk, "subdirectory27"); + status += fx_directory_default_set(&ram_disk, "subdirectory27"); + status += fx_directory_create(&ram_disk, "subdirectory28"); + status += fx_directory_default_set(&ram_disk, "subdirectory28"); + status += fx_directory_create(&ram_disk, "subdirectory29"); + status += fx_directory_default_set(&ram_disk, "subdirectory29"); + status += fx_directory_first_entry_find(&ram_disk, name); /* Should be . */ + status += fx_directory_next_entry_find(&ram_disk, name); /* Should be .. */ + + /* Check for any errors. */ + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(36); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(37); + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4096, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(38); + } + + /* Open the ram_disk - 1 sector cache. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(39); + } + + /* Call first entry find with nothing in the root directory. */ + status = fx_directory_first_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); + + /* This should return no more entries. */ + if (status != FX_NO_MORE_ENTRIES) + { + printf("ERROR!\n"); + test_control_return(40); + } + + /* Create a directory structure that we can traverse. */ + status = fx_directory_create(&ram_disk, "\\SUB1"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST1.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST2.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST3.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST4.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST5.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST6.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST7.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST8.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST9.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST10.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST11.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST12.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST13.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST14.TXT"); + status += fx_file_create(&ram_disk, "\\SUB1\\TEST15.TXT"); + status += fx_file_delete(&ram_disk, "\\SUB1\\TEST7.TXT"); /* Leave a free hole in the directory. */ + status += fx_directory_default_set(&ram_disk, "\\SUB1"); + /* See if we can add a 255 character name. */ + for (i = 0; i < 256; i++) + { + name[i] = 'a'; + } + name[255] = 0; + + /* Create a max length named file in this sub-directory. */ + status += fx_file_create(&ram_disk, name); + + /* Check for any errors. */ + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(41); + } + + /* Now get the first entry... with a cluster of 0 error first. Note this calls next entry find inside. */ + _fx_utility_fat_entry_read_error_request = 10001; + status = fx_directory_first_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &minute, FX_NULL); + _fx_utility_fat_entry_read_error_request = 0; + + /* This should return no more entries. */ + if (status != FX_FAT_READ_ERROR) + { + printf("ERROR!\n"); + test_control_return(42); + } + + /* Now get the first entry... with a cluster equal to itself first. Note this calls next entry find inside. */ + _fx_utility_fat_entry_read_error_request = 30001; + status = fx_directory_first_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); + _fx_utility_fat_entry_read_error_request = 0; + + /* This should return no more entries. */ + if (status != FX_FAT_READ_ERROR) + { + printf("ERROR!\n"); + test_control_return(43); + } + + /* Now get the first entry... with an I/O error on the FAT read. Note this calls next entry find inside. */ + _fx_utility_fat_entry_read_error_request = 1; + status = fx_directory_first_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); + _fx_utility_fat_entry_read_error_request = 0; + + /* This should return no more entries. */ + if (status != FX_IO_ERROR) + { + printf("ERROR!\n"); + test_control_return(44); + } + + /* Now get the first entry... with a smaller value of total clusters. Note this calls next entry find inside. */ + temp = ram_disk.fx_media_total_clusters; + ram_disk.fx_media_total_clusters = 1; + status = fx_directory_first_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); + ram_disk.fx_media_total_clusters = temp; + + /* This should return no more entries. */ + if (status != FX_FAT_READ_ERROR) + { + printf("ERROR!\n"); + test_control_return(45); + } + + /* Now get the first entry... successfully. Note this calls next entry find inside. */ + status = fx_directory_first_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); /* TEST.TXT */ + + /* Check for any errors. */ + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(46); + } + + /* Now flush everything out. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Now get the first entry... with an I/O error on the directory read. Note this calls next entry find inside. */ + _fx_utility_logical_sector_read_error_request = 1; + status = fx_directory_next_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); + _fx_utility_logical_sector_read_error_request = 0; + + /* This should return no more entries. */ + if (status != FX_IO_ERROR) + { + printf("ERROR!\n"); + test_control_return(47); + } + + /* Now get all the directory entires. */ + status = fx_directory_next_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); /* . */ + status = fx_directory_next_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &minute, FX_NULL); /* .. */ + status = fx_directory_next_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); /* TEST1.TXT */ + status += fx_directory_next_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); /* TEST2.TXT */ + status += fx_directory_next_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); /* TEST3.TXT */ + status += fx_directory_next_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); /* TEST4.TXT */ + status += fx_directory_next_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); /* TEST5.TXT */ + status += fx_directory_next_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); /* TEST6.TXT */ + status += fx_directory_next_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); /* TEST8.TXT */ + status += fx_directory_next_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); /* TEST9.TXT */ + status += fx_directory_next_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); /* TEST10.TXT */ + status += fx_directory_next_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); /* TEST11.TXT */ + status += fx_directory_next_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); /* TEST12.TXT */ + status += fx_directory_next_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); /* TEST13.TXT */ + status += fx_directory_next_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); /* TEST14.TXT */ + status += fx_directory_next_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); /* TEST15.TXT */ + status += fx_directory_next_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); /* 255 character file name.... */ + + /* Check for any errors. */ + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(48); + } + + /* Now make another request to get the FX_NO_MORE_ENTRIES error. */ + status = fx_directory_next_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); /* END of Directory! */ + + /* This should return no more entries. */ + if (status != FX_NO_MORE_ENTRIES) + { + printf("ERROR!\n"); + test_control_return(49); + } + + /* Now make another request to get the FX_NO_MORE_ENTRIES error, but artifically move the current entry ahead. */ + temp = ram_disk.fx_media_default_path.fx_path_current_entry; + ram_disk.fx_media_default_path.fx_path_current_entry = 100; + status = fx_directory_next_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); /* END of Directory! */ + ram_disk.fx_media_default_path.fx_path_current_entry = temp; + + /* This should return no more entries. */ + if (status != FX_NO_MORE_ENTRIES) + { + printf("ERROR!\n"); + test_control_return(50); + } + + /* Now create enough sub-directories to exceed the maximum path. */ + status = fx_directory_create(&ram_disk, "subdirectory1"); + status += fx_directory_default_set(&ram_disk, "subdirectory1"); + + // Create the file whose name's first byte is 0xe5. + status = fx_file_create(&ram_disk, (CHAR *)specified_ascii_name); + + status += fx_directory_create(&ram_disk, "subdirectory2"); + status += fx_directory_default_set(&ram_disk, "subdirectory2"); + status += fx_directory_create(&ram_disk, "subdirectory3"); + status += fx_directory_default_set(&ram_disk, "subdirectory3"); + status += fx_directory_create(&ram_disk, "subdirectory4"); + status += fx_directory_default_set(&ram_disk, "subdirectory4"); + status += fx_directory_create(&ram_disk, "subdirectory5"); + status += fx_directory_default_set(&ram_disk, "subdirectory5"); + status += fx_directory_create(&ram_disk, "subdirectory6"); + status += fx_directory_default_set(&ram_disk, "subdirectory6"); + status += fx_directory_create(&ram_disk, "subdirectory7"); + status += fx_directory_default_set(&ram_disk, "subdirectory7"); + status += fx_directory_create(&ram_disk, "subdirectory8"); + status += fx_directory_default_set(&ram_disk, "subdirectory8"); + status += fx_directory_create(&ram_disk, "subdirectory9"); + status += fx_directory_default_set(&ram_disk, "subdirectory9"); + status += fx_directory_create(&ram_disk, "subdirectory10"); + status += fx_directory_default_set(&ram_disk, "subdirectory10"); + status += fx_directory_create(&ram_disk, "subdirectory11"); + status += fx_directory_default_set(&ram_disk, "subdirectory11"); + status += fx_directory_create(&ram_disk, "subdirectory12"); + status += fx_directory_default_set(&ram_disk, "subdirectory12"); + status += fx_directory_create(&ram_disk, "subdirectory13"); + status += fx_directory_default_set(&ram_disk, "subdirectory13"); + status += fx_directory_create(&ram_disk, "subdirectory14"); + status += fx_directory_default_set(&ram_disk, "subdirectory14"); + status += fx_directory_create(&ram_disk, "subdirectory15"); + status += fx_directory_default_set(&ram_disk, "subdirectory15"); + status += fx_directory_create(&ram_disk, "subdirectory16"); + status += fx_directory_default_set(&ram_disk, "subdirectory16"); + status += fx_directory_create(&ram_disk, "subdirectory17"); + status += fx_directory_default_set(&ram_disk, "subdirectory17"); + status += fx_directory_create(&ram_disk, "subdirectory18"); + status += fx_directory_default_set(&ram_disk, "subdirectory18"); + status += fx_directory_create(&ram_disk, "subdirectory19"); + status += fx_directory_default_set(&ram_disk, "subdirectory19"); + status += fx_directory_create(&ram_disk, "subdirectory20"); + status += fx_directory_default_set(&ram_disk, "subdirectory20"); + status += fx_directory_create(&ram_disk, "subdirectory21"); + status += fx_directory_default_set(&ram_disk, "subdirectory21"); + status += fx_directory_create(&ram_disk, "subdirectory22"); + status += fx_directory_default_set(&ram_disk, "subdirectory22"); + status += fx_directory_create(&ram_disk, "subdirectory23"); + status += fx_directory_default_set(&ram_disk, "subdirectory23"); + status += fx_directory_create(&ram_disk, "subdirectory24"); + status += fx_directory_default_set(&ram_disk, "subdirectory24"); + status += fx_directory_create(&ram_disk, "subdirectory25"); + status += fx_directory_default_set(&ram_disk, "subdirectory25"); + status += fx_directory_create(&ram_disk, "subdirectory26"); + status += fx_directory_default_set(&ram_disk, "subdirectory26"); + status += fx_directory_create(&ram_disk, "subdirectory27"); + status += fx_directory_default_set(&ram_disk, "subdirectory27"); + status += fx_directory_create(&ram_disk, "subdirectory28"); + status += fx_directory_default_set(&ram_disk, "subdirectory28"); + status += fx_directory_create(&ram_disk, "subdirectory29"); + status += fx_directory_default_set(&ram_disk, "subdirectory29"); + status += fx_directory_first_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); /* Should be . */ + status += fx_directory_next_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); /* Should be .. */ + + /* Check for any errors. */ + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(51); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(52); + } + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4096, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Open the ram_disk. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_value_if_fail(status == FX_SUCCESS, 53); + + status = fx_file_create(&ram_disk, (CHAR *)specified_ascii_name); + return_value_if_fail(status == FX_SUCCESS, 54); + + specified_ascii_name[1]++; + status = fx_file_create(&ram_disk, (CHAR *)specified_ascii_name); + return_value_if_fail(status == FX_SUCCESS, 55); + + // Test the file whose name's first byte is 0xe5. + status = fx_directory_first_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); + + /* Now get all the directory entires. */ + status += fx_directory_next_full_entry_find(&ram_disk, name, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, &second); + return_value_if_fail(status == FX_SUCCESS, 56); + + // Try fx_directory_first_entry_find this time. + status = fx_directory_first_entry_find(&ram_disk, name); + return_value_if_fail(status == FX_SUCCESS, 57); + + // Try fx_media_volume_set this time. + status = fx_media_volume_set(&ram_disk, (CHAR *)specified_ascii_name); + return_value_if_fail(status == FX_SUCCESS, 58); + + printf("SUCCESS!\n"); + test_control_return(0); +} + diff --git a/test/regression_test/filex_directory_local_path_test.c b/test/regression_test/filex_directory_local_path_test.c new file mode 100644 index 0000000..ac98ea1 --- /dev/null +++ b/test/regression_test/filex_directory_local_path_test.c @@ -0,0 +1,803 @@ +/* This FileX test concentrates on the local path operations. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#include "fx_api.h" +#include "fx_ram_driver_test.h" +#include + +#define DEMO_STACK_SIZE 8192 +#define CACHE_SIZE 16*128 + + +/* Define the ThreadX and FileX object control blocks... */ + +static TX_THREAD ftest_0; +static FX_MEDIA ram_disk; +static UINT error_counter = 0; + + +/* Define the counters used in the test application... */ + +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; + +static UINT expected_index = 0; +static CHAR * expected_name[] = { + + "RootFile01", + "RootFile02", + "RootDir03", + ".", + "..", + "RootDir03_SubDir01", + ".", + "..", + "RootDir03_File02", + "RootDir03_SubDir03", + ".", + "..", + "RootDir03_File04", + "RootDir03_SubDir05", + ".", + "..", + "RootDir03_File06", + "RootDir03_SubDir07", + ".", + "..", + "RootDir03_File08", + "RootDir04", + ".", + "..", + "RootDir04_File01", + "RootDir04_File02", + "RootDir04_SubDir03", + ".", + "..", + "RootDir04_SubDir03_SubSubDir01", + ".", + "..", + "RootDir04_SubDir03_SubSubDir02", + ".", + "..", + "RootDir04_SubDir03_SubSubDir03", + ".", + "..", + "RootDir04_SubDir03_SubSubFile04", + "RootDir04_SubDir04", + ".", + "..", + "RootDir04_SubDir04_SubSubDir01", + ".", + "..", + "RootDir04_SubDir04_SubSubDir01_SubSubSubDir01", + ".", + "..", + "RootDir04_SubDir04_SubSubDir01_SubSubSubDir02", + ".", + "..", + "RootDir04_SubDir04_SubSubDir01_SubSubSubDir03", + ".", + "..", + "RootDir04_SubDir04_SubSubDir01_SubSubSubFile04", + "RootDir04_SubDir04_SubSubDir02", + ".", + "..", + "RootDir04_SubDir04_SubSubDir01_SubSubSubDir01", + ".", + "..", + "RootDir04_SubDir04_SubSubDir01_SubSubSubDir02", + ".", + "..", + "RootDir04_SubDir04_SubSubDir01_SubSubSubDir03", + ".", + "..", + "RootDir04_SubDir04_SubSubDir01_SubSubSubFile04", + "RootDir04_SubDir04_SubSubDir03", + ".", + "..", + "RootDir04_SubDir04_SubSubDir01_SubSubSubDir01", + ".", + "..", + "RootDir04_SubDir04_SubSubDir01_SubSubSubDir02", + ".", + "..", + "RootDir04_SubDir04_SubSubDir01_SubSubSubDir03", + ".", + "..", + "RootDir04_SubDir04_SubSubDir01_SubSubSubFile04", + "RootDir04_SubDir04_SubSubFile04", + "RootDir04_SubDir05", + ".", + "..", + "RootDir04_SubDir05_SubSubDir01", + ".", + "..", + "RootDir04_SubDir05_SubSubDir02", + ".", + "..", + "RootDir04_SubDir05_SubSubDir03", + ".", + "..", + "RootDir04_SubDir05_SubSubFile04", + "RootDir04_SubDir08_SubSubFile04", + "RootDir04_File06", + "RootDir04_File07", + "RootDir04_SubDir08", + ".", + "..", + "RootDir04_SubDir08_SubSubDir01", + ".", + "..", + "RootDir04_SubDir08_SubSubDir02", + ".", + "..", + "RootDir04_SubDir08_SubSubDir03", + ".", + "..", + "RootDir05", + ".", + "..", + "RootDir05_File02", + "RootDir05_File03", + "RootDir05_File04", + "RootDir05_SubDir05", + ".", + "..", + "RootDir05_SubDir06", + ".", + "..", + "RootDir05_SubDir07", + ".", + "..", + "RootDir05_File08", + "RootDir05_SubDir09", + ".", + "..", + "RootDir06", + ".", + "..", + "RootDir06_SubDir01", + ".", + "..", + "RootDir06_SubDir02", + ".", + "..", + "RootDir06_SubDir03", + ".", + "..", + "RootDir06_File04", + "RootDir06_File05", + "RootDir06_File06", + "RootDir06_File07", + "RootDir06_SubDir08", + ".", + "..", + "RootDir07", + ".", + "..", + "RootDir07_SubDir01", + ".", + "..", + "RootDir07_SubDir02", + ".", + "..", + "RootDir07_SubDir03", + ".", + "..", + "RootDir07_SubDir04", + ".", + "..", + "RootDir07_File05", + "RootDir07_File06", + "RootDir07_File07", + "RootDir07_File08", + "RootDir08", + ".", + "..", + "RootDir08_File01", + "RootDir08_File02", + "RootDir08_File03", + "RootDir08_File04", + "RootDir08_SubDir05", + ".", + "..", + "RootDir08_SubDir06", + ".", + "..", + "RootDir08_SubDir07", + ".", + "..", + "RootDir08_SubDir08", + ".", + "..", + "RootFile09", + "RootFile10", + "RootDir11", + ".", + "..", + "RootDir11_File01", + "RootDir11_File02", + "RootDir11_SubDir03", + ".", + "..", + "RootDir11_SubDir04", + ".", + "..", + "RootDir11_File05", + "RootDir11_File06", + "RootDir11_SubDir07", + ".", + "..", + "RootDir11_SubDir08", + ".", + "..", + "RootDir12", + ".", + "..", + "RootDir12_File01", + "RootDir12_File02", + "RootDir12_SubDir03", + ".", + "..", + "RootDir12_SubDir04", + ".", + "..", + "RootDir12_File05", + "RootDir12_File06", + "RootDir12_SubDir07", + ".", + "..", + "RootDir12_SubDir08", + ".", + "..", + "RootDir13", + ".", + "..", + "RootDir13_File01", + "RootDir13_File02", + "RootDir13_SubDir03", + ".", + "..", + "RootDir13_SubDir04", + ".", + "..", + "RootDir13_File05", + "RootDir13_File06", + "RootDir13_SubDir07", + ".", + "..", + "RootDir13_SubDir08", + ".", + "..", + "RootDir14", + ".", + "..", + "RootFile15", + "END"}; + + + +/* Define thread prototypes. */ + +void filex_directory_local_path_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); +static void traverse_directory(CHAR *directory_name); +static void last_character_compare_test(); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_directory_local_path_application_define(void *first_unused_memory) +#endif +{ + +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +memset(ram_disk_memory, 0xbf, 512*128); + + /* Initialize the FileX system. */ + fx_system_initialize(); +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +FX_LOCAL_PATH local_path; +CHAR *path_name; +CHAR path_name_buffer[32]; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Directory local path test.............................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Attempt to clear the local path before the media has been opened to generate an error */ + status = fx_directory_local_path_clear(&ram_disk); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(2); + } + + /* Attempt to restore the local path before the media has been opened to generate an error */ + status = fx_directory_local_path_restore(&ram_disk, &local_path); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(3); + } + + /* Attempt to set the local path before the media has been opened to generate an error */ + status = fx_directory_local_path_set(&ram_disk, &local_path, "/A0"); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(4); + } + + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(5); + } + + /* Attempt to set the local path to somewhere that does not exist to generate an error */ + status = fx_directory_local_path_set(&ram_disk, &local_path, "/DOES_NOT_EXIST"); + if (status != FX_INVALID_PATH) + { + printf("ERROR!\n"); + test_control_return(6); + } + + /* Create a hiearchy of files and sub-directories. */ + status = fx_file_create(&ram_disk, "RootFile01"); + status += fx_file_create(&ram_disk, "RootFile02"); + status += fx_directory_create(&ram_disk, "RootDir03"); + status += fx_directory_create(&ram_disk, "RootDir04"); + status += fx_directory_create(&ram_disk, "RootDir05"); + status += fx_directory_create(&ram_disk, "RootDir06"); + status += fx_directory_create(&ram_disk, "RootDir07"); + status += fx_directory_create(&ram_disk, "RootDir08"); + status += fx_file_create(&ram_disk, "RootFile09"); + status += fx_file_create(&ram_disk, "RootFile10"); + status += fx_directory_create(&ram_disk, "RootDir11"); + status += fx_directory_create(&ram_disk, "RootDir12"); + status += fx_directory_create(&ram_disk, "RootDir13"); + status += fx_directory_create(&ram_disk, "RootDir14"); + status += fx_file_create(&ram_disk, "RootFile15"); + + /* Create the sub-directories for RootDir03. */ + status += fx_directory_create(&ram_disk, "/RootDir03/RootDir03_SubDir01"); + status += fx_file_create(&ram_disk, "/RootDir03/RootDir03_File02"); + status += fx_directory_create(&ram_disk, "/RootDir03/RootDir03_SubDir03"); + status += fx_file_create(&ram_disk, "/RootDir03/RootDir03_File04"); + status += fx_directory_create(&ram_disk, "/RootDir03/RootDir03_SubDir05"); + status += fx_file_create(&ram_disk, "/RootDir03/RootDir03_File06"); + status += fx_directory_create(&ram_disk, "/RootDir03/RootDir03_SubDir07"); + status += fx_file_create(&ram_disk, "/RootDir03/RootDir03_File08"); + + /* Create the sub-directories for RootDir04. */ + status += fx_file_create(&ram_disk, "/RootDir04/RootDir04_File01"); + status += fx_file_create(&ram_disk, "/RootDir04/RootDir04_File02"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir03"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir05"); + status += fx_file_create(&ram_disk, "/RootDir04/RootDir04_File06"); + status += fx_file_create(&ram_disk, "/RootDir04/RootDir04_File07"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir08"); + + /* Create the sub-directories for RootDir05. */ + status += fx_file_create(&ram_disk, "/RootDir05/RootDir05_File02"); + status += fx_file_create(&ram_disk, "/RootDir05/RootDir05_File03"); + status += fx_file_create(&ram_disk, "/RootDir05/RootDir05_File04"); + status += fx_directory_create(&ram_disk, "/RootDir05/RootDir05_SubDir05"); + status += fx_directory_create(&ram_disk, "/RootDir05/RootDir05_SubDir06"); + status += fx_directory_create(&ram_disk, "/RootDir05/RootDir05_SubDir07"); + status += fx_file_create(&ram_disk, "/RootDir05/RootDir05_File08"); + status += fx_directory_create(&ram_disk, "/RootDir05/RootDir05_SubDir09"); + + + /* Create the sub-directories for RootDir06. */ + status += fx_directory_create(&ram_disk, "/RootDir06/RootDir06_SubDir01"); + status += fx_directory_create(&ram_disk, "/RootDir06/RootDir06_SubDir02"); + status += fx_directory_create(&ram_disk, "/RootDir06/RootDir06_SubDir03"); + status += fx_file_create(&ram_disk, "/RootDir06/RootDir06_File04"); + status += fx_file_create(&ram_disk, "/RootDir06/RootDir06_File05"); + status += fx_file_create(&ram_disk, "/RootDir06/RootDir06_File06"); + status += fx_file_create(&ram_disk, "/RootDir06/RootDir06_File07"); + status += fx_directory_create(&ram_disk, "/RootDir06/RootDir06_SubDir08"); + + + /* Create the sub-directories for RootDir07. */ + status += fx_directory_create(&ram_disk, "/RootDir07/RootDir07_SubDir01"); + status += fx_directory_create(&ram_disk, "/RootDir07/RootDir07_SubDir02"); + status += fx_directory_create(&ram_disk, "/RootDir07/RootDir07_SubDir03"); + status += fx_directory_create(&ram_disk, "/RootDir07/RootDir07_SubDir04"); + status += fx_file_create(&ram_disk, "/RootDir07/RootDir07_File05"); + status += fx_file_create(&ram_disk, "/RootDir07/RootDir07_File06"); + status += fx_file_create(&ram_disk, "/RootDir07/RootDir07_File07"); + status += fx_file_create(&ram_disk, "/RootDir07/RootDir07_File08"); + + /* Create the sub-directories for RootDir08. */ + status += fx_file_create(&ram_disk, "/RootDir08/RootDir08_File01"); + status += fx_file_create(&ram_disk, "/RootDir08/RootDir08_File02"); + status += fx_file_create(&ram_disk, "/RootDir08/RootDir08_File03"); + status += fx_file_create(&ram_disk, "/RootDir08/RootDir08_File04"); + status += fx_directory_create(&ram_disk, "/RootDir08/RootDir08_SubDir05"); + status += fx_directory_create(&ram_disk, "/RootDir08/RootDir08_SubDir06"); + status += fx_directory_create(&ram_disk, "/RootDir08/RootDir08_SubDir07"); + status += fx_directory_create(&ram_disk, "/RootDir08/RootDir08_SubDir08"); + + /* Create the sub-directories for RootDir11. */ + status += fx_file_create(&ram_disk, "/RootDir11/RootDir11_File01"); + status += fx_file_create(&ram_disk, "/RootDir11/RootDir11_File02"); + status += fx_directory_create(&ram_disk, "/RootDir11/RootDir11_SubDir03"); + status += fx_directory_create(&ram_disk, "/RootDir11/RootDir11_SubDir04"); + status += fx_file_create(&ram_disk, "/RootDir11/RootDir11_File05"); + status += fx_file_create(&ram_disk, "/RootDir11/RootDir11_File06"); + status += fx_directory_create(&ram_disk, "/RootDir11/RootDir11_SubDir07"); + status += fx_directory_create(&ram_disk, "/RootDir11/RootDir11_SubDir08"); + + + /* Create the sub-directories for RootDir12. */ + status += fx_file_create(&ram_disk, "/RootDir12/RootDir12_File01"); + status += fx_file_create(&ram_disk, "/RootDir12/RootDir12_File02"); + status += fx_directory_create(&ram_disk, "/RootDir12/RootDir12_SubDir03"); + status += fx_directory_create(&ram_disk, "/RootDir12/RootDir12_SubDir04"); + status += fx_file_create(&ram_disk, "/RootDir12/RootDir12_File05"); + status += fx_file_create(&ram_disk, "/RootDir12/RootDir12_File06"); + status += fx_directory_create(&ram_disk, "/RootDir12/RootDir12_SubDir07"); + status += fx_directory_create(&ram_disk, "/RootDir12/RootDir12_SubDir08"); + + /* Create the sub-directories for RootDir13. */ + status += fx_file_create(&ram_disk, "/RootDir13/RootDir13_File01"); + status += fx_file_create(&ram_disk, "/RootDir13/RootDir13_File02"); + status += fx_directory_create(&ram_disk, "/RootDir13/RootDir13_SubDir03"); + status += fx_directory_create(&ram_disk, "/RootDir13/RootDir13_SubDir04"); + status += fx_file_create(&ram_disk, "/RootDir13/RootDir13_File05"); + status += fx_file_create(&ram_disk, "/RootDir13/RootDir13_File06"); + status += fx_directory_create(&ram_disk, "/RootDir13/RootDir13_SubDir07"); + status += fx_directory_create(&ram_disk, "/RootDir13/RootDir13_SubDir08"); + + /* Create the Sub-sub directories under RootDir04. */ + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir03/RootDir04_SubDir03_SubSubDir01"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir03/RootDir04_SubDir03_SubSubDir02"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir03/RootDir04_SubDir03_SubSubDir03"); + status += fx_file_create(&ram_disk, "/RootDir04/RootDir04_SubDir03/RootDir04_SubDir03_SubSubFile04"); + + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir01"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir02"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir03"); + status += fx_file_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubFile04"); + + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir05/RootDir04_SubDir05_SubSubDir01"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir05/RootDir04_SubDir05_SubSubDir02"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir05/RootDir04_SubDir05_SubSubDir03"); + status += fx_file_create(&ram_disk, "/RootDir04/RootDir04_SubDir05/RootDir04_SubDir05_SubSubFile04"); + + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir08/RootDir04_SubDir08_SubSubDir01"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir08/RootDir04_SubDir08_SubSubDir02"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir08/RootDir04_SubDir08_SubSubDir03"); + status += fx_file_create(&ram_disk, "/RootDir04/RootDir04_SubDir05/RootDir04_SubDir08_SubSubFile04"); + + /* Create the sub-sub-sub directories under RootDir04. */ + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir01/RootDir04_SubDir04_SubSubDir01_SubSubSubDir01"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir01/RootDir04_SubDir04_SubSubDir01_SubSubSubDir02"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir01/RootDir04_SubDir04_SubSubDir01_SubSubSubDir03"); + status += fx_file_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir01/RootDir04_SubDir04_SubSubDir01_SubSubSubFile04"); + + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir02/RootDir04_SubDir04_SubSubDir01_SubSubSubDir01"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir02/RootDir04_SubDir04_SubSubDir01_SubSubSubDir02"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir02/RootDir04_SubDir04_SubSubDir01_SubSubSubDir03"); + status += fx_file_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir02/RootDir04_SubDir04_SubSubDir01_SubSubSubFile04"); + + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir03/RootDir04_SubDir04_SubSubDir01_SubSubSubDir01"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir03/RootDir04_SubDir04_SubSubDir01_SubSubSubDir02"); + status += fx_directory_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir03/RootDir04_SubDir04_SubSubDir01_SubSubSubDir03"); + status += fx_file_create(&ram_disk, "/RootDir04/RootDir04_SubDir04/RootDir04_SubDir04_SubSubDir03/RootDir04_SubDir04_SubSubDir01_SubSubSubFile04"); + + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error creating directory structure. Return to caller. */ + printf("ERROR!\n"); + test_control_return(7); + } + + /* Traverse the directory structure recursively. */ + traverse_directory(FX_NULL); + + /* Determine if the traversal matched what was expected. */ + if (error_counter) + { + + /* Nope, error traversing the directory structure! */ + printf("ERROR!\n"); + test_control_return(8); + } + + status = fx_directory_local_path_clear(&ram_disk); + status += fx_directory_local_path_set(&ram_disk, &local_path, "RootDir04"); + status += fx_directory_local_path_set(&ram_disk, &local_path, "../RootDir03"); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(9); + } + + fx_directory_local_path_get_copy(&ram_disk, path_name_buffer, sizeof(path_name_buffer)); + if (strcmp(path_name_buffer, "/RootDir03")) + { + printf("ERROR!\n"); + test_control_return(233); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail(status == FX_SUCCESS); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail(status == FX_SUCCESS); + + /* Try getting directory local path before media open. */ + status = fx_directory_local_path_get(&ram_disk, &path_name); + return_if_fail(status == FX_MEDIA_NOT_OPEN); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail(status == FX_SUCCESS); + + /* Get local path. */ + status = fx_directory_local_path_get(&ram_disk, &path_name); + return_if_fail(status == FX_SUCCESS); + + status = fx_directory_local_path_clear(&ram_disk); + return_if_fail(status == FX_SUCCESS); + + /* Get local path after clearing. */ + status = fx_directory_local_path_get(&ram_disk, &path_name); + return_if_fail(status == FX_SUCCESS); + + /* Test corner case where last character of fx_media_last_found_name is different from current path. */ + last_character_compare_test(); + + printf("SUCCESS!\n"); + test_control_return(0); +} + + +/* Define the directory traversal routine for the local path test. */ +void traverse_directory(CHAR *directory_name) +{ + +FX_LOCAL_PATH local_path; +FX_FILE file; +UINT status; +CHAR name[300]; +UINT skip; + + + /* Determine if we are at the start. */ + if (directory_name == FX_NULL) + { + status = fx_directory_local_path_set(&ram_disk, &local_path, "\\"); + skip = 0; + } + else + { + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name); + skip = 2; + } + + /* Get the first directory entry in the root path. */ + status = fx_directory_first_entry_find(&ram_disk, name); + + /* Loop through the directory entries for this path. */ + while (status == FX_SUCCESS) + { + + /* Compare with what is expected. */ + if (strcmp(name, expected_name[expected_index++])) + error_counter++; + + /* Determine if this name is a directory or a file. */ + status = fx_directory_name_test(&ram_disk, name); + + /* What is it? */ + if (status == FX_NOT_DIRECTORY) + { + + /* This is a file, open it to test its integrity. */ + status = fx_file_open(&ram_disk, &file, name, FX_OPEN_FOR_READ); + if (status != FX_SUCCESS) + error_counter++; + status = fx_file_close(&file); + if (status != FX_SUCCESS) + error_counter++; + + /* Pickup the next directory entry. */ + status = fx_directory_next_entry_find(&ram_disk, name); + } + else if (status == FX_SUCCESS) + { + + /* Skip the first two entries... */ + if (skip) + skip--; + else + { + /* Recursive call to traverse directory. */ + traverse_directory(name); + + /* Restore path. */ + status = fx_directory_local_path_restore(&ram_disk, &local_path); + } + + status = fx_directory_next_entry_find(&ram_disk, name); + } + } + + fx_directory_local_path_clear(&ram_disk); + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + /* send null pointer to generate an error */ + status = fx_directory_local_path_clear(FX_NULL); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(11); + } + + /* send null pointer to generate an error */ + status = fx_directory_local_path_restore(FX_NULL, FX_NULL); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(12); + } + + /* send null pointer to generate an error */ + status = fx_directory_local_path_set(FX_NULL, FX_NULL, "\\"); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(13); + } + + /* send null pointer to generate an error */ + status = fx_directory_name_test(FX_NULL, name); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(14); + } +#endif /* FX_DISABLE_ERROR_CHECKING */ +} + + +/* Test corner case where last character of fx_media_last_found_name is different from current path. */ +static void last_character_compare_test() +{ +FX_FILE my_file; +FX_LOCAL_PATH root_path, test1_path, test2_path; + + return_if_fail(fx_directory_local_path_set(&ram_disk, &root_path, "/") == FX_SUCCESS); + return_if_fail(fx_directory_create(&ram_disk, "test_1") == FX_SUCCESS); + return_if_fail(fx_directory_create(&ram_disk, "test_2") == FX_SUCCESS); + return_if_fail(fx_directory_local_path_set(&ram_disk, &test1_path, "test_1") == FX_SUCCESS); + return_if_fail(fx_directory_local_path_set(&ram_disk, &root_path, "/") == FX_SUCCESS); + return_if_fail(fx_directory_local_path_set(&ram_disk, &test2_path, "test_2") == FX_SUCCESS); + + return_if_fail(fx_directory_local_path_restore(&ram_disk, &test1_path) == FX_SUCCESS); + return_if_fail(fx_file_create(&ram_disk, "file_0001.txt") == FX_SUCCESS); + return_if_fail(fx_file_open(&ram_disk, &my_file, "/test_1/file_0001.txt", FX_OPEN_FOR_READ) == FX_SUCCESS); + return_if_fail(fx_file_close(&my_file) == FX_SUCCESS); + return_if_fail(fx_directory_local_path_restore(&ram_disk, &test2_path) == FX_SUCCESS); + return_if_fail(fx_file_create(&ram_disk, "file_0001.txt") == FX_SUCCESS); + return_if_fail(fx_file_open(&ram_disk, &my_file, "/test_2/file_0001.txt", FX_OPEN_FOR_READ) == FX_SUCCESS); + return_if_fail(fx_file_close(&my_file) == FX_SUCCESS); +} + +#else + +#include "fx_api.h" +#include "fx_ram_driver_test.h" +#include + + +void test_control_return(UINT status); + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_directory_local_path_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Directory local path test..............................N/A\n"); + + test_control_return(255); +} + +#endif diff --git a/test/regression_test/filex_directory_long_short_get_test.c b/test/regression_test/filex_directory_long_short_get_test.c new file mode 100644 index 0000000..bc84a28 --- /dev/null +++ b/test/regression_test/filex_directory_long_short_get_test.c @@ -0,0 +1,382 @@ +/* This FileX test concentrates on the directory long/short name get operations. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include +#include "fx_ram_driver_test.h" + +#define DEMO_STACK_SIZE 8192 +#define CACHE_SIZE 16*128 + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif +static CHAR name[50]; +static CHAR max_name[FX_MAX_LONG_NAME_LEN+1]; +static CHAR return_name[FX_MAX_LONG_NAME_LEN+1]; + + +/* Define thread prototypes. */ + +void filex_directory_long_short_get_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_directory_long_short_get_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE + UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Directory long/short name get test....................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 128, // Directory Entries + 0, // Hidden sectors + 2048, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Attempt to get a directory that does not exist */ + status = fx_directory_short_name_get(&ram_disk, "does_not_exist", "does_not_exist"); + if (status == FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(3); + } + + /* Simple 8.3 rename in the root directory. */ + status = fx_directory_create(&ram_disk, "subdir"); + status += fx_file_create(&ram_disk, "name"); + status += fx_file_create(&ram_disk, "longnametest"); + status += fx_file_create(&ram_disk, "subdir/SNAME.TXT"); + status += fx_file_create(&ram_disk, "subdir/longnametest"); + + /* Check the directory/file create status. */ + if (status != FX_SUCCESS) + { + + /* Error creating files/directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(4); + } + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + /* send null pointer to generate an error */ + status = fx_directory_short_name_get(FX_NULL, "subdir", name); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(5); + } + /* send null pointer to generate an error */ + status = fx_directory_short_name_get_extended(FX_NULL, "subdir", name, 1); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(5); + } +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* attempt to get the long name of something that doesnt exist */ + status = fx_directory_long_name_get(&ram_disk, "does_not_exist", "does_not_exist"); + if (status == FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(6); + } + + /* Get the short name of a long name. */ + status = fx_directory_short_name_get(&ram_disk, "subdir", name); + status += fx_directory_long_name_get(&ram_disk, name, name); + + /* Check for errors. */ + if ((status != FX_SUCCESS) || (strcmp(name, "subdir"))) + { + + /* Error getting long/short name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(7); + } + + /* Set name to known state. */ + memcpy(name, "ORIGINALSTRING", 15); + + /* Test extended short name get with short buffer length. */ + status = fx_directory_short_name_get_extended(&ram_disk, "subdir", name, 2); + + /* Check for errors. */ + if ((status != FX_BUFFER_ERROR) || (memcmp(name, "S\0IGINALSTRING\0", 15))) + { + + /* Error getting short name extended. Return to caller. */ + printf("ERROR!\n"); + test_control_return(7); + } + + /* Get the long name from short name. */ + status = fx_directory_long_name_get(&ram_disk, "NAME", name); + status += fx_directory_short_name_get(&ram_disk, name, name); + + /* Check for errors. */ + if ((status != FX_SUCCESS) || (strcmp(name, "NAME"))) + { + + /* Error getting long/short name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(8); + } + + /* Get the short name of a long name. */ + status = fx_directory_short_name_get(&ram_disk, "longnametest", name); + status += fx_directory_long_name_get(&ram_disk, name, name); + + /* Check for errors. */ + if ((status != FX_SUCCESS) || (strcmp(name, "longnametest"))) + { + + /* Error getting long/short name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(9); + } + + /* Move to sub-directory. */ + status += fx_directory_default_set(&ram_disk, "subdir"); + + /* Get the long name from short name. */ + status += fx_directory_long_name_get(&ram_disk, "SNAME.TXT", name); + status += fx_directory_short_name_get(&ram_disk, name, name); + + /* Check for errors. */ + if ((status != FX_SUCCESS) || (strcmp(name, "SNAME.TXT"))) + { + + /* Error getting long/short name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(10); + } + + /* Set name to known state. */ + memcpy(name, "ORIGINALSTRING", 15); + + /* Get the long name from short name using extended version. */ + status = fx_directory_long_name_get_extended(&ram_disk, "SNAME.TXT", name, 2); + + /* Check for error. */ + if ((status != FX_BUFFER_ERROR) || (memcmp(name, "S\0IGINALSTRING\0", 15))) + { + + /* Error getting long/short name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(10); + } + + /* Set name to known state. */ + memcpy(name, "ORIGINALSTRING", 15); + status = fx_directory_short_name_get_extended(&ram_disk, "SNAME.TXT", name, 2); + + /* Check for error. */ + if ((status != FX_BUFFER_ERROR) || (memcmp(name, "S\0IGINALSTRING\0", 15))) + { + + /* Error getting long/short name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(10); + } + + /* Get the short name from long name. */ + status = fx_directory_short_name_get(&ram_disk, "longnametest", name); + status += fx_directory_long_name_get(&ram_disk, name, name); + + /* Check for errors. */ + if ((status != FX_SUCCESS) || (strcmp(name, "longnametest"))) + { + + /* Error getting long/short name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(11); + } + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + /* send null pointer to generate an error */ + status = fx_directory_long_name_get(FX_NULL, "NAME", name); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(12); + } + /* send null pointer to generate an error */ + status = fx_directory_long_name_get_extended(FX_NULL, "NAME", name, 1); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(12); + } +#endif /* FX_DISABLE_ERROR_CHECKING */ + + + /* Create a maximum size short file name. */ + status = fx_file_create(&ram_disk, "MAX_SIZE.TXT"); + + /* Get the short name. */ + status += fx_directory_short_name_get(&ram_disk, "MAX_SIZE.TXT", name); + + /* Check for errors. */ + if ((status != FX_SUCCESS) || (strcmp(name, "MAX_SIZE.TXT"))) + { + + /* Error getting long/short name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(13); + } + + /* Build a maximum sized long file name. */ + for (i = 0; i < (FX_MAX_LONG_NAME_LEN-1); i++) + { + + /* Set a character in the file name. */ + max_name[i] = 'a'; + } + max_name[i] = 0; + max_name[i-4] = '.'; + + /* Create a maximum size long file name. */ + status = fx_file_create(&ram_disk, max_name); + + /* Get the short name. */ + status += fx_directory_short_name_get(&ram_disk, max_name, return_name); + + /* Check for errors. */ + if ((status != FX_SUCCESS) || (strcmp(return_name, "AAA~001A.AAA"))) + { + + /* Error getting long/short name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(14); + } + + /* Get the long name. */ + status += fx_directory_long_name_get(&ram_disk, return_name, return_name); + + /* Check for errors. */ + if ((status != FX_SUCCESS) || (strcmp(return_name, max_name))) + { + + /* Error getting long/short name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(15); + } + + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + else + { + + printf("SUCCESS!\n"); + test_control_return(0); + } +} diff --git a/test/regression_test/filex_directory_naming_test.c b/test/regression_test/filex_directory_naming_test.c new file mode 100644 index 0000000..7be04d7 --- /dev/null +++ b/test/regression_test/filex_directory_naming_test.c @@ -0,0 +1,633 @@ +/* This FileX test concentrates on the basic directory naming operations. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_directory.h" +#include "fx_utility.h" +#include +#include "fx_ram_driver_test.h" + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 16*128 + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static CHAR max_name[FX_MAX_LONG_NAME_LEN+2]; +static CHAR return_name[FX_MAX_LONG_NAME_LEN+1]; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + + +/* Define thread prototypes. */ + +void filex_directory_naming_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_directory_naming_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +CHAR *path_ptr; +CHAR special_name[10]; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Directory naming test.................................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Try to rename a directory before the media is opened to generate an error */ + status = fx_directory_rename(&ram_disk, "/A0", "/A1"); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(2); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(3); + } + + /* Get the current path. This should be / or NULL. */ + status = fx_directory_default_get(&ram_disk, &path_ptr); + + /* Check the status. */ + if ((status != FX_SUCCESS) || (path_ptr[0])) + { + + /* Error getting the path. Return to caller. */ + printf("ERROR!\n"); + test_control_return(4); + } + + /* Test a directory name that does not exist to generate an error */ +/* status = fx_directory_name_test(&ram_disk, "/A0"); + if (status == FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(5); + } +*/ + + /* test some directory names to test the directory search function */ +/* status = fx_directory_create(&ram_disk, "\\TEST"); + status += fx_directory_name_test(&ram_disk, "\\TEST"); + status += fx_directory_delete(&ram_disk, "\\TEST"); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(6); + } +*/ + + + /* Create a series of directories... */ + status = fx_directory_create(&ram_disk, "/A0"); + status += fx_directory_create(&ram_disk, "/b0"); + status += fx_directory_create(&ram_disk, "/C0"); + status += fx_directory_create(&ram_disk, "/d0"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(7); + } + + /* Attempt to create the same directory again. */ + status = fx_directory_create(&ram_disk, "/a0"); + + /* Check for errors... */ + if (status != FX_ALREADY_CREATED) + { + + /* Error creating same directory twice. Return to caller. */ + printf("ERROR!\n"); + test_control_return(8); + } + + /* Create sub-directories with the E5 character. */ + special_name[0] = (CHAR)0xE5; + special_name[1] = 0x2E; + special_name[2] = 0x54; + special_name[3] = 0x58; + special_name[4] = 0x54; + special_name[5] = 0; + status = fx_directory_create(&ram_disk, special_name); + status += fx_media_flush(&ram_disk); + status += fx_directory_delete(&ram_disk, special_name); + + /* Check for errors. */ + if (status != FX_SUCCESS) + { + + /* Error creating special name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create another special file name. */ + special_name[0] = (CHAR)0xE5; + special_name[1] = 0x31; + special_name[2] = 0x2E; + special_name[3] = 0x54; + special_name[4] = 0x58; + special_name[5] = 0x54; + special_name[6] = 0; + status = fx_directory_create(&ram_disk, special_name); + status += fx_media_flush(&ram_disk); + status += fx_directory_delete(&ram_disk, special_name); + + /* Check for errors. */ + if (status != FX_SUCCESS) + { + + /* Error creating special name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(10); + } + + /* Create the next level of sub-directories.... with the interesting names... */ + status = fx_directory_default_set(&ram_disk, "/A0"); + + status = fx_directory_create(&ram_disk, "a"); + status += fx_directory_create(&ram_disk, "this...ismytestdir"); + status += fx_directory_create(&ram_disk, "this ... is my test dir"); + status += fx_directory_create(&ram_disk, "ThisIsTheFirstVeryLongDirNameForThisTestABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZfirstfile111111111111111100000000000000000000000000000000000000000000000000000000"); + status += fx_directory_create(&ram_disk, " c"); + status += fx_directory_create(&ram_disk, " c/ e"); + status += fx_directory_create(&ram_disk, " c/ e/ f"); + + /* Check for errors. */ + if (status != FX_SUCCESS) + { + + /* Error creating interesting directory names. Return to caller. */ + printf("ERROR!\n"); + test_control_return(11); + } + + /* Now attempt to create a file in each of these sub-directories. */ + status = fx_file_create(&ram_disk, "/A0/a/test.txt"); + status += fx_file_create(&ram_disk, "/A0/this...ismytestdir/test.txt"); + status += fx_file_create(&ram_disk, "/A0/this ... is my test dir/test.txt"); + status += fx_file_create(&ram_disk, "/A0/ThisIsTheFirstVeryLongDirNameForThisTestABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZfirstfile111111111111111100000000000000000000000000000000000000000000000000000000/test.txt"); + fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "/A0 / c/ e/ f/test.txt"); + fx_media_flush(&ram_disk); + + /* Check for errors. */ + if (status != FX_SUCCESS) + { + + /* Error creating test files in the interesting sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(12); + } + + /* Now attempt to delete the file in each of these sub-directories. */ + status = fx_file_delete(&ram_disk, "/A0/a/test.txt"); + status += fx_file_delete(&ram_disk, "/A0/this...ismytestdir/test.txt"); + status += fx_file_delete(&ram_disk, "/A0/this ... is my test dir/test.txt"); + status += fx_file_delete(&ram_disk, "/A0/ThisIsTheFirstVeryLongDirNameForThisTestABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZfirstfile111111111111111100000000000000000000000000000000000000000000000000000000/test.txt"); + status += fx_file_delete(&ram_disk, "/A0 / c/ e/ f/test.txt"); + + /* Check for errors. */ + if (status != FX_SUCCESS) + { + + /* Error deleting test files in the interesting sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(13); + } + + /* Now attempt to delete the interesting sub-directories. */ + status = fx_directory_delete(&ram_disk, "/A0/a"); + status += fx_directory_delete(&ram_disk, "/A0/this...ismytestdir"); + status += fx_directory_delete(&ram_disk, "/A0/this ... is my test dir"); + status += fx_directory_delete(&ram_disk, "/A0/ThisIsTheFirstVeryLongDirNameForThisTestABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZfirstfile111111111111111100000000000000000000000000000000000000000000000000000000"); + status += fx_directory_delete(&ram_disk, "/A0/c/e/f"); + status += fx_directory_delete(&ram_disk, "/A0/c/e"); + status += fx_directory_delete(&ram_disk, "/A0/c"); + + /* Check for errors. */ + if (status != FX_SUCCESS) + { + + /* Error deleting the interesting sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(14); + } + + /* Delete a series of directories... Use short name for b0 and d0! */ + status = fx_directory_delete(&ram_disk, "/A0"); + status += fx_directory_delete(&ram_disk, "/B0"); + status += fx_directory_delete(&ram_disk, "/C0"); + status += fx_directory_delete(&ram_disk, "/D0"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error deleting directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(15); + } + + /* Set default to root directory. */ + status = fx_directory_default_set(&ram_disk, "/"); + + /* Flush the media... should be empty at this point. */ + status += fx_media_flush(&ram_disk); + + /* Now fill the media's root directory until we get an error... We will use 8.3 names + to attempt to create 32 names. */ + status += fx_directory_create(&ram_disk, "A01"); + status += fx_directory_create(&ram_disk, "A02"); + status += fx_directory_create(&ram_disk, "A03"); + status += fx_directory_create(&ram_disk, "A04"); + status += fx_directory_create(&ram_disk, "A05"); + status += fx_directory_create(&ram_disk, "A06"); + status += fx_directory_create(&ram_disk, "A07"); + status += fx_directory_create(&ram_disk, "A08"); + status += fx_directory_create(&ram_disk, "A09"); + status += fx_directory_create(&ram_disk, "A10"); + status += fx_directory_create(&ram_disk, "A11"); + status += fx_directory_create(&ram_disk, "A12"); + status += fx_directory_create(&ram_disk, "A13"); + status += fx_directory_create(&ram_disk, "A14"); + status += fx_directory_create(&ram_disk, "A15"); + status += fx_directory_create(&ram_disk, "A16"); + status += fx_directory_create(&ram_disk, "A17"); + status += fx_directory_create(&ram_disk, "A18"); + status += fx_directory_create(&ram_disk, "A19"); + status += fx_directory_create(&ram_disk, "A20"); + status += fx_directory_create(&ram_disk, "A21"); + status += fx_directory_create(&ram_disk, "A22"); + status += fx_directory_create(&ram_disk, "A23"); + status += fx_directory_create(&ram_disk, "A24"); + status += fx_directory_create(&ram_disk, "A25"); + status += fx_directory_create(&ram_disk, "A26"); + status += fx_directory_create(&ram_disk, "A27"); + status += fx_directory_create(&ram_disk, "A28"); + status += fx_directory_create(&ram_disk, "A29"); + status += fx_directory_create(&ram_disk, "A30"); + status += fx_directory_create(&ram_disk, "A31"); + status += fx_directory_create(&ram_disk, "A32"); + +fx_media_flush(&ram_disk); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(16); + } + + /* Attempt to create 33rd name... this should fail. */ + status = fx_directory_create(&ram_disk, "A33"); + + /* Check for errors... */ + if (status != FX_NO_MORE_SPACE) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(17); + } + + /* Flush the media. */ + status = fx_media_flush(&ram_disk); + + /* Now delete all the names. */ + status += fx_directory_delete(&ram_disk, "A01"); + status += fx_directory_delete(&ram_disk, "A02"); + status += fx_directory_delete(&ram_disk, "A03"); + status += fx_directory_delete(&ram_disk, "A04"); + status += fx_directory_delete(&ram_disk, "A05"); + status += fx_directory_delete(&ram_disk, "A06"); + status += fx_directory_delete(&ram_disk, "A07"); + status += fx_directory_delete(&ram_disk, "A08"); + status += fx_directory_delete(&ram_disk, "A09"); + status += fx_directory_delete(&ram_disk, "A10"); + status += fx_directory_delete(&ram_disk, "A11"); + status += fx_directory_delete(&ram_disk, "A12"); + status += fx_directory_delete(&ram_disk, "A13"); + status += fx_directory_delete(&ram_disk, "A14"); + status += fx_directory_delete(&ram_disk, "A15"); + status += fx_directory_delete(&ram_disk, "A16"); + status += fx_directory_delete(&ram_disk, "A17"); + status += fx_directory_delete(&ram_disk, "A18"); + status += fx_directory_delete(&ram_disk, "A19"); + status += fx_directory_delete(&ram_disk, "A20"); + status += fx_directory_delete(&ram_disk, "A21"); + status += fx_directory_delete(&ram_disk, "A22"); + status += fx_directory_delete(&ram_disk, "A23"); + status += fx_directory_delete(&ram_disk, "A24"); + status += fx_directory_delete(&ram_disk, "A25"); + status += fx_directory_delete(&ram_disk, "A26"); + status += fx_directory_delete(&ram_disk, "A27"); + status += fx_directory_delete(&ram_disk, "A28"); + status += fx_directory_delete(&ram_disk, "A29"); + status += fx_directory_delete(&ram_disk, "A30"); + status += fx_directory_delete(&ram_disk, "A31"); + status += fx_directory_delete(&ram_disk, "A32"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error deleting directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(18); + } + + /* Flush the media. */ + status = fx_media_flush(&ram_disk); + + /* Now do the same thing, except with 2 entry long names. */ + status += fx_directory_create(&ram_disk, "b01"); + status += fx_directory_create(&ram_disk, "b02"); + status += fx_directory_create(&ram_disk, "b03"); + status += fx_directory_create(&ram_disk, "b04"); + status += fx_directory_create(&ram_disk, "b05"); + status += fx_directory_create(&ram_disk, "b06"); + status += fx_directory_create(&ram_disk, "b07"); + status += fx_directory_create(&ram_disk, "b08"); + status += fx_directory_create(&ram_disk, "b09"); + status += fx_directory_create(&ram_disk, "b10"); + status += fx_directory_create(&ram_disk, "b11"); + status += fx_directory_create(&ram_disk, "b12"); + status += fx_directory_create(&ram_disk, "b13"); + status += fx_directory_create(&ram_disk, "b14"); + status += fx_directory_create(&ram_disk, "b15"); + status += fx_directory_create(&ram_disk, "b16"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(19); + } + + /* Attempt to create 17th long name... this should fail. */ + status = fx_directory_create(&ram_disk, "b17"); + + /* Check for errors... */ + if (status != FX_NO_MORE_SPACE) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(20); + } + + /* Flush the media. */ + status = fx_media_flush(&ram_disk); + + /* Now delete all the names. */ + status += fx_directory_delete(&ram_disk, "b01"); + status += fx_directory_delete(&ram_disk, "b02"); + status += fx_directory_delete(&ram_disk, "b03"); + status += fx_directory_delete(&ram_disk, "b04"); + status += fx_directory_delete(&ram_disk, "b05"); + status += fx_directory_delete(&ram_disk, "b06"); + status += fx_directory_delete(&ram_disk, "b07"); + status += fx_directory_delete(&ram_disk, "b08"); + status += fx_directory_delete(&ram_disk, "b09"); + status += fx_directory_delete(&ram_disk, "b10"); + status += fx_directory_delete(&ram_disk, "b11"); + status += fx_directory_delete(&ram_disk, "b12"); + status += fx_directory_delete(&ram_disk, "b13"); + status += fx_directory_delete(&ram_disk, "b14"); + status += fx_directory_delete(&ram_disk, "b15"); + status += fx_directory_delete(&ram_disk, "b16"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error deleting directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(21); + } + +#if 0 + /* test the directory free search function for proper error handing with invalid names */ + FX_DIR_ENTRY dir_entry; + dir_entry.fx_dir_entry_name = (char*) malloc(0x100 * sizeof(char)); + dir_entry.fx_dir_entry_name[0] = '.'; + dir_entry.fx_dir_entry_name[1] = '.'; + dir_entry.fx_dir_entry_name[2] = (char)0; + status = _fx_directory_free_search(&ram_disk, &dir_entry, &dir_entry); + if (status != FX_INVALID_NAME) + { + printf("ERROR!\n"); + test_control_return(22); + } + + /* test the directory free search function for proper error handling of special characters */ +/* This code is executing differently on local vs server. Disabled until cause is explored */ + dir_entry.fx_dir_entry_name[0] = (char)128; + dir_entry.fx_dir_entry_name[1] = (char)128; + dir_entry.fx_dir_entry_name[2] = (char)0; + status = _fx_directory_free_search(&ram_disk, &dir_entry, &dir_entry); + if (status == FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(23); + } + + /* test the directory free search function for proper error handling of special characters */ + dir_entry.fx_dir_entry_name[0] = '%'; + dir_entry.fx_dir_entry_name[1] = '%'; + dir_entry.fx_dir_entry_name[2] = (char)0; + status = _fx_directory_free_search(&ram_disk, &dir_entry, &dir_entry); + if (status == FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(24); + } + + /* test the directory free search function for proper error handling of special characters */ + dir_entry.fx_dir_entry_name[0] = ']'; + dir_entry.fx_dir_entry_name[1] = ']'; + dir_entry.fx_dir_entry_name[2] = (char)0; + status = _fx_directory_free_search(&ram_disk, &dir_entry, &dir_entry); + if (status == FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(25); + } +#endif + + /* Create a directory. */ + status = fx_directory_create(&ram_disk, "b16"); + + status += fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Create an I/O error for the directory search inside of fx_directory_name_test. */ + _fx_utility_logical_sector_read_error_request = 1; + status = fx_directory_name_test(&ram_disk, "b16"); + _fx_utility_logical_sector_read_error_request = 0; + + /* Check for the I/O error. */ + if (status != FX_IO_ERROR) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Test the directory name. */ + _fx_directory_name_extract("\\", return_name); + + /* Check for a good return name. */ + if (return_name[0] != 0) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Now test the maximum length of a name. */ + for (i = 0; i < FX_MAX_LONG_NAME_LEN+1; i++) + { + max_name[i] = 'a'; + } + + /* Test for maximum size... and removal of blank spaces. */ + _fx_directory_name_extract(max_name, return_name); + + /* Was the name truncated? */ + if (return_name[FX_MAX_LONG_NAME_LEN - 1] != 0) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + else + { + + printf("SUCCESS!\n"); + test_control_return(0); + } +} + diff --git a/test/regression_test/filex_directory_rename_test.c b/test/regression_test/filex_directory_rename_test.c new file mode 100644 index 0000000..f51442a --- /dev/null +++ b/test/regression_test/filex_directory_rename_test.c @@ -0,0 +1,290 @@ +/* This FileX test concentrates on the directory rename operations. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_ram_driver_test.h" +#include "fx_fault_tolerant.h" +#include "fx_utility.h" +#include + +#define DEMO_STACK_SIZE 8192 +#define CACHE_SIZE 4096 +#ifdef FX_ENABLE_FAULT_TOLERANT +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE +#else +#define FAULT_TOLERANT_SIZE 0 +#endif + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static CHAR max_name[FX_MAX_LONG_NAME_LEN + 2]; +static CHAR max_newname[FX_MAX_LONG_NAME_LEN + 1]; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif + + +/* Define thread prototypes. */ + +void filex_directory_rename_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_directory_rename_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE + UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + + UINT status; + UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Directory rename test.................................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 128, // Directory Entries + 0, // Hidden sectors + +/* We need a larger disk to test fault tolerant feature. */ +#ifdef FX_ENABLE_FAULT_TOLERANT + 4096 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster +#else + 4096, // Total sectors + 128, // Sector size + 1, // Sectors per cluster +#endif + + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Test a directory name before the media is opened to generate an error */ + status = fx_directory_name_test(&ram_disk, "/A0"); + return_if_fail( status == FX_MEDIA_NOT_OPEN); + + /* Try to rename a directory to an invalid name */ + status = fx_directory_rename(&ram_disk, "/A0", ""); + return_if_fail( status == FX_INVALID_NAME); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Enable fault tolerant if FX_ENABLE_FAULT_TOLERANT is defined. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); +#endif + + /* Attempt to rename a directory while the media is write protected */ + ram_disk.fx_media_driver_write_protect = FX_TRUE; + status = fx_directory_rename(&ram_disk, "/A0", "/A1"); + ram_disk.fx_media_driver_write_protect = FX_FALSE; + return_if_fail( status == FX_WRITE_PROTECT); + + /* Attempt to rename a directory that does not exist */ + status = fx_directory_rename(&ram_disk, "/DOES_NOT_EXIST", "/VALID"); + return_if_fail( status != FX_SUCCESS); + + /* Attempt to rename something that is not a directory */ + status = fx_file_create(&ram_disk, "NOT_A_DIR"); + status += fx_directory_rename(&ram_disk, "NOT_A_DIR", "/VALID"); + status += fx_file_delete(&ram_disk, "NOT_A_DIR"); + return_if_fail( status == FX_NOT_DIRECTORY); + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + + /* send null pointer to generate an error */ + status = fx_directory_rename(FX_NULL, "MYTEST", "OURTEST"); + return_if_fail( status == FX_PTR_ERROR); + +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Simple 8.3 rename in the root directory. */ + status = fx_directory_create(&ram_disk, "MYTEST"); + status += fx_directory_rename(&ram_disk, "MYTEST", "OURTEST"); + return_if_fail( status == FX_SUCCESS); + + /* Now create a sub-directory in the "OURTEST" directory. */ + status = fx_directory_create(&ram_disk, "OURTEST/newdir"); + status += fx_directory_rename(&ram_disk, "OURTEST/newdir", "newnewdir"); + status += fx_directory_rename(&ram_disk, "newnewdir", "OURTEST/newdir"); + status += fx_directory_create(&ram_disk, "/OURTEST/newdir/finaltest"); + return_if_fail( status == FX_SUCCESS); + + /* Attempt to rename a directory to a name that is taken */ + status = fx_directory_rename(&ram_disk, "/OURTEST/newdir/finaltest", "/OURTEST/newdir"); + return_if_fail( status == FX_ALREADY_CREATED); + + /* Attempt to rename a directory to a subdirectory with an invalid name */ + status = fx_directory_rename(&ram_disk, "OURTEST/newdir/finaltest", "OURTEST/newdir/does_not_exist/this_wont_work"); + return_if_fail( status == FX_INVALID_NAME); + + /* Rename a directory and make it hidden */ + status = fx_directory_rename(&ram_disk, "OURTEST/newdir/finaltest", "/.hidden"); + return_if_fail( status == FX_SUCCESS); + + /* Create a root directory so we can rename it. */ + status = fx_directory_create(&ram_disk, "newroot"); + + /* Now try to rename the root directory, but with leading and trailing spaces. */ + status += fx_directory_rename(&ram_disk, "newroot", " newnewroot "); + return_if_fail( status == FX_SUCCESS); + + /* Now test the maximum size of the new directory name. */ + for (i = 0; i < FX_MAX_LONG_NAME_LEN; i++) + { + max_name[i] = 'a'; + } + max_name[i] = 0; + + /* Now try to test the maximum new name length. */ + status = fx_directory_rename(&ram_disk, "newnewroot", max_name); + return_if_fail( status == FX_INVALID_NAME); + + max_name[i] = 'a'; + max_name[i + 1] = 'a'; + + /* Now try to test the maximum new name length. */ + status = fx_directory_rename(&ram_disk, "newnewroot", max_name); + return_if_fail(status == FX_INVALID_NAME); + + /* Now make the max name valid. */ + max_name[FX_MAX_LONG_NAME_LEN-1] = 0; + max_name[FX_MAX_LONG_NAME_LEN-2] = '~'; + + /* Rename the directory to a valid maximum name. */ + status = fx_directory_rename(&ram_disk, "newnewroot", max_name); + return_if_fail( status == FX_SUCCESS); + + /* Now build a new long file name with only one cap character different. */ + for (i = 0; i < FX_MAX_LONG_NAME_LEN+1; i++) + { + max_newname[i] = max_name[i]; + } + /* Change just the first character to upper case. */ + max_newname[0] = 'A'; + + /* Rename the directory to a valid maximum name with just an upper case change. */ + status = fx_directory_rename(&ram_disk, max_name, max_newname); + +/* If we rename a file with longest allowed long file, the file size will overflow FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE, which is defined as 2048 in test enviroment. */ +#if defined(FX_ENABLE_FAULT_TOLERANT) && (FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE < 3072) + return_if_fail( status == FX_NO_MORE_SPACE); +#else + return_if_fail( status == FX_SUCCESS); +#endif + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk - but with only one sector cache. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 256); + return_if_fail( status == FX_SUCCESS); + + /* Now try to get an I/O error on the directory free search call. */ +#ifndef FX_ENABLE_FAULT_TOLERANT + _fx_utility_logical_sector_read_error_request = 74; +#else + _fx_utility_logical_sector_read_error_request = 22; +#endif + status = fx_directory_rename(&ram_disk, max_newname, "newroot"); + _fx_utility_logical_sector_read_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Now try to get an I/O error on the directory write call. */ + _fx_utility_logical_sector_write_error_request = 1; + status = fx_directory_rename(&ram_disk, max_newname, "newroot"); + _fx_utility_logical_sector_write_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Now rename the direction with a root directory path. */ + status = fx_directory_rename(&ram_disk, max_newname, "\\newroot"); + return_if_fail( status == FX_SUCCESS); + + /* Fail to update old directory entry. */ + _fx_directory_entry_write_error_request = 2; + status = fx_directory_rename(&ram_disk, "\\newroot", "aaa"); + return_if_fail( status == FX_IO_ERROR); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + printf("SUCCESS!\n"); + test_control_return(0); +} diff --git a/test/regression_test/filex_fault_tolerant_corrupted_log_file_test.c b/test/regression_test/filex_fault_tolerant_corrupted_log_file_test.c new file mode 100644 index 0000000..b9d6740 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_corrupted_log_file_test.c @@ -0,0 +1,246 @@ +/* This FileX test concentrates on validating the buffer overflow when Fault-Tolerant is enabled. */ +#if 0 +*Description* +The Fault Tolerant feature of Azure RTOS FileX includes integer under and overflows which may be exploited to achieve buffer overflow and modify memory contents. +When a valid log file with correct ID and checksum is detected by the _fx_fault_tolerant_enable function an attempt to recover the previous failed write operation is taken by call of _fx_fault_tolerant_apply_logs. This function iterates through the log entries and performs required recovery operations. When properly crafted a log including entries of type FX_FAULT_TOLERANT_DIR_LOG_TYPE may be utilized to introduce unexpected behavior. Multiple values are retrieved from the log file thus controlled by a potential attacker, these include log total size (size variable), number of log entries (remaining_logs), entry type (log_type), length (log_len), copy_size etc. Certain combinations of values may be utilized to bypass validation by exploiting a integer overflow (and optionally underflow). +For example the comparison of copy_offset and copy_size against media_ptr -> fx_media_memory_size may be bypassed by using a value of log_len smaller than FX_FAULT_TOLERANT_DIR_LOG_ENTRY_SIZE to cause a integer underflow and set copy_size to a unexpectedly large value. With correct selection of copy_offset the sum of copy_offset and copy_size will result in an integer overflow resulting in check bypass. Alternatively one may manipulate copy_offset and a not-underflown value of copy_size to again cause integer overflow of the sum. With a substantially large offset value the destination address of memcpy should also be overflown. +With the condition bypassed one may force a buffer overflow in the memcpy call with the possibility to manipulate both the destination address with copy_offset and amount of copied data with copy_size. +For example with log_len set to 15 copy_size will have the value of 4294967295 due to integer underflow, +with copy_offset set to 1 the result of sum of copy_size and copy_offset will be 0 and will result in bypass of the comparison against media_ptr -> fx_media_memory_size allowing a buffer overflow. +``` + case FX_FAULT_TOLERANT_DIR_LOG_TYPE: + /* This is a DIR log. */ + dir_log = (FX_FAULT_TOLERANT_DIR_LOG *)current_ptr; + log_sector = _fx_utility_64_unsigned_read((UCHAR *)&dir_log -> fx_fault_tolerant_dir_log_sector); + /* Get the destination sector. */ + status = _fx_utility_logical_sector_read(media_ptr, + log_sector, + media_ptr -> fx_media_memory_buffer, + ((ULONG) 1), FX_DATA_SECTOR); + if (status != FX_SUCCESS) + { + /* Return the error status. */ + return(status); + } + /* Set copy information. */ + copy_offset = _fx_utility_32_unsigned_read((UCHAR *)&dir_log -> fx_fault_tolerant_dir_log_offset); + copy_size = log_len - FX_FAULT_TOLERANT_DIR_LOG_ENTRY_SIZE; + if ((copy_offset + copy_size) > media_ptr -> fx_media_memory_size) + { + return(FX_FILE_CORRUPT); + } + /* Copy data into destination sector. */ + memcpy(media_ptr -> fx_media_memory_buffer + copy_offset, /* Use case of memcpy is verified. */ + current_ptr + FX_FAULT_TOLERANT_DIR_LOG_ENTRY_SIZE, copy_size); +``` +#endif + +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_corrupted_log_file_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_corrupted_log_file_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status, temp = 0; +UINT i = 0; +ULONG sector; +FX_FAULT_TOLERANT_DIR_LOG *dir_log; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Corrupted Log File test................."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + return_if_fail(ram_disk.fx_media_fault_tolerant_enabled != FX_TRUE); + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Start transaction. */ + _fx_fault_tolerant_transaction_start(&ram_disk); + + /* Save log pointer. */ + dir_log = (FX_FAULT_TOLERANT_DIR_LOG *)(ram_disk.fx_media_fault_tolerant_memory_buffer + + ram_disk.fx_media_fault_tolerant_file_size); + + /* Create a DIR log entry with data that are no sense. */ + sector = ((ULONG)ram_disk.fx_media_data_sector_start) + + (((ULONG64)(ram_disk.fx_media_fault_tolerant_start_cluster + 1 - FX_FAT_ENTRY_START)) * + ((ULONG)ram_disk.fx_media_sectors_per_cluster)); + _fx_fault_tolerant_add_dir_log(&ram_disk, sector, 1, (UCHAR *)&temp, 1); + + /* Manipulate the log size to 15. */ + _fx_utility_16_unsigned_write((UCHAR *)&dir_log -> fx_fault_tolerant_dir_log_size, 15); + + /* End transaction. */ + status = _fx_fault_tolerant_transaction_end(&ram_disk); + return_if_fail( status == FX_FILE_CORRUPT); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_corrupted_log_file_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Corrupted Log File test.................N/A\n"); + + test_control_return(255); +} +#endif diff --git a/test/regression_test/filex_fault_tolerant_delete_large_data_test.c b/test/regression_test/filex_fault_tolerant_delete_large_data_test.c new file mode 100644 index 0000000..212da25 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_delete_large_data_test.c @@ -0,0 +1,374 @@ +/* This FileX test concentrates on the Fault-Tolerant delete large data test. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; +Check media full operation: +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create new file called "TEST.TXT"; +Step4: Get the media available bytes; +Step5: Delete large file from the media; +Step6: Check the media available bytes. +Step7: Loop to read and check the file data from the media; +*/ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_fault_tolerant.h" +#include +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_delete_large_data_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_delete_large_data_test_application_define(void *first_unused_memory) +#endif +{ + + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +ULONG available_bytes; +ULONG i, j; +ULONG data_value; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Delete Large Data Test.................."); + + /* Generate a random number for write data. */ + srand((ULONG)time(NULL)); + data_value = (ULONG)(rand() & 0xFF) | (ULONG)((rand() & 0xFF) << 8) | (ULONG)((rand() & 0xFF) << 16) | (ULONG)((rand() & 0xFF) << 24); + + /* Genearte the write data. */ + for (j = 0; j < large_data_buffer_size / sizeof(ULONG); j ++) + { + ((ULONG*)large_data_buffer)[j] = data_value++; + } + + /* Roll back the value for later verification use. */ + data_value -= (large_data_buffer_size / sizeof(ULONG)); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 60000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 400000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Pickup the available bytes in the media. */ + status = fx_media_space_available(&ram_disk, &available_bytes); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (available_bytes < sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Write the data to fill the media one time. */ + status = fx_file_write(&my_file, (void *) large_data_buffer, available_bytes); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Pickup the available bytes in the media again. */ + status = fx_media_space_available(&ram_disk, &j); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (j != 0)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + +#ifndef FX_DISABLE_CACHE + /* At this point, we should invalidate the (which also flushes the cache) media to ensure that all + dirty sectors are written. */ + status = fx_media_cache_invalidate(&ram_disk); + + /* Check for flush errors. */ + if ((status != FX_SUCCESS) || (ram_disk.fx_media_sector_cache_dirty_count)) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* See if any sectors are still valid in the cache. */ + for (j = 0; j < ram_disk.fx_media_sector_cache_size; j++) + { + + /* Determine if this cache entry is still valid. */ + if (ram_disk.fx_media_sector_cache[j].fx_cached_sector_valid) + { + + printf("ERROR!\n"); + test_control_return(10); + } + } +#endif /* FX_DISABLE_CACHE */ + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Now read in all the bytes again to make sure the file contents are really there. */ + status = fx_file_read(&my_file, (void *) large_data_buffer, large_data_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != available_bytes)) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Delete the data from the file. */ + status = fx_file_delete(&ram_disk, "TEST.TXT"); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_delete_large_data_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Delete Large Data Test..................N/A\n"); + + test_control_return(255); +} +#endif diff --git a/test/regression_test/filex_fault_tolerant_directory_attributes_set_directory_interrupt_test.c b/test/regression_test/filex_fault_tolerant_directory_attributes_set_directory_interrupt_test.c new file mode 100644 index 0000000..458bf71 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_directory_attributes_set_directory_interrupt_test.c @@ -0,0 +1,364 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check directory interrupt for fx_directory_attributes_set(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create directory "TEST_DIR"; +Step4: Check the attributes for "TEST_DIR" directory; +Step5: Create a new thread to set the attributes as FX_DIRECTORY | FX_ARCHIVE | FX_SYSTEM | FX_READ_ONLY | FX_HIDDEN for "TEST_DIR" directory; +Step6: Terminate the new thread to simulate poweroff when update the directory after record the redo log. +Step7: Open the media; +Step8: Enable fault tolerant feature to recover the data(redo operation); +Step9: Check the attributes for "TEST_DIR" directory. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_directory_attributes_set_directory_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT directory_write_interrupt = FX_FALSE; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_directory_attributes_set_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT attributes; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Dir Attrib Set DIR Interrupt Test......."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Create a directory called TEST_DIR in the root directory. */ + status = fx_directory_create(&ram_disk, "TEST_DIR"); + return_if_fail( status == FX_SUCCESS); + + /* Invalidate the media cache. */ + fx_media_cache_invalidate(&ram_disk); + + /* Pickup the attributes of the directory. */ + status = fx_directory_attributes_read(&ram_disk, "TEST_DIR", &attributes); + + /* Check the attributes read status. */ + return_if_fail( (status == FX_SUCCESS) && (attributes == FX_DIRECTORY)); + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + directory_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable( &ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Invalidate the media cache. */ + fx_media_cache_invalidate(&ram_disk); + + /* Pickup the attributes of the directory again. */ + status = fx_directory_attributes_read(&ram_disk, "TEST_DIR", &attributes); + + /* Check the attributes read status. */ + return_if_fail((status == FX_SUCCESS) && (attributes == (FX_ARCHIVE | FX_READ_ONLY | FX_DIRECTORY | FX_SYSTEM | FX_HIDDEN))) + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + return_if_fail((status == FX_SUCCESS) && (directory_write_interrupt == FX_TRUE) && (error_couter == 0)); + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Now write the attributes out for the directory. */ + fx_directory_attributes_set(&ram_disk, "TEST_DIR", FX_DIRECTORY | FX_ARCHIVE | FX_SYSTEM | FX_READ_ONLY | FX_HIDDEN); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the Directory write operation after record the redo log. */ + if ((sector_type == FX_DIRECTORY_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + directory_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_directory_attributes_set_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Dir Attrib Set DIR Interrupt Test.......N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_directory_attributes_set_redo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_directory_attributes_set_redo_log_interrupt_test.c new file mode 100644 index 0000000..c9a72d3 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_directory_attributes_set_redo_log_interrupt_test.c @@ -0,0 +1,422 @@ +/* This FileX test concentrates on the Fault-Tolerant redo_log write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check redo log interrupt for fx_directory_attributes_set(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create directory "TEST_DIR"; +Step4: Check the attributes for "TEST_DIR" directory; +Step5: Create new thread to set the attributes as FX_DIRECTORY | FX_ARCHIVE | FX_SYSTEM | FX_READ_ONLY | FX_HIDDEN for "TEST_DIR" directory; +Step6: Terminate the new thread to simulate poweroff when update the redo log. +Step7: Open the media; +Step8: Enable fault tolerant feature to recover the data(undo operation); +Step9: Check the attributes for "TEST_DIR" directory. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_directory_attributes_set_redo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT log_write_interrupt = FX_FALSE; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_directory_attributes_set_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT attributes; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Dir Attrib Set Redo LOG Interrupt Test.."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a directory called TEST_DIR in the root directory. */ + status = fx_directory_create(&ram_disk, "TEST_DIR"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Invalidate the media cache. */ + fx_media_cache_invalidate(&ram_disk); + + /* Pickup the attributes of the directory. */ + status = fx_directory_attributes_read(&ram_disk, "TEST_DIR", &attributes); + + /* Check the attributes read status. */ + if ((status != FX_SUCCESS) || (attributes != FX_DIRECTORY)) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(6); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Invalidate the media cache. */ + fx_media_cache_invalidate(&ram_disk); + + /* Pickup the attributes of the directory again. */ + status = fx_directory_attributes_read(&ram_disk, "TEST_DIR", &attributes); + + /* Check the attributes read status. */ + if ((status != FX_SUCCESS) || (attributes != FX_DIRECTORY)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (log_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Now write the attributes out for the directory. */ + fx_directory_attributes_set(&ram_disk, "TEST_DIR", FX_DIRECTORY | FX_ARCHIVE | FX_SYSTEM | FX_READ_ONLY | FX_HIDDEN); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the redo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_directory_attributes_set_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Dir Attrib Set Redo LOG Interrupt Test..N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_directory_create_directory_interrupt_test.c b/test/regression_test/filex_fault_tolerant_directory_create_directory_interrupt_test.c new file mode 100644 index 0000000..15da3ac --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_directory_create_directory_interrupt_test.c @@ -0,0 +1,688 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check directory interrupt for fx_directory_create(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create directories and sub-directories; +Step4: Traverse the directory; +Step5: Create new thread to create new directory "/D0/D5"; +Step6: Terminate the new thread to simulate poweroff when update the directory after record the redo log. +Step7: Open the media; +Step8: Enable fault tolerant feature to recover the data(redo operation); +Step9: Traverse the directory; +Step10: Recreate directory "/D0/D5"; +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_directory_create_directory_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static UCHAR *pointer; + +#define TEST_COUNT 3 + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_counter = 0; +static UINT directory_write_interrupt = FX_FALSE; +static UINT expected_index = 0; +static CHAR * expected_name[] = { + + "A0", + ".", + "..", + "A1", + ".", + "..", + "A00", + ".", + "..", + "A2", + ".", + "..", + "A3", + ".", + "..", + "B0", + ".", + "..", + "B1", + ".", + "..", + "B2", + ".", + "..", + "B00", + ".", + "..", + "B3", + ".", + "..", + "C0", + ".", + "..", + "C1", + ".", + "..", + "C2", + ".", + "..", + "C3", + ".", + "..", + "C00", + ".", + "..", + "D0", + ".", + "..", + "D1", + ".", + "..", + "D2", + ".", + "..", + "D3", + ".", + "..", + "D4", + ".", + "..", + "D00", + ".", + "..", + "D5", + ".", + "..", + "END"}; + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +static void traverse_directory(CHAR *directory_name); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_directory_create_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Create the main thread. */ + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Dir Create DIR Interrupt Test..........."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a series of directories... */ + status = fx_directory_create(&ram_disk, "/A0"); + status += fx_directory_create(&ram_disk, "/B0"); + status += fx_directory_create(&ram_disk, "/C0"); + status += fx_directory_create(&ram_disk, "/D0"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(4); + } + + /* Create the next level of sub-directories. */ + status = fx_directory_create(&ram_disk, "/A0/A1"); + status += fx_directory_create(&ram_disk, "/A0/A2"); + status += fx_directory_create(&ram_disk, "/A0/A3"); + + status += fx_directory_create(&ram_disk, "/B0/B1"); + status += fx_directory_create(&ram_disk, "/B0/B2"); + status += fx_directory_create(&ram_disk, "/B0/B3"); + + status += fx_directory_create(&ram_disk, "/C0/C1"); + status += fx_directory_create(&ram_disk, "/C0/C2"); + status += fx_directory_create(&ram_disk, "/C0/C3"); + + status += fx_directory_create(&ram_disk, "/D0/D1"); + status += fx_directory_create(&ram_disk, "/D0/D2"); + status += fx_directory_create(&ram_disk, "/D0/D3"); + status += fx_directory_create(&ram_disk, "/D0/D4"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(5); + } + + /* Now create the third level of sub-directories... */ + status = fx_directory_create(&ram_disk, "/A0/A1/A00"); + status += fx_directory_create(&ram_disk, "/B0/B2/B00"); + status += fx_directory_create(&ram_disk, "/C0/C3/C00"); + status += fx_directory_create(&ram_disk, "/D0/D4/D00"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(6); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + + /* Traverse the directory structure recursively. */ + traverse_directory(FX_NULL); + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + directory_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(7); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + + /* Traverse the directory structure recursively. */ + traverse_directory(FX_NULL); + + /* Attempt to create the same directory again. */ + status = fx_directory_create(&ram_disk, "/D0/D5"); + + /* Check for errors... */ + if (status != FX_ALREADY_CREATED) + { + + /* Error creating same directory twice. Return to caller. */ + printf("ERROR!\n"); + test_control_return(9); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (directory_write_interrupt != FX_TRUE) || (error_counter)) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Create the next level of sub-directories. */ + fx_directory_create(&ram_disk, "/D0/D5"); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the Directory write operation after record the redo log. */ + if ((sector_type == FX_DIRECTORY_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + directory_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} + +#ifndef FX_STANDALONE_ENABLE +/* Define the FAT12, 16 and 32 directory traversal routine for the local path test. */ +static void traverse_directory(CHAR *directory_name) +{ + +FX_LOCAL_PATH local_path; +FX_FILE file; +UINT status; +CHAR name[300]; +UINT skip; + + + /* Determine if we are at the start. */ + if (directory_name == FX_NULL) + { + status = fx_directory_local_path_set(&ram_disk, &local_path, "\\"); + skip = 0; + } + else + { + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name); + skip = 2; + } + + /* Get the first directory entry in the root path. */ + status = fx_directory_first_entry_find(&ram_disk, name); + + /* Loop through the directory entries for this path. */ + while (status == FX_SUCCESS) + { + + /* Compare with what is expected. */ + if (strcmp(name, expected_name[expected_index++])) + error_counter++; + + /* Determine if this name is a directory or a file. */ + status = fx_directory_name_test(&ram_disk, name); + + /* What is it? */ + if (status == FX_NOT_DIRECTORY) + { + + /* This is a file, open it to test its integrity. */ + status = fx_file_open(&ram_disk, &file, name, FX_OPEN_FOR_READ); + if (status != FX_SUCCESS) + error_counter++; + status = fx_file_close(&file); + if (status != FX_SUCCESS) + error_counter++; + + /* Pickup the next directory entry. */ + status = fx_directory_next_entry_find(&ram_disk, name); + } + else if (status == FX_SUCCESS) + { + + /* Skip the first two entries... */ + if (skip) + skip--; + else + { + /* Recursive call to traverse directory. */ + traverse_directory(name); + + /* Restore path. */ + status = fx_directory_local_path_restore(&ram_disk, &local_path); + } + + status = fx_directory_next_entry_find(&ram_disk, name); + } + } + fx_directory_local_path_clear(&ram_disk); +} + +#else + +static void traverse_directory(CHAR *directory_name) +{ +UINT status; +CHAR name[300]; + + /* Pickup the first entry in the root directory. */ + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, "A0")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "B0")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "C0")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "D0")) + status++; + + /* Now read one past the number of entries in order to get an error condition. */ + status += fx_directory_next_entry_find(&ram_disk, name); + + /* Determine if the test was successful. */ + if (status != FX_NO_MORE_ENTRIES) + { + + printf("ERROR!\n"); + test_control_return(12); + } + /* Okay, now postion to the deepest sub-directory and ensure that the directory first/next + operations work properly there as well. */ + + /* Set the current path */ + status = fx_directory_default_set(&ram_disk, "/D0/D4"); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error setting the path. Return to caller. */ + printf("ERROR!\n"); + test_control_return(16); + } + + /* Pickup the first entry in this sub-sub directory. */ + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, ".")) + status++; + + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "..")) + status++; + + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "D00")) + status++; + + /* Now read one past the number of entries in order to get an error condition. */ + status += fx_directory_next_entry_find(&ram_disk, name); + + /* Determine if the test was successful. */ + if (status != FX_NO_MORE_ENTRIES) + { + + printf("ERROR!\n"); + test_control_return(18); + } + +} + +#endif +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_directory_create_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Dir Create DIR Interrupt Test...........N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_directory_create_redo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_directory_create_redo_log_interrupt_test.c new file mode 100644 index 0000000..25b5467 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_directory_create_redo_log_interrupt_test.c @@ -0,0 +1,703 @@ +/* This FileX test concentrates on the Fault-Tolerant redo log write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check redo log interrupt for fx_directory_create(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create directories and sub-directories; +Step4: Traverse the directory; +Step5: Create new thread to create new directory "/D0/D5"; +Step6: Terminate the new thread to simulate poweroff when update the redo log. +Step7: Open the media; +Step8: Enable fault tolerant feature to recover the data(undo operation); +Step9: Traverse the directory; +Step10: Recreate directory "/D0/D5"; +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_directory_create_redo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static UCHAR *pointer; + +#define TEST_COUNT 3 + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_counter = 0; +static UINT redo_complete =0; +static UINT log_write_interrupt = FX_FALSE; +static UINT expected_index = 0; +static CHAR * expected_name[] = { + + "A0", + ".", + "..", + "A1", + ".", + "..", + "A00", + ".", + "..", + "A2", + ".", + "..", + "A3", + ".", + "..", + "B0", + ".", + "..", + "B1", + ".", + "..", + "B2", + ".", + "..", + "B00", + ".", + "..", + "B3", + ".", + "..", + "C0", + ".", + "..", + "C1", + ".", + "..", + "C2", + ".", + "..", + "C3", + ".", + "..", + "C00", + ".", + "..", + "D0", + ".", + "..", + "D1", + ".", + "..", + "D2", + ".", + "..", + "D3", + ".", + "..", + "D4", + ".", + "..", + "D00", + ".", + "..", + "D5", + ".", + "..", + "END"}; + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +static void traverse_directory(CHAR *directory_name); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_directory_create_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Create the main thread. */ + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Dir Create Redo LOG Interrupt Test......"); + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + redo_complete = 0; + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a series of directories... */ + status = fx_directory_create(&ram_disk, "/A0"); + status += fx_directory_create(&ram_disk, "/B0"); + status += fx_directory_create(&ram_disk, "/C0"); + status += fx_directory_create(&ram_disk, "/D0"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(4); + } + + /* Create the next level of sub-directories. */ + status = fx_directory_create(&ram_disk, "/A0/A1"); + status += fx_directory_create(&ram_disk, "/A0/A2"); + status += fx_directory_create(&ram_disk, "/A0/A3"); + + status += fx_directory_create(&ram_disk, "/B0/B1"); + status += fx_directory_create(&ram_disk, "/B0/B2"); + status += fx_directory_create(&ram_disk, "/B0/B3"); + + status += fx_directory_create(&ram_disk, "/C0/C1"); + status += fx_directory_create(&ram_disk, "/C0/C2"); + status += fx_directory_create(&ram_disk, "/C0/C3"); + + status += fx_directory_create(&ram_disk, "/D0/D1"); + status += fx_directory_create(&ram_disk, "/D0/D2"); + status += fx_directory_create(&ram_disk, "/D0/D3"); + status += fx_directory_create(&ram_disk, "/D0/D4"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(5); + } + + /* Now create the third level of sub-directories... */ + status = fx_directory_create(&ram_disk, "/A0/A1/A00"); + status += fx_directory_create(&ram_disk, "/B0/B2/B00"); + status += fx_directory_create(&ram_disk, "/C0/C3/C00"); + status += fx_directory_create(&ram_disk, "/D0/D4/D00"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(6); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + + /* Traverse the directory structure recursively. */ + traverse_directory(FX_NULL); + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); + +#endif + + /* After interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(7); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + + /* Traverse the directory structure recursively. */ + traverse_directory(FX_NULL); + + /* Attempt to create the same directory again. */ + status = fx_directory_create(&ram_disk, "/D0/D5"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating same directory twice. Return to caller. */ + printf("ERROR!\n"); + test_control_return(9); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + + redo_complete = 1; + /* Traverse the directory structure recursively. */ + traverse_directory(FX_NULL); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (log_write_interrupt != FX_TRUE) || (error_counter)) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Create the next level of sub-directories. */ + fx_directory_create(&ram_disk, "/D0/D5"); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + /* Interrupt the redo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} + +#ifndef FX_STANDALONE_ENABLE +/* Define the FAT12, 16 and 32 directory traversal routine for the local path test. */ +static void traverse_directory(CHAR *directory_name) +{ + +FX_LOCAL_PATH local_path; +FX_FILE file; +UINT status; +CHAR name[300]; +UINT skip; + + + /* Determine if we are at the start. */ + if (directory_name == FX_NULL) + { + status = fx_directory_local_path_set(&ram_disk, &local_path, "\\"); + skip = 0; + } + else + { + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name); + skip = 2; + } + + /* Get the first directory entry in the root path. */ + status = fx_directory_first_entry_find(&ram_disk, name); + + /* Loop through the directory entries for this path. */ + while (status == FX_SUCCESS) + { + + /* Compare with what is expected. */ + if (strcmp(name, expected_name[expected_index++])) + error_counter++; + + /* Determine if this name is a directory or a file. */ + status = fx_directory_name_test(&ram_disk, name); + + /* What is it? */ + if (status == FX_NOT_DIRECTORY) + { + + /* This is a file, open it to test its integrity. */ + status = fx_file_open(&ram_disk, &file, name, FX_OPEN_FOR_READ); + if (status != FX_SUCCESS) + error_counter++; + status = fx_file_close(&file); + if (status != FX_SUCCESS) + error_counter++; + + /* Pickup the next directory entry. */ + status = fx_directory_next_entry_find(&ram_disk, name); + } + else if (status == FX_SUCCESS) + { + + /* Skip the first two entries... */ + if (skip) + skip--; + else + { + /* Recursive call to traverse directory. */ + traverse_directory(name); + + /* Restore path. */ + status = fx_directory_local_path_restore(&ram_disk, &local_path); + } + + status = fx_directory_next_entry_find(&ram_disk, name); + } + } + fx_directory_local_path_clear(&ram_disk); +} + +#else + +static void traverse_directory(CHAR *directory_name) +{ +UINT status; +CHAR name[300]; + + /* Pickup the first entry in the root directory. */ + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, "A0")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "B0")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "C0")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "D0")) + status++; + + /* Okay, now postion to the deepest sub-directory and ensure that the directory first/next + operations work properly there as well. */ + + /* Set the current path */ + status = fx_directory_default_set(&ram_disk, "/D0/D4"); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error setting the path. Return to caller. */ + printf("ERROR!\n"); + test_control_return(16); + } + + /* Pickup the first entry in this sub-sub directory. */ + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, ".")) + status++; + + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "..")) + status++; + + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "D00")) + status++; + + /* Set the current path */ + status = fx_directory_default_set(&ram_disk, "/D0"); + status += fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, ".")) + error_counter++; + status += fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "..")) + error_counter++; + status += fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "D1")) + error_counter++; + status += fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "D2")) + error_counter++; + status += fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "D3")) + error_counter++; + status += fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "D4")) + error_counter++; + if(redo_complete==1) + { + status += fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "D5")) + error_counter++; + } +} + +#endif + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_directory_create_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Dir Create Redo LOG Interrupt Test......N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_directory_delete_directory_interrupt_test.c b/test/regression_test/filex_fault_tolerant_directory_delete_directory_interrupt_test.c new file mode 100644 index 0000000..100c407 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_directory_delete_directory_interrupt_test.c @@ -0,0 +1,676 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check directory interrupt for fx_directory_delete(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create directories and sub-directories; +Step4: Traverse the directory; +Step5: Create new thread to delete directory "/D0/D4/D00"; +Step6: Terminate the new thread to simulate poweroff when update the directory after record the redo log. +Step7: Open the media; +Step8: Enable fault tolerant feature to recover the data(redo operation); +Step9: Traverse the directory; +Step10: Recreate directory "/D0/D4/D00"; +Step11: Traverse the directory; +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_directory_delete_directory_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static UCHAR *pointer; + +#define TEST_COUNT 3 + +/* Define the counters used in the test application... */ +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_counter = 0; +static UINT poweroff_flag=0; +static UINT directory_write_interrupt = FX_FALSE; +static UINT expected_index = 0; +static CHAR * expected_name[] = { + + "A0", + ".", + "..", + "A1", + ".", + "..", + "A00", + ".", + "..", + "A2", + ".", + "..", + "A3", + ".", + "..", + "B0", + ".", + "..", + "B1", + ".", + "..", + "B2", + ".", + "..", + "B00", + ".", + "..", + "B3", + ".", + "..", + "C0", + ".", + "..", + "C1", + ".", + "..", + "C2", + ".", + "..", + "C3", + ".", + "..", + "C00", + ".", + "..", + "D0", + ".", + "..", + "D1", + ".", + "..", + "D2", + ".", + "..", + "D3", + ".", + "..", + "D4", + ".", + "..", + "D00", + ".", + "..", + "D5", + ".", + "..", + "END"}; + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +static void traverse_directory(CHAR *directory_name); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_directory_delete_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Dir Delete DIR Interrupt Test..........."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + poweroff_flag =0; + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a series of directories... */ + status = fx_directory_create(&ram_disk, "/A0"); + status += fx_directory_create(&ram_disk, "/B0"); + status += fx_directory_create(&ram_disk, "/C0"); + status += fx_directory_create(&ram_disk, "/D0"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(4); + } + + /* Create the next level of sub-directories. */ + status = fx_directory_create(&ram_disk, "/A0/A1"); + status += fx_directory_create(&ram_disk, "/A0/A2"); + status += fx_directory_create(&ram_disk, "/A0/A3"); + + status += fx_directory_create(&ram_disk, "/B0/B1"); + status += fx_directory_create(&ram_disk, "/B0/B2"); + status += fx_directory_create(&ram_disk, "/B0/B3"); + + status += fx_directory_create(&ram_disk, "/C0/C1"); + status += fx_directory_create(&ram_disk, "/C0/C2"); + status += fx_directory_create(&ram_disk, "/C0/C3"); + + status += fx_directory_create(&ram_disk, "/D0/D1"); + status += fx_directory_create(&ram_disk, "/D0/D2"); + status += fx_directory_create(&ram_disk, "/D0/D3"); + status += fx_directory_create(&ram_disk, "/D0/D4"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(5); + } + + /* Now create the third level of sub-directories... */ + status = fx_directory_create(&ram_disk, "/A0/A1/A00"); + status += fx_directory_create(&ram_disk, "/B0/B2/B00"); + status += fx_directory_create(&ram_disk, "/C0/C3/C00"); + status += fx_directory_create(&ram_disk, "/D0/D4/D00"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(6); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + + /* Traverse the directory structure recursively. */ + traverse_directory(FX_NULL); + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + directory_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); + +#endif + + /* After interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(7); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + poweroff_flag++; + /* Traverse the directory structure recursively. */ + traverse_directory(FX_NULL); + + /* Attempt to create the same directory again. */ + status = fx_directory_create(&ram_disk, "/D0/D4/D00"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating same directory twice. Return to caller. */ + printf("ERROR!\n"); + test_control_return(9); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + poweroff_flag++; + + /* Traverse the directory structure recursively. */ + traverse_directory(FX_NULL); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (directory_write_interrupt != FX_TRUE) || (error_counter)) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Delete the next level of sub-directories. */ + fx_directory_delete(&ram_disk, "/D0/D4/D00"); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the Directory write operation after record the redo log. */ + if ((sector_type == FX_DIRECTORY_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) == FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + directory_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} + +#ifndef FX_STANDALONE_ENABLE +/* Define the FAT12, 16 and 32 directory traversal routine for the local path test. */ +static void traverse_directory(CHAR *directory_name) +{ + +FX_LOCAL_PATH local_path; +FX_FILE file; +UINT status; +CHAR name[300]; +UINT skip; + + + /* Determine if we are at the start. */ + if (directory_name == FX_NULL) + { + status = fx_directory_local_path_set(&ram_disk, &local_path, "\\"); + skip = 0; + } + else + { + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name); + skip = 2; + } + + /* Get the first directory entry in the root path. */ + status = fx_directory_first_entry_find(&ram_disk, name); + + /* Loop through the directory entries for this path. */ + while (status == FX_SUCCESS) + { + + /* Compare with what is expected. */ + if (strcmp(name, expected_name[expected_index++])) + error_counter++; + + /* Determine if this name is a directory or a file. */ + status = fx_directory_name_test(&ram_disk, name); + + /* What is it? */ + if (status == FX_NOT_DIRECTORY) + { + + /* This is a file, open it to test its integrity. */ + status = fx_file_open(&ram_disk, &file, name, FX_OPEN_FOR_READ); + if (status != FX_SUCCESS) + error_counter++; + status = fx_file_close(&file); + if (status != FX_SUCCESS) + error_counter++; + + /* Pickup the next directory entry. */ + status = fx_directory_next_entry_find(&ram_disk, name); + } + else if (status == FX_SUCCESS) + { + + /* Skip the first two entries... */ + if (skip) + skip--; + else + { + /* Recursive call to traverse directory. */ + traverse_directory(name); + + /* Restore path. */ + status = fx_directory_local_path_restore(&ram_disk, &local_path); + } + + status = fx_directory_next_entry_find(&ram_disk, name); + } + } + fx_directory_local_path_clear(&ram_disk); +} + +#else + +static void traverse_directory(CHAR *directory_name) +{ +UINT status; +CHAR name[300]; + + /* Pickup the first entry in the root directory. */ + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, "A0")) + error_counter++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "B0")) + error_counter++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "C0")) + error_counter++; + if(poweroff_flag !=1) + { + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "D0")) + error_counter++; + + /* Okay, now postion to the deepest sub-directory and ensure that the directory first/next + operations work properly there as well. */ + + /* Set the current path */ + status = fx_directory_default_set(&ram_disk, "/D0/D4"); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error setting the path. Return to caller. */ + printf("ERROR!\n"); + test_control_return(16); + } + + /* Pickup the first entry in this sub-sub directory. */ + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, ".")) + error_counter++; + + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "..")) + error_counter++; + + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "D00")) + error_counter++; + } +} + +#endif +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_directory_delete_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Dir Delete DIR Interrupt Test...........N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_directory_delete_redo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_directory_delete_redo_log_interrupt_test.c new file mode 100644 index 0000000..8a9fddb --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_directory_delete_redo_log_interrupt_test.c @@ -0,0 +1,672 @@ +/* This FileX test concentrates on the Fault-Tolerant redo log write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check redo interrupt for fx_directory_delete(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create directories and sub-directories; +Step4: Traverse the directory; +Step5: Create new thread to delete directory "/D0/D4/D00"; +Step6: Terminate the new thread to simulate poweroff when update the redo log. +Step7: Open the media; +Step8: Enable fault tolerant feature to recover the data(undo operation); +Step9: Traverse the directory; +Step10: Recreate directory "/D0/D4/D00"; +Step11: Traverse the directory; +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_directory_delete_redo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static UCHAR *pointer; + +#define TEST_COUNT 3 + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_counter = 0; +static UINT log_write_interrupt = FX_FALSE; +static UINT expected_index = 0; +static CHAR * expected_name[] = { + + "A0", + ".", + "..", + "A1", + ".", + "..", + "A00", + ".", + "..", + "A2", + ".", + "..", + "A3", + ".", + "..", + "B0", + ".", + "..", + "B1", + ".", + "..", + "B2", + ".", + "..", + "B00", + ".", + "..", + "B3", + ".", + "..", + "C0", + ".", + "..", + "C1", + ".", + "..", + "C2", + ".", + "..", + "C3", + ".", + "..", + "C00", + ".", + "..", + "D0", + ".", + "..", + "D1", + ".", + "..", + "D2", + ".", + "..", + "D3", + ".", + "..", + "D4", + ".", + "..", + "D00", + ".", + "..", + "D5", + ".", + "..", + "END"}; + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +static void traverse_directory(CHAR *directory_name); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_directory_delete_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Dir Delete Redo LOG Interrupt Test......"); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a series of directories... */ + status = fx_directory_create(&ram_disk, "/A0"); + status += fx_directory_create(&ram_disk, "/B0"); + status += fx_directory_create(&ram_disk, "/C0"); + status += fx_directory_create(&ram_disk, "/D0"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(4); + } + + /* Create the next level of sub-directories. */ + status = fx_directory_create(&ram_disk, "/A0/A1"); + status += fx_directory_create(&ram_disk, "/A0/A2"); + status += fx_directory_create(&ram_disk, "/A0/A3"); + + status += fx_directory_create(&ram_disk, "/B0/B1"); + status += fx_directory_create(&ram_disk, "/B0/B2"); + status += fx_directory_create(&ram_disk, "/B0/B3"); + + status += fx_directory_create(&ram_disk, "/C0/C1"); + status += fx_directory_create(&ram_disk, "/C0/C2"); + status += fx_directory_create(&ram_disk, "/C0/C3"); + + status += fx_directory_create(&ram_disk, "/D0/D1"); + status += fx_directory_create(&ram_disk, "/D0/D2"); + status += fx_directory_create(&ram_disk, "/D0/D3"); + status += fx_directory_create(&ram_disk, "/D0/D4"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(5); + } + + /* Now create the third level of sub-directories... */ + status = fx_directory_create(&ram_disk, "/A0/A1/A00"); + status += fx_directory_create(&ram_disk, "/B0/B2/B00"); + status += fx_directory_create(&ram_disk, "/C0/C3/C00"); + status += fx_directory_create(&ram_disk, "/D0/D4/D00"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(6); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + + /* Traverse the directory structure recursively. */ + traverse_directory(FX_NULL); + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); + +#endif + + /* After interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(7); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + + /* Traverse the directory structure recursively. */ + traverse_directory(FX_NULL); + + /* Attempt to create the same directory again. */ + status = fx_directory_create(&ram_disk, "/D0/D4/D00"); + + /* Check for errors... */ + if (status != FX_ALREADY_CREATED) + { + + /* Error creating same directory twice. Return to caller. */ + printf("ERROR!\n"); + test_control_return(9); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + + /* Traverse the directory structure recursively. */ + traverse_directory(FX_NULL); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (log_write_interrupt != FX_TRUE) || (error_counter)) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Delete the next level of sub-directories. */ + fx_directory_delete(&ram_disk, "/D0/D4/D00"); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the redo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} + +#ifndef FX_STANDALONE_ENABLE +/* Define the FAT12, 16 and 32 directory traversal routine for the local path test. */ +static void traverse_directory(CHAR *directory_name) +{ + +FX_LOCAL_PATH local_path; +FX_FILE file; +UINT status; +CHAR name[300]; +UINT skip; + + + /* Determine if we are at the start. */ + if (directory_name == FX_NULL) + { + status = fx_directory_local_path_set(&ram_disk, &local_path, "\\"); + skip = 0; + } + else + { + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name); + skip = 2; + } + + /* Get the first directory entry in the root path. */ + status = fx_directory_first_entry_find(&ram_disk, name); + + /* Loop through the directory entries for this path. */ + while (status == FX_SUCCESS) + { + + /* Compare with what is expected. */ + if (strcmp(name, expected_name[expected_index++])) + error_counter++; + + /* Determine if this name is a directory or a file. */ + status = fx_directory_name_test(&ram_disk, name); + + /* What is it? */ + if (status == FX_NOT_DIRECTORY) + { + + /* This is a file, open it to test its integrity. */ + status = fx_file_open(&ram_disk, &file, name, FX_OPEN_FOR_READ); + if (status != FX_SUCCESS) + error_counter++; + status = fx_file_close(&file); + if (status != FX_SUCCESS) + error_counter++; + + /* Pickup the next directory entry. */ + status = fx_directory_next_entry_find(&ram_disk, name); + } + else if (status == FX_SUCCESS) + { + + /* Skip the first two entries... */ + if (skip) + skip--; + else + { + /* Recursive call to traverse directory. */ + traverse_directory(name); + + /* Restore path. */ + status = fx_directory_local_path_restore(&ram_disk, &local_path); + } + + status = fx_directory_next_entry_find(&ram_disk, name); + } + } + fx_directory_local_path_clear(&ram_disk); +} + +#else + +static void traverse_directory(CHAR *directory_name) +{ +UINT status; +CHAR name[300]; + + /* Pickup the first entry in the root directory. */ + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, "A0")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "B0")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "C0")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "D0")) + status++; + + /* Verify if the path /D0/D4/D00 was created */ + + /* Set the current path */ + status = fx_directory_default_set(&ram_disk, "/D0/D4"); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error setting the path. Return to caller. */ + printf("ERROR!\n"); + test_control_return(16); + } + + /* Pickup the first entry in this sub-sub directory. */ + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, ".")) + status++; + + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "..")) + status++; + + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "D00")) + status++; + +} + +#endif + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_directory_delete_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Dir Delete Redo LOG Interrupt Test......N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_directory_rename_directory_interrupt_test.c b/test/regression_test/filex_fault_tolerant_directory_rename_directory_interrupt_test.c new file mode 100644 index 0000000..9e000db --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_directory_rename_directory_interrupt_test.c @@ -0,0 +1,691 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check directory interrupt for fx_directory_rename(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create directories and sub-directories; +Step4: Traverse the directory; +Step5: Create new thread to rename directory from "/D0/D4" to "/D0/D5"; +Step6: Terminate the new thread to simulate poweroff when update the directory after record the redo log. +Step7: Open the media; +Step8: Enable fault tolerant feature to recover the data(redo operation); +Step9: Create directory "/D0/D4"; +Step10: Create directory "/D0/D5"; +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_directory_rename_directory_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static UCHAR *pointer; + +#define TEST_COUNT 3 + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_counter = 0; +static UINT exit_flag = 0; +static UINT directory_write_interrupt = FX_FALSE; +static UINT expected_index = 0; +static CHAR * expected_name[] = { + + "A0", + ".", + "..", + "A1", + ".", + "..", + "A00", + ".", + "..", + "A2", + ".", + "..", + "A3", + ".", + "..", + "B0", + ".", + "..", + "B1", + ".", + "..", + "B2", + ".", + "..", + "B00", + ".", + "..", + "B3", + ".", + "..", + "C0", + ".", + "..", + "C1", + ".", + "..", + "C2", + ".", + "..", + "C3", + ".", + "..", + "C00", + ".", + "..", + "D0", + ".", + "..", + "D1", + ".", + "..", + "D2", + ".", + "..", + "D3", + ".", + "..", + "D4", + ".", + "..", + "D00", + ".", + "..", + "END"}; + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +static void traverse_directory(CHAR *directory_name); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_directory_rename_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Dir Rename DIR Interrupt Test..........."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + exit_flag = 0; + + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + } + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a series of directories... */ + status = fx_directory_create(&ram_disk, "/A0"); + status += fx_directory_create(&ram_disk, "/B0"); + status += fx_directory_create(&ram_disk, "/C0"); + status += fx_directory_create(&ram_disk, "/D0"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(4); + } + + /* Create the next level of sub-directories. */ + status = fx_directory_create(&ram_disk, "/A0/A1"); + status += fx_directory_create(&ram_disk, "/A0/A2"); + status += fx_directory_create(&ram_disk, "/A0/A3"); + + status += fx_directory_create(&ram_disk, "/B0/B1"); + status += fx_directory_create(&ram_disk, "/B0/B2"); + status += fx_directory_create(&ram_disk, "/B0/B3"); + + status += fx_directory_create(&ram_disk, "/C0/C1"); + status += fx_directory_create(&ram_disk, "/C0/C2"); + status += fx_directory_create(&ram_disk, "/C0/C3"); + + status += fx_directory_create(&ram_disk, "/D0/D1"); + status += fx_directory_create(&ram_disk, "/D0/D2"); + status += fx_directory_create(&ram_disk, "/D0/D3"); + status += fx_directory_create(&ram_disk, "/D0/D4"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(5); + } + + /* Now create the third level of sub-directories... */ + status = fx_directory_create(&ram_disk, "/A0/A1/A00"); + status += fx_directory_create(&ram_disk, "/B0/B2/B00"); + status += fx_directory_create(&ram_disk, "/C0/C3/C00"); + status += fx_directory_create(&ram_disk, "/D0/D4/D00"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(6); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + + /* Traverse the directory structure recursively. */ + traverse_directory(FX_NULL); + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + directory_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(7); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Attempt to create the "/D0/D4" directory again. */ + status = fx_directory_create(&ram_disk, "/D0/D4"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating same directory twice. Return to caller. */ + printf("ERROR!\n"); + test_control_return(9); + } + + /* Attempt to create the "/D0/D5" directory again. */ + status = fx_directory_create(&ram_disk, "/D0/D5"); + + /* Check for errors... */ + if (status != FX_ALREADY_CREATED) + { + + /* Error creating same directory twice. Return to caller. */ + printf("ERROR!\n"); + test_control_return(10); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (directory_write_interrupt != FX_TRUE) || (error_counter)) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation. */ + driver_write_callback = my_driver_write; + + /* Rename the directory "/D0/D4" to "/D0/D5". */ + fx_directory_rename(&ram_disk, "/D0/D4", "/D0/D5"); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the Directory write operation after record the redo log. */ + if ((sector_type == FX_DIRECTORY_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) == FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + directory_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} + +#ifndef FX_STANDALONE_ENABLE +/* Define the FAT12, 16 and 32 directory traversal routine for the local path test. */ +static void traverse_directory(CHAR *directory_name) +{ + +FX_LOCAL_PATH local_path; +FX_FILE file; +UINT status; +CHAR name[300]; +UINT skip; + + + /* Determine if we are at the start. */ + if (directory_name == FX_NULL) + { + status = fx_directory_local_path_set(&ram_disk, &local_path, "\\"); + skip = 0; + } + else + { + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name); + skip = 2; + } + + /* Get the first directory entry in the root path. */ + status = fx_directory_first_entry_find(&ram_disk, name); + + /* Loop through the directory entries for this path. */ + while (status == FX_SUCCESS) + { + + /* Compare with what is expected. */ + if (strcmp(name, expected_name[expected_index++])) + error_counter++; + + /* Determine if this name is a directory or a file. */ + status = fx_directory_name_test(&ram_disk, name); + + /* What is it? */ + if (status == FX_NOT_DIRECTORY) + { + + /* This is a file, open it to test its integrity. */ + status = fx_file_open(&ram_disk, &file, name, FX_OPEN_FOR_READ); + if (status != FX_SUCCESS) + error_counter++; + status = fx_file_close(&file); + if (status != FX_SUCCESS) + error_counter++; + + /* Pickup the next directory entry. */ + status = fx_directory_next_entry_find(&ram_disk, name); + } + else if (status == FX_SUCCESS) + { + + /* Skip the first two entries... */ + if (skip) + skip--; + else + { + /* Recursive call to traverse directory. */ + traverse_directory(name); + + /* Restore path. */ + status = fx_directory_local_path_restore(&ram_disk, &local_path); + } + + status = fx_directory_next_entry_find(&ram_disk, name); + } + } + fx_directory_local_path_clear(&ram_disk); +} + +#else + +static void traverse_directory(CHAR *directory_name) +{ +UINT status; +CHAR name[300]; + + /* Pickup the first entry in the root directory. */ + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, "A0")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "B0")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "C0")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "D0")) + status++; + + /* Now read one past the number of entries in order to get an error condition. */ + status += fx_directory_next_entry_find(&ram_disk, name); + + /* Determine if the test was successful. */ + if (status != FX_NO_MORE_ENTRIES) + { + + printf("ERROR!\n"); + test_control_return(12); + } + /* Okay, now postion to the deepest sub-directory and ensure that the directory first/next + operations work properly there as well. */ + + /* Set the current path */ + status = fx_directory_default_set(&ram_disk, "/D0/D4"); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error setting the path. Return to caller. */ + printf("ERROR!\n"); + test_control_return(16); + } + + /* Pickup the first entry in this sub-sub directory. */ + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, ".")) + status++; + + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "..")) + status++; + + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "D00")) + status++; + + /* Now read one past the number of entries in order to get an error condition. */ + status += fx_directory_next_entry_find(&ram_disk, name); + + /* Determine if the test was successful. */ + if (status != FX_NO_MORE_ENTRIES) + { + + printf("ERROR!\n"); + test_control_return(18); + } + +} + +#endif +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_directory_rename_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Dir Rename DIR Interrupt Test...........N/A\n"); + + test_control_return(255); +} +#endif diff --git a/test/regression_test/filex_fault_tolerant_directory_rename_redo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_directory_rename_redo_log_interrupt_test.c new file mode 100644 index 0000000..38a593f --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_directory_rename_redo_log_interrupt_test.c @@ -0,0 +1,690 @@ +/* This FileX test concentrates on the Fault-Tolerant redo log write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check redo log interrupt for fx_directory_rename(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create directories and sub-directories; +Step4: Traverse the directory; +Step5: Create new thread to rename directory from "/D0/D4" to "/D0/D5"; +Step6: Terminate the new thread to simulate poweroff when update the redo log. +Step7: Open the media; +Step8: Enable fault tolerant feature to recover the data(undo operation); +Step9: Create directory "/D0/D4"; +Step10: Create directory "/D0/D5"; +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_directory_rename_redo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static UCHAR *pointer; + +#define TEST_COUNT 3 + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_counter = 0; +static UINT log_write_interrupt = FX_FALSE; +static UINT expected_index = 0; +static CHAR * expected_name[] = { + + "A0", + ".", + "..", + "A1", + ".", + "..", + "A00", + ".", + "..", + "A2", + ".", + "..", + "A3", + ".", + "..", + "B0", + ".", + "..", + "B1", + ".", + "..", + "B2", + ".", + "..", + "B00", + ".", + "..", + "B3", + ".", + "..", + "C0", + ".", + "..", + "C1", + ".", + "..", + "C2", + ".", + "..", + "C3", + ".", + "..", + "C00", + ".", + "..", + "D0", + ".", + "..", + "D1", + ".", + "..", + "D2", + ".", + "..", + "D3", + ".", + "..", + "D4", + ".", + "..", + "D00", + ".", + "..", + "END"}; + + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +static void traverse_directory(CHAR *directory_name); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_directory_rename_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Dir Rename Redo LOG Interrupt Test......"); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a series of directories... */ + status = fx_directory_create(&ram_disk, "/A0"); + status += fx_directory_create(&ram_disk, "/B0"); + status += fx_directory_create(&ram_disk, "/C0"); + status += fx_directory_create(&ram_disk, "/D0"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(4); + } + + /* Create the next level of sub-directories. */ + status = fx_directory_create(&ram_disk, "/A0/A1"); + status += fx_directory_create(&ram_disk, "/A0/A2"); + status += fx_directory_create(&ram_disk, "/A0/A3"); + + status += fx_directory_create(&ram_disk, "/B0/B1"); + status += fx_directory_create(&ram_disk, "/B0/B2"); + status += fx_directory_create(&ram_disk, "/B0/B3"); + + status += fx_directory_create(&ram_disk, "/C0/C1"); + status += fx_directory_create(&ram_disk, "/C0/C2"); + status += fx_directory_create(&ram_disk, "/C0/C3"); + + status += fx_directory_create(&ram_disk, "/D0/D1"); + status += fx_directory_create(&ram_disk, "/D0/D2"); + status += fx_directory_create(&ram_disk, "/D0/D3"); + status += fx_directory_create(&ram_disk, "/D0/D4"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(5); + } + + /* Now create the third level of sub-directories... */ + status = fx_directory_create(&ram_disk, "/A0/A1/A00"); + status += fx_directory_create(&ram_disk, "/B0/B2/B00"); + status += fx_directory_create(&ram_disk, "/C0/C3/C00"); + status += fx_directory_create(&ram_disk, "/D0/D4/D00"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(6); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + + /* Traverse the directory structure recursively. */ + traverse_directory(FX_NULL); + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(7); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Attempt to create the same directory again. */ + status = fx_directory_create(&ram_disk, "/D0/D4"); + + /* Check for errors... */ + if (status != FX_ALREADY_CREATED) + { + + /* Error creating same directory twice. Return to caller. */ + printf("ERROR!\n"); + test_control_return(9); + } + + /* Attempt to create the "/D0/D5". */ + status = fx_directory_create(&ram_disk, "/D0/D5"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating same directory twice. Return to caller. */ + printf("ERROR!\n"); + test_control_return(10); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (log_write_interrupt != FX_TRUE) || (error_counter)) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Rename the directory "/D0/D4" to "/D0/D5". */ + fx_directory_create(&ram_disk, "/D0/D5"); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the redo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} + +#ifndef FX_STANDALONE_ENABLE +/* Define the FAT12, 16 and 32 directory traversal routine for the local path test. */ +static void traverse_directory(CHAR *directory_name) +{ + +FX_LOCAL_PATH local_path; +FX_FILE file; +UINT status; +CHAR name[300]; +UINT skip; + + + /* Determine if we are at the start. */ + if (directory_name == FX_NULL) + { + status = fx_directory_local_path_set(&ram_disk, &local_path, "\\"); + skip = 0; + } + else + { + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name); + skip = 2; + } + + /* Get the first directory entry in the root path. */ + status = fx_directory_first_entry_find(&ram_disk, name); + + /* Loop through the directory entries for this path. */ + while (status == FX_SUCCESS) + { + + /* Compare with what is expected. */ + if (strcmp(name, expected_name[expected_index++])) + error_counter++; + + /* Determine if this name is a directory or a file. */ + status = fx_directory_name_test(&ram_disk, name); + + /* What is it? */ + if (status == FX_NOT_DIRECTORY) + { + + /* This is a file, open it to test its integrity. */ + status = fx_file_open(&ram_disk, &file, name, FX_OPEN_FOR_READ); + if (status != FX_SUCCESS) + error_counter++; + status = fx_file_close(&file); + if (status != FX_SUCCESS) + error_counter++; + + /* Pickup the next directory entry. */ + status = fx_directory_next_entry_find(&ram_disk, name); + } + else if (status == FX_SUCCESS) + { + + /* Skip the first two entries... */ + if (skip) + skip--; + else + { + /* Recursive call to traverse directory. */ + traverse_directory(name); + + /* Restore path. */ + status = fx_directory_local_path_restore(&ram_disk, &local_path); + } + + status = fx_directory_next_entry_find(&ram_disk, name); + } + } + fx_directory_local_path_clear(&ram_disk); +} + +#else + +static void traverse_directory(CHAR *directory_name) +{ +UINT status; +CHAR name[300]; + + /* Pickup the first entry in the root directory. */ + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, "A0")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "B0")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "C0")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "D0")) + status++; + + /* Now read one past the number of entries in order to get an error condition. */ + status += fx_directory_next_entry_find(&ram_disk, name); + + /* Determine if the test was successful. */ + if (status != FX_NO_MORE_ENTRIES) + { + + printf("ERROR!\n"); + test_control_return(12); + } + /* Okay, now postion to the deepest sub-directory and ensure that the directory first/next + operations work properly there as well. */ + + /* Set the current path */ + status = fx_directory_default_set(&ram_disk, "/D0/D4"); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error setting the path. Return to caller. */ + printf("ERROR!\n"); + test_control_return(16); + } + + /* Pickup the first entry in this sub-sub directory. */ + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, ".")) + status++; + + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "..")) + status++; + + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "D00")) + status++; + + /* Now read one past the number of entries in order to get an error condition. */ + status += fx_directory_next_entry_find(&ram_disk, name); + + /* Determine if the test was successful. */ + if (status != FX_NO_MORE_ENTRIES) + { + + printf("ERROR!\n"); + test_control_return(18); + } + +} + +#endif + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_directory_rename_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Dir Rename Redo LOG Interrupt Test......N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_enable_1_test.c b/test/regression_test/filex_fault_tolerant_enable_1_test.c new file mode 100644 index 0000000..02948eb --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_enable_1_test.c @@ -0,0 +1,491 @@ +/* In this test point: */ +/* Send null pointer to generate an error in fxe_fault_tolerant_enable */ +/* Register an invalid cluster number for fault_tolerant log file and enable the Fault-tolerant feature. */ +/* Register an invalid cluster number for fault_tolerant log file and enable the Fault-tolerant feature, but 0xffffffff this time. */ +/* Enable fault tolerant and make an IO ERROR while reading the FAT entry log file. */ +/* Enable fault tolerant and make an IO ERROR while reading log file. */ +/* Insert an ucorrect FAT information in cache before enable fault tolerant feature. */ +/* Modify header checksum in log file before enable fault tolerant. */ +/* Modify fat chain checksum in log file before enable fault tolerant. */ +/* Make an IO ERROR in fx_fault_tolerant_create_log_file while reading FAT entries. */ +/* Make an IO ERROR in fx_fault_tolerant_create_log_file while writiing FAT entries in the method of FX_UTILITY_FAT_ENTRY_WRITE_EXTENSION. */ +/* Make an IO ERROR in fx_fault_tolerant_create_log_file while writing start cluster of log file into boot sector. */ +/* Set fx_media_cluster_search_start to enforce filesystem to search wrapping the disk for a free cluster for log file. */ +/* Access all the fat chain to get the last free cluster for log file and filesystem set fx_media_cluster_search_start as FX_FAT_ENTRY_START. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#include "tx_thread.h" +#include "tx_timer.h" +#endif +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_fault_tolerant.h" +#include +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_enable_1_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_enable_1_test_application_define(void *first_unused_memory) +#endif +{ + + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE + 1024; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UCHAR buffer[2048]; +ULONG clusters; +ULONG bytes_per_cluster; +FX_FILE my_file; +FX_LOCAL_PATH local_path; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Enable 1 Test..........................."); + + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + + /* send null pointer to generate an error */ + status = fx_fault_tolerant_enable( FX_NULL, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_PTR_ERROR); + + /* send null pointer to generate an error */ + status = fx_fault_tolerant_enable( &ram_disk, FX_NULL, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_PTR_ERROR); + +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Register an invalid cluster number for fault_tolerant log file and enable the Fault-tolerant feature. */ + _fx_utility_32_unsigned_write((UCHAR *)ram_disk.fx_media_driver_info + FX_FAULT_TOLERANT_BOOT_INDEX, (ULONG)1); + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Register an invalid cluster number for fault_tolerant log file and enable the Fault-tolerant feature, but 0xffffffff this time. */ + _fx_utility_32_unsigned_write((UCHAR *)ram_disk.fx_media_driver_info + FX_FAULT_TOLERANT_BOOT_INDEX, (ULONG)0xffffffff); + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 12, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Register an invalid cluster number for fault_tolerant log file and enable the Fault-tolerant feature, but the last FAT entry does not link to fat last. */ + _fx_utility_32_unsigned_write((UCHAR *)ram_disk.fx_media_driver_info + FX_FAULT_TOLERANT_BOOT_INDEX, (ULONG)2); + _fx_utility_FAT_entry_write( &ram_disk, 2, 3); + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media with FAT16 and enable fault tolerant. */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open( &ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + status += fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + status += fx_media_close( &ram_disk); + status += fx_media_open( &ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Enable fault tolerant and make an IO ERROR while reading the FAT entry log file. */ + _fx_ram_driver_io_error_request = 2; + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_IO_ERROR); + + /* Enable fault tolerant and make an IO ERROR while reading log file. */ + _fx_ram_driver_io_error_request = 3; + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_IO_ERROR); + + /* Insert an ucorrect FAT information in cache. */ + ram_disk.fx_media_fat_cache[8].fx_fat_cache_entry_cluster = 2; + ram_disk.fx_media_fat_cache[8].fx_fat_cache_entry_value = 5; + + /* Enable fault tolerant still successfully but original log file is lost. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media with FAT16 and enable fault tolerant. */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open( &ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + status += fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + status += fx_media_close( &ram_disk); + status += fx_media_open( &ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* By default, log file locate at the first sector of user area. So we change the data in the log file to make checksum a non zero number. */ + _fx_utility_logical_sector_read( &ram_disk, 1 + ram_disk.fx_media_sectors_per_FAT + ram_disk.fx_media_root_sectors, buffer, (ULONG64)1, FX_DATA_SECTOR); + + /* Modify header checksum. */ + buffer[5]++; + + /* Write out our modification. */ + _fx_utility_logical_sector_write( &ram_disk, 1 + ram_disk.fx_media_sectors_per_FAT + ram_disk.fx_media_root_sectors, buffer, (ULONG64)1, FX_DATA_SECTOR); + + /* Access our log file now. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + status = fx_media_open( &ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Modify log file again, but fat chain this time. */ + _fx_utility_logical_sector_read( &ram_disk, 1 + ram_disk.fx_media_sectors_per_FAT + ram_disk.fx_media_root_sectors, buffer, (ULONG64)1, FX_DATA_SECTOR); + + /* Modify FAT chain checksum. */ + buffer[FX_FAULT_TOLERANT_LOG_HEADER_SIZE]++; + _fx_utility_logical_sector_write( &ram_disk, 1 + ram_disk.fx_media_sectors_per_FAT + ram_disk.fx_media_root_sectors, buffer, (ULONG64)1, FX_DATA_SECTOR); + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media with FAT16 and enable fault tolerant. */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open( &ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Make an IO ERROR in fx_fault_tolerant_create_log_file while reading FAT entries. */ + _fx_ram_driver_io_error_request = 2; + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_IO_ERROR); + + /* Make an IO ERROR in fx_fault_tolerant_create_log_file while writiing FAT entries in the method of FX_UTILITY_FAT_ENTRY_WRITE_EXTENSION. */ + /* Honestly, _fx_utility_FAT_entry_write will not call driver at this particular position. */ + _fx_utility_fat_entry_write_error_request = 1; + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_IO_ERROR); + + /* Make an IO ERROR in fx_fault_tolerant_create_log_file while writing start cluster of log file into boot sector. */ + _fx_ram_driver_io_error_request = 3; + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_BOOT_ERROR); + + /* Make an IO ERROR in fx_fault_tolerant_create_log_file while writiing FAT entries in the method of FX_UTILITY_FAT_ENTRY_WRITE_EXTENSION. */ + /* Honestly, _fx_utility_FAT_entry_write will not call driver at this particular position. */ + _fx_utility_fat_entry_write_error_request = 2; + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_IO_ERROR); + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media with FAT16 and enable fault tolerant. */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open( &ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Create files in a subdir to fill user data area. */ + status = fx_directory_create( &ram_disk, "root"); +#ifndef FX_STANDALONE_ENABLE + status += fx_directory_local_path_set( &ram_disk, &local_path, "root"); +#else + status += fx_directory_default_set(&ram_disk, "/root"); +#endif + /* Fill all space so that there is no space left for fault tolerant log file. */ + status += fx_file_create( &ram_disk, "dev"); + status += fx_file_open( &ram_disk, &my_file, "dev", FX_OPEN_FOR_WRITE); + status += fx_file_allocate( &my_file, 256 * 8 * 4000); + status += fx_file_close( &my_file); + status += fx_file_create( &ram_disk, "src"); + status += fx_file_open( &ram_disk, &my_file, "src", FX_OPEN_FOR_WRITE); + status += fx_file_allocate( &my_file, 256 * 8 * 194); + return_if_fail( status == FX_SUCCESS); + + /* No space is left! */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_NO_MORE_SPACE); + + /* Delete a file to enable fault tolerant. */ + status = fx_file_delete( &ram_disk, "dev"); + return_if_fail( status == FX_SUCCESS); + + /* Set cluster_search_start as a cluster which is occupied by "src" to enforce filesystem to search wrapping the disk for a free cluster. */ + ram_disk.fx_media_cluster_search_start = 4100; + status += fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media with FAT16 and enable fault tolerant. */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open( &ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Create files in a subdir to fill user data area. */ + status = fx_directory_create( &ram_disk, "root"); +#ifndef FX_STANDALONE_ENABLE + status += fx_directory_local_path_set( &ram_disk, &local_path, "/root"); +#else + status += fx_directory_default_set(&ram_disk, "/root"); +#endif + return_if_fail( status == FX_SUCCESS); + + bytes_per_cluster = ram_disk.fx_media_bytes_per_sector * ram_disk.fx_media_sectors_per_cluster; + clusters = (FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + bytes_per_cluster - 1) / bytes_per_cluster; + + /* Fill user data area. Only the last cluster is left. */ + status = fx_file_create( &ram_disk, "dev"); + status += fx_file_open( &ram_disk, &my_file, "dev", FX_OPEN_FOR_WRITE); + status += fx_file_allocate( &my_file, 256 * 8 * (4194 - clusters)); + return_if_fail( status == FX_SUCCESS); + + /* Access all the fat chain to get the last free cluster for log file and filesystem set fx_media_cluster_search_start as FX_FAT_ENTRY_START. */ + status = fx_fault_tolerant_enable( &ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Enable fault tolerant with buffer size larger than required. */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 4, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Create files in a subdir to fill user data area. */ + status += fx_media_open( &ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + status = fx_fault_tolerant_enable( &ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE + 1024); + return_if_fail( status == FX_SUCCESS); + + return_if_fail(ram_disk.fx_media_fault_tolerant_memory_buffer_size == FAULT_TOLERANT_SIZE); + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_enable_1_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Enable 1 Test...........................N/A\n"); + + test_control_return(255); +} +#endif diff --git a/test/regression_test/filex_fault_tolerant_enable_2_test.c b/test/regression_test/filex_fault_tolerant_enable_2_test.c new file mode 100644 index 0000000..5e0edf7 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_enable_2_test.c @@ -0,0 +1,425 @@ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#include "tx_thread.h" +#include "tx_timer.h" +#endif +#include "fx_api.h" +#include "fx_unicode.h" +#include "fx_utility.h" +#include "fx_fault_tolerant.h" +#include +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_enable_2_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static CHAR read_buffer[10240]; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_enable_2_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +FX_DIR_ENTRY dir_entry1, dir_entry2; +ULONG uentry; +ULONG unicode_size; +UINT status, length; +ULONG actual; +UCHAR buffer[2048]; +CHAR destination_name[100]; +UCHAR unicode_name_A[] = { 'A', 0, 0, 0}; +UCHAR unicode_name_1[] = { 1, 0, 0, 0}; +UCHAR unicode_name_2[] = { 1, 0, 1, 0, 0, 0}; +UCHAR unicode_name_A1[] = { 'A', 0, 1, 0, 0, 0}; +UCHAR *source_buffer; +USHORT log_length; +FX_FILE my_file; +ULONG clusters; +ULONG bytes_per_cluster; +ULONG cluster; +FX_LOCAL_PATH local_path; +FX_FAULT_TOLERANT_FAT_CHAIN *FAT_chain; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Enable 2 Test..........................."); + + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Enable fault tolerant. */ + status = fx_fault_tolerant_enable( &ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + status = fx_file_create( &ram_disk, "TEST.TXT"); + status += fx_file_open( &ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_allocate( &my_file, 2048); + return_if_fail( status == FX_SUCCESS); + + /* Set the state of fault tolerant as FX_FAULT_TOLERANT_STATE_STARTED and add an extra transaction record. */ + ram_disk.fx_media_fault_tolerant_state = FX_FAULT_TOLERANT_STATE_STARTED; + ram_disk.fx_media_fault_tolerant_transaction_count = 1; + + /* Overflow the size of log file. */ + source_buffer = fault_tolerant_buffer + FX_FAULT_TOLERANT_LOG_CONTENT_OFFSET + FX_FAULT_TOLERANT_LOG_CONTENT_HEADER_SIZE; + log_length = (USHORT)_fx_utility_16_unsigned_read( source_buffer + 2); + _fx_utility_16_unsigned_write( source_buffer + 2, 0xffff); + status = fx_file_truncate_release( &my_file, 26); + return_if_fail( status == FX_FILE_CORRUPT); + + /* Reset the log file to revert our modification and close transaction. */ + _fx_fault_tolerant_reset_log_file( &ram_disk); + status = fx_file_truncate_release( &my_file, 26); + return_if_fail( status == FX_SUCCESS); + + /* Deal with the tail. */ + status = fx_file_close( &my_file); + status += fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Enable fault tolerant. */ + status = fx_fault_tolerant_enable( &ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + length = fx_unicode_length_get( unicode_name_A); + status = fx_unicode_directory_create( &ram_disk, unicode_name_A, length, destination_name); +#ifndef FX_STANDALONE_ENABLE + status += fx_directory_local_path_set( &ram_disk, &local_path, destination_name); +#else + status += fx_directory_default_set(&ram_disk, destination_name); +#endif + return_if_fail( status == FX_SUCCESS); + + /* Create a lot of directries which makes the first level subdir occupy space more than a single cluster, which will leave fat logs in log file. */ + + /* For our special implement of fx_unicode_xx_create, we can only 26 files/directories with the same length of unicode name. */ + for ( UINT i = 0; i < 26; i++) + { + length = fx_unicode_length_get( unicode_name_1); + status = fx_unicode_directory_create( &ram_disk, unicode_name_1, length, destination_name); + return_if_fail( status == FX_SUCCESS); + unicode_name_1[0]++; + } + + /* When more directories is attempted to be created, fat logs will be read in fx_directory_search and fx_directory_free_search. */ + for ( UINT i = 0; i < 6; i++) + { + length = fx_unicode_length_get( unicode_name_2); + status = fx_unicode_directory_create( &ram_disk, unicode_name_2, length, destination_name); + return_if_fail( status == FX_SUCCESS); + unicode_name_2[0]++; + } + + /* Make an I/O error while _fx_fault_tolerant_apply_log is calling _fx_utility_FAT_entry_write. */ + _fx_utility_fat_entry_write_error_request = 1; + status = fx_unicode_directory_create( &ram_disk, unicode_name_2, length, destination_name); + return_if_fail( status == FX_IO_ERROR); + + /* Make an I/O error while _fx_fault_tolerant_apply_log is calling _fx_utility_logical_sector_read. */ + _fx_utility_logical_sector_read_error_request = 339; + status = fx_unicode_directory_create( &ram_disk, unicode_name_2, length, destination_name); + return_if_fail( status == FX_IO_ERROR); + + /* Make an I/O error while _fx_fault_tolerant_apply_log is calling _fx_utility_logical_sector_write. */ + _fx_utility_logical_sector_write_error_request = 4; + status = fx_unicode_directory_create( &ram_disk, unicode_name_2, length, destination_name); + return_if_fail( status == FX_IO_ERROR); + +#ifndef FX_DISABLE_CACHE + /* Make an I/O error while _fx_fault_tolerant_apply_log is calling _fx_utility_logical_sector_flush. */ + _fx_utility_logical_sector_flush_error_request = 6; + status = fx_unicode_directory_create( &ram_disk, unicode_name_2, length, destination_name); + return_if_fail( status == FX_IO_ERROR); +#endif + + /* Start transaction manually. */ + ram_disk.fx_media_fault_tolerant_state = FX_FAULT_TOLERANT_STATE_STARTED; + ram_disk.fx_media_fault_tolerant_transaction_count = 1; + source_buffer = fault_tolerant_buffer + FX_FAULT_TOLERANT_LOG_CONTENT_OFFSET + FX_FAULT_TOLERANT_LOG_CONTENT_HEADER_SIZE; + + /* Modify the length of the log to overflow the size of log file, which will be caught by _fx_fault_tolerant_apply_logs. */ + log_length = (USHORT)_fx_utility_16_unsigned_read( source_buffer + 2); + _fx_utility_16_unsigned_write( source_buffer + 2, 0xffff); + status = _fx_fault_tolerant_transaction_end( &ram_disk); + return_if_fail( status == FX_FILE_CORRUPT); + + /* Recover log length. */ + _fx_utility_16_unsigned_write( source_buffer + 2, log_length); + + /* Start transaction manually. */ + ram_disk.fx_media_fault_tolerant_state = FX_FAULT_TOLERANT_STATE_STARTED; + ram_disk.fx_media_fault_tolerant_transaction_count = 1; + source_buffer = fault_tolerant_buffer + FX_FAULT_TOLERANT_LOG_CONTENT_OFFSET + FX_FAULT_TOLERANT_LOG_CONTENT_HEADER_SIZE; + + /* Modify the type of the log as an undefined number, which will be caught by _fx_fault_tolerant_apply_logs. */ + _fx_utility_16_unsigned_write( source_buffer, 0xffff); + status = _fx_fault_tolerant_transaction_end( &ram_disk); + return_if_fail( status == FX_SECTOR_INVALID); + + /* The size of a dir log is larger than a sector which is invalid, which will be caught by _fx_fault_tolerant_apply_logs. */ + _fx_utility_16_unsigned_write( ram_disk.fx_media_fault_tolerant_memory_buffer + 4, 0xffff); + source_buffer = ram_disk.fx_media_fault_tolerant_memory_buffer + FX_FAULT_TOLERANT_LOG_CONTENT_OFFSET + FX_FAULT_TOLERANT_LOG_CONTENT_HEADER_SIZE; + _fx_utility_16_unsigned_write( source_buffer, FX_FAULT_TOLERANT_DIR_LOG_TYPE); + _fx_utility_16_unsigned_write( source_buffer + 2, 4096); + _fx_utility_32_unsigned_write( source_buffer + 4, 0); /* sector_offset = 0 */ + _fx_utility_64_unsigned_write( source_buffer + 8, 0); /* log sector = 0 */ + status = _fx_fault_tolerant_apply_logs( &ram_disk); + return_if_fail( status == FX_FILE_CORRUPT); + + /* _fx_fault_tolerant_add_dir_log always return FX_SUCCESS when data_size(the fourth argument) is zero. */ + status = _fx_fault_tolerant_add_dir_log( &ram_disk, 0, 0, FX_NULL, 0); + return_if_fail( status == FX_SUCCESS); + + /* The size of a log overflow the size of log file. */ + ram_disk.fx_media_fault_tolerant_total_logs = 1; + source_buffer = ram_disk.fx_media_fault_tolerant_memory_buffer + FX_FAULT_TOLERANT_LOG_CONTENT_OFFSET + FX_FAULT_TOLERANT_LOG_CONTENT_HEADER_SIZE; + _fx_utility_16_unsigned_write( source_buffer + 2, 0xffff); + status = _fx_fault_tolerant_read_directory_sector( &ram_disk, 0, FX_NULL, 0); + return_if_fail( status == FX_FILE_CORRUPT); + + /* The size of a dir log is larger than a sector which is invalid. */ + ram_disk.fx_media_fault_tolerant_file_size = 1024; + ram_disk.fx_media_fault_tolerant_total_logs = 1; + source_buffer = ram_disk.fx_media_fault_tolerant_memory_buffer + FX_FAULT_TOLERANT_LOG_CONTENT_OFFSET + FX_FAULT_TOLERANT_LOG_CONTENT_HEADER_SIZE; + _fx_utility_16_unsigned_write( source_buffer, FX_FAULT_TOLERANT_DIR_LOG_TYPE); + _fx_utility_16_unsigned_write( source_buffer + 2, 512); + _fx_utility_32_unsigned_write( source_buffer + 4, 0); /* sector_offset = 0 */ + _fx_utility_64_unsigned_write( source_buffer + 8, 0); /* log sector = 0 */ + status = _fx_fault_tolerant_read_directory_sector( &ram_disk, 0, FX_NULL, 1); + return_if_fail( status == FX_FILE_CORRUPT); + + /* The size of a log overflow the size of log file. */ + ram_disk.fx_media_fault_tolerant_total_logs = 1; + ram_disk.fx_media_fault_tolerant_state |= FX_FAULT_TOLERANT_STATE_STARTED; + source_buffer = ram_disk.fx_media_fault_tolerant_memory_buffer + FX_FAULT_TOLERANT_LOG_CONTENT_OFFSET + FX_FAULT_TOLERANT_LOG_CONTENT_HEADER_SIZE; + _fx_utility_16_unsigned_write( source_buffer + 2, 0xffff); + status = _fx_utility_logical_sector_read( &ram_disk, 2, ram_disk.fx_media_memory_buffer, 1, FX_DIRECTORY_SECTOR); + return_if_fail( status == FX_FILE_CORRUPT); + + _fx_fault_tolerant_recover( &ram_disk); + + /* Make a mistake in fx_utility_sector_read.c the last time calling _fx_utility_logical_sector_cache_entry_read. */ + _fx_utility_logical_sector_read_1_error_request = 1; + status = _fx_utility_logical_sector_read( &ram_disk, 99, buffer, 1, FX_DIRECTORY_SECTOR); + return_if_fail( status == FX_SUCCESS); + + status = fx_file_create( &ram_disk, "test.txt"); + status += fx_file_open( &ram_disk, &my_file, "test.txt", FX_OPEN_FOR_WRITE); + status += fx_file_allocate( &my_file, 2048 * 3); + status += fx_file_close( &my_file); + return_if_fail( status == FX_SUCCESS); + + /* Register a bad fat entry in cache for the last cluster of the file. */ + ram_disk.fx_media_fat_cache[28].fx_fat_cache_entry_cluster = 39; + ram_disk.fx_media_fat_cache[28].fx_fat_cache_entry_value = 1; + + /* File corruption is found while calling fx_file_open. */ + status = fx_file_open( &ram_disk, &my_file, "test.txt", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_FILE_CORRUPT); + + status = fx_file_create( &ram_disk, "aa"); + status += fx_file_open( &ram_disk, &my_file, "aa", FX_OPEN_FOR_WRITE); + status += fx_file_write( &my_file, read_buffer, 2048 * 3); + return_if_fail( status == FX_SUCCESS); + + bytes_per_cluster = ram_disk.fx_media_bytes_per_sector * ram_disk.fx_media_sectors_per_cluster; + clusters = (FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + bytes_per_cluster - 1) / bytes_per_cluster; + cluster = 40 + clusters; + +#ifndef FX_DISABLE_CACHE + /* Register a bad fat entry in cache in the middle of the file. */ + ram_disk.fx_media_fat_cache[(cluster & FX_FAT_CACHE_HASH_MASK) * FX_FAT_CACHE_DEPTH].fx_fat_cache_entry_cluster = cluster; + ram_disk.fx_media_fat_cache[(cluster & FX_FAT_CACHE_HASH_MASK) * FX_FAT_CACHE_DEPTH].fx_fat_cache_entry_value = 0xffff; + status += fx_file_seek( &my_file, 2047); + status += fx_file_read( &my_file, read_buffer, 5, &actual); + return_if_fail( status == FX_FILE_CORRUPT); +#endif + + status = fx_file_close( &my_file); + return_if_fail( status == FX_SUCCESS); + + status = fx_file_create( &ram_disk, "tt"); + status += fx_file_open( &ram_disk, &my_file, "tt", FX_OPEN_FOR_WRITE); + status += fx_file_allocate( &my_file, 2048 * 3); + return_if_fail( status == FX_SUCCESS); + + /* Attempt to cover the branch at the end of fx_file_extended_truncate */ + /* that size >= file_ptr -> fx_file_maximum_size_used. */ + my_file.fx_file_maximum_size_used = 0; + status = fx_file_extended_truncate( &my_file, 2048); + status += fx_file_close( &my_file); + return_if_fail( status == FX_SUCCESS); + + status = fx_file_create( &ram_disk, "test"); + status += fx_file_open( &ram_disk, &my_file, "test", FX_OPEN_FOR_WRITE); + status += fx_file_allocate( &my_file, 2048); + return_if_fail( status == FX_SUCCESS); + + /* Cover the branch in fx_file_extended_truncate_release that fx_file_current_offset <= fx_file_current_file_size. */ + status = fx_file_seek( &my_file, 0); + status += fx_file_extended_truncate_release( &my_file, 1024); + return_if_fail( status == FX_SUCCESS); + + /* Just cover the branch of FX_UTILITY_LOGICAL_SECTOR_READ_EXTENSION_1. */ + _fx_utility_logical_sector_read_1_error_request = 2; + status = _fx_utility_logical_sector_read( &ram_disk, 99, buffer, 1, FX_DIRECTORY_SECTOR); + + /* Attempt to cover the last branch in _fx_unicode_directory_entry_read that cluster < FX_FAT_ENTRY_START. */ + uentry = 129; + dir_entry1.fx_dir_entry_cluster = 2; + unicode_size = fx_unicode_length_get( unicode_name_A1); + + /* Register an uncorrent fat cache to satisfy cluster < FX_FAT_ENTRY_START. */ + ram_disk.fx_media_fat_cache[8].fx_fat_cache_entry_cluster = 2; + ram_disk.fx_media_fat_cache[8].fx_fat_cache_entry_value = 1; + + /* Make an IO error to return from the function earlier avoiding core dump. */ + _fx_utility_logical_sector_read_error_request = 1; + status = _fx_unicode_directory_entry_read( &ram_disk, &dir_entry1, &uentry, &dir_entry2, + unicode_name_A1, &unicode_size); + return_if_fail( status == FX_FILE_CORRUPT); + + /* Modify fault tolerant FAT chain in buffer. */ + FAT_chain = (FX_FAULT_TOLERANT_FAT_CHAIN *)(ram_disk.fx_media_fault_tolerant_memory_buffer + FX_FAULT_TOLERANT_FAT_CHAIN_OFFSET); + _fx_utility_32_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_head_new, 3); + _fx_utility_32_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_next_deletion, ram_disk.fx_media_fat_reserved + 1); + + /* Get fx_fault_tolerant_FAT_chain_next_deletion while looping to cleanup FAT entries. */ + status = _fx_fault_tolerant_cleanup_FAT_chain( &ram_disk, FX_FAULT_TOLERANT_FAT_CHAIN_RECOVER); + return_if_fail( status == FX_SUCCESS); + + /* Modify fault tolerant FAT chain in buffer. */ + FAT_chain = (FX_FAULT_TOLERANT_FAT_CHAIN *)(ram_disk.fx_media_fault_tolerant_memory_buffer + FX_FAULT_TOLERANT_FAT_CHAIN_OFFSET); + _fx_utility_32_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_head_new, 3); + _fx_utility_FAT_entry_write(&ram_disk, 3, 1); + + /* Get fx_fault_tolerant_FAT_chain_next_deletion while looping to cleanup FAT entries. */ + status = _fx_fault_tolerant_cleanup_FAT_chain( &ram_disk, FX_FAULT_TOLERANT_FAT_CHAIN_RECOVER); + return_if_fail( status == FX_SUCCESS); + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_enable_2_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Enable 2 Test...........................N/A\n"); + + test_control_return(255); +} +#endif diff --git a/test/regression_test/filex_fault_tolerant_enable_3_test.c b/test/regression_test/filex_fault_tolerant_enable_3_test.c new file mode 100644 index 0000000..d53e5ad --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_enable_3_test.c @@ -0,0 +1,1082 @@ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#include "tx_thread.h" +#include "tx_timer.h" +#endif +#include "fx_api.h" +#include "fx_directory.h" +#include "fx_utility.h" +#include "fx_fault_tolerant.h" +#include +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_enable_3_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *ram_disk_memory; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static CHAR read_buffer[10240]; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_enable_3_test_application_define(void *first_unused_memory) +#endif +{ + + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + ram_disk_memory = pointer; + pointer += (256 * 128); +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +ULONG64 actual_64; +UINT status, length, new_length, old_length; +ULONG actual; +ULONG temp; +UCHAR buffer[2048]; +CHAR destination_name[100]; +UCHAR unicode_name_A[] = { 'A', 0, 0, 0}; +UCHAR unicode_name_1[] = { 1, 0, 0, 0}; +UCHAR unicode_name_2[] = { 1, 0, 1, 0, 0, 0}; +UCHAR unicode_name_3[] = { 1, 0, 1, 0, 1, 0, 0, 0}; +UCHAR unicode_name_14[] = { 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 0, 0}; +UCHAR unicode_name_A1[] = { 'A', 0, 1, 0, 0, 0}; +UCHAR unicode_name_A3[] = { 'A', 1, 2, 1, 0, 0}; +UCHAR long_unicode_name[300]; +FX_FILE my_file; +FX_LOCAL_PATH local_path; +FX_DIR_ENTRY dir_entry; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Enable 3 Test..........................."); + + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Enable fault tolerant. */ + status = fx_fault_tolerant_enable( &ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Prepare a file for our test. */ + length = fx_unicode_length_get( unicode_name_2); + status = fx_unicode_file_create( &ram_disk, unicode_name_2, length, destination_name); + status += fx_file_open( &ram_disk, &my_file, destination_name, FX_OPEN_FOR_WRITE); + status += fx_file_allocate( &my_file, 2048); + status += fx_file_seek( &my_file, 2048); + return_if_fail( status == FX_SUCCESS); + + /* Write some data append to the new file, which will call fx_fault_tolerant_cleanup_FAT_chain */ + /* Make IO error while reading FAT chain. */ + _fx_utility_fat_entry_read_error_request = 2; + status += fx_file_write( &my_file, "1234567890", 10); + return_if_fail( status == FX_IO_ERROR); + + status = fx_file_close( &my_file); + return_if_fail( status == FX_SUCCESS); + + /* Make IO error while _fx_fault_tolerant_recover is calling _fx_fault_tolerant_cleanup_FAT_chain. */ + ram_disk.fx_media_driver_write_protect = FX_TRUE; + /*_fx_utility_fat_entry_read_error_request = 1;*/ + status = fx_file_rename(&ram_disk, "MYTEST", "OURTEST"); + ram_disk.fx_media_driver_write_protect = FX_FALSE; + return_if_fail( status == FX_WRITE_PROTECT); + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors - FAT32 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + return_if_fail( status == FX_SUCCESS); + + /* Now create a series to sub-directories to expand the root directory FAT chain. */ + status = fx_file_create(&ram_disk, "FILE1"); + + /* Open the file. */ + status += fx_file_open(&ram_disk, &my_file, "FILE1", FX_OPEN_FOR_WRITE); + + /* Write to the file. */ + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + return_if_fail( status == FX_SUCCESS); + + /* Make an IO error while flushing dirty buffer in fx_utility_logical_sector_read.c. */ + _fx_ram_driver_io_error_request = 4; + status = fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + return_if_fail( status == FX_IO_ERROR); + + status = fx_file_close( &my_file); + status += fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors - FAT32 + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Enable fault tolerant. */ + status = fx_fault_tolerant_enable( &ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Attempt to truncate file which is open for read while fault tolerant is enabled. */ + status = fx_file_create( &ram_disk, "FILE2"); + status += fx_file_open( &ram_disk, &my_file, "FILE2", FX_OPEN_FOR_READ); + status += fx_file_truncate_release( &my_file, 0); + return_if_fail( status == FX_ACCESS_ERROR); + + /* Attempt to truncate file while fault tolerant is enabled and the disk is write protected. */ + status = fx_file_close( &my_file); + status += fx_file_open( &ram_disk, &my_file, "FILE2", FX_OPEN_FOR_WRITE); + ram_disk.fx_media_driver_write_protect = 1; + status += fx_file_truncate_release( &my_file, 0); + ram_disk.fx_media_driver_write_protect = 0; + return_if_fail( status == FX_WRITE_PROTECT); + + status = fx_file_allocate( &my_file, 2048 * 2); + return_if_fail( status == FX_SUCCESS); + + /* Make IO error in _fx_directory_entry_write. */ + _fx_utility_logical_sector_read_error_request = 1; + status = fx_file_truncate_release( &my_file, 1000); + return_if_fail( status == FX_IO_ERROR); + + /* Size expected is less the original file size. */ + status = fx_file_truncate_release( &my_file, 2048 * 3); + status += fx_file_seek( &my_file, 2048 * 3); + status += fx_file_truncate_release( &my_file, 2048 * 2); + status += fx_file_close( &my_file); + return_if_fail( status == FX_SUCCESS); + + /* Prepare a file for our test. */ + status = fx_file_create( &ram_disk, "FILE3"); + status += fx_file_open( &ram_disk, &my_file, "FILE3", FX_OPEN_FOR_WRITE); + status += fx_file_allocate( &my_file, 2048 * 3); + return_if_fail( status == FX_SUCCESS); + + /* Make IO error while first reading current cluster entry from FAT chain. */ + _fx_utility_fat_entry_read_error_request = 3; + status = fx_file_truncate_release( &my_file, 2048); + return_if_fail( status == FX_IO_ERROR); + + /* Allocate 5 clusters for our new file. */ + status = fx_file_allocate( &my_file, 5 * 2048); + return_if_fail( status == FX_SUCCESS); + + /* Make IO error while reading more cluster entries from FAT chain. */ + _fx_utility_fat_entry_read_error_request = 4; + status = fx_file_truncate_release( &my_file, 2048); + return_if_fail( status == FX_IO_ERROR); + + /* Allocate 5 clusters for our new file. */ + status = fx_file_allocate( &my_file, 5 * 2048); + return_if_fail( status == FX_SUCCESS); + + /* Make IO error while setting undo log in FAT chain. */ + _fx_utility_logical_sector_write_error_request = 1; + status = fx_file_truncate_release( &my_file, 2048); + return_if_fail( status == FX_IO_ERROR); + + /* Allocate 5 clusters for our new file. */ + status = fx_file_allocate( &my_file, 5 * 2048); + return_if_fail( status == FX_SUCCESS); + + /* Make IO error while setting the end of the FAT chain. */ + _fx_utility_fat_entry_write_error_request = 1; + status = fx_file_truncate_release( &my_file, 2048); + return_if_fail( status == FX_IO_ERROR); + + /* Allocate 5 clusters for our new file. */ + status = fx_file_allocate( &my_file, 5 * 2048); + return_if_fail( status == FX_SUCCESS); + + /* Make IO error while flushing cached FAT entries. */ + _fx_utility_logical_sector_read_error_request = 2; + status = fx_file_truncate_release( &my_file, 2048); + return_if_fail( status == FX_IO_ERROR); + + /* Allocate 5 clusters for our new file. */ + status = fx_file_allocate( &my_file, 5 * 2048); + return_if_fail( status == FX_SUCCESS); + + /* Make IO error at the end of fx_file_truncate_release while writing directory entries to the media. */ + _fx_utility_logical_sector_read_error_request = 3; + status = fx_file_truncate_release( &my_file, 2048); + return_if_fail( status == FX_IO_ERROR); + + /* Allocate 5 clusters for our new file. */ + status = fx_file_allocate( &my_file, 5 * 2048); + return_if_fail( status == FX_SUCCESS); + + /* Make IO error in fx_file_truncate_release while calling _fx_fault_tolerant_transaction_end. */ + _fx_utility_logical_sector_read_error_request = 4; + status = fx_file_truncate_release( &my_file, 2048); + return_if_fail( status == FX_IO_ERROR); + + status = fx_file_close( &my_file); + status += fx_media_close( &ram_disk); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors - FAT32 + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + status = fx_file_create( &ram_disk, "TEST"); + status += fx_file_open( &ram_disk, &my_file, "TEST", FX_OPEN_FOR_WRITE); + status += fx_file_allocate( &my_file, 2048 * 5); + return_if_fail( status == FX_SUCCESS); + + /* Make an IO error at the end of fx_file_extended_truncate_release while calling _fx_utility_FAT_flush. */ + /* which can only be reached when FX_FAULT_TOLERANT is defined but not enabled since fat entries will never be set as dirty while fault tolerant is enabled. */ + /* We add fat log instead of setting fx_fat_cache_entry_dirty while fault tolerant is enabled. */ + _fx_utility_logical_sector_read_error_request = 3; + status = fx_file_truncate_release( &my_file, 2048); + return_if_fail( status == FX_IO_ERROR); + + status = fx_file_close( &my_file); + status += fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors - FAT32 + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Enable fault tolerant. */ + status = fx_fault_tolerant_enable( &ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Make an IO error in fx_directory_create while writing back directory sectors. */ + _fx_utility_logical_sector_write_error_request = 1; + status = fx_directory_create( &ram_disk, "dev"); + return_if_fail( status == FX_IO_ERROR); + +#ifndef FX_DISABLE_CACHE + /* Make an IO error in fx_directory_create while flushing internal logical sector cache. */ + _fx_utility_logical_sector_flush_error_request = 1; + status = fx_directory_create( &ram_disk, "dev"); + return_if_fail( status == FX_IO_ERROR); +#endif + + /* Make an IO error at the end of fx_directory_create while writing back new directory sector. */ + _fx_utility_logical_sector_read_error_request = 10; + status = fx_directory_create( &ram_disk, "dev"); + return_if_fail( status == FX_IO_ERROR); + + status = fx_file_create( &ram_disk, "TEST"); + status += fx_file_open( &ram_disk, &my_file, "TEST", FX_OPEN_FOR_WRITE); + status += fx_file_allocate( &my_file, 2048 * 3); + return_if_fail( status == FX_SUCCESS); + + /* Make an IO error at the end of fx_file_extended_truncate while calling _fx_directory_entry_write. */ + _fx_utility_logical_sector_read_error_request = 1; + status = fx_file_extended_truncate( &my_file, 2048 * 2); + return_if_fail( status == FX_IO_ERROR); + + /* Make an IO error in fx_file_extended_truncate while calling _fx_fault_tolerant_transaction_end. */ + _fx_utility_logical_sector_read_error_request = 2; + status = fx_file_extended_truncate( &my_file, 2048); + return_if_fail( status == FX_IO_ERROR); + + status = fx_file_allocate( &my_file, 2048 * 2); + status += fx_file_close( &my_file); + return_if_fail( status == FX_SUCCESS); + + /* Open the file we just closed which will update fx_file_maximum_size_used of the file. */ + status = fx_file_open( &ram_disk, &my_file, "TEST", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + /* Truncated the file size less than fx_file_maximum_size_used. */ + status = fx_file_extended_truncate( &my_file, 1024); + return_if_fail( status == FX_SUCCESS); + + status = fx_file_close( &my_file); + return_if_fail( status == FX_SUCCESS); + + status = fx_file_create( &ram_disk, "FILE"); + status += fx_file_open( &ram_disk, &my_file, "FILE", FX_OPEN_FOR_WRITE); + status += fx_file_allocate( &my_file, 2048 * 3); + status += fx_file_extended_truncate_release( &my_file, 0); + return_if_fail( status == FX_SUCCESS); + + /* Extend the file which have no clusters, which need to sep FAT chain. */ + status = fx_file_extended_best_effort_allocate( &my_file, 2048 * 4, &actual_64); + return_if_fail( status == FX_SUCCESS); + + /* Make an IO error while writing the directory back to the media. */ + _fx_utility_logical_sector_read_error_request = 7; + status = fx_file_extended_best_effort_allocate( &my_file, 2048 * 5, &actual_64); + return_if_fail( status == FX_IO_ERROR); + +#ifndef FX_DISABLE_CACHE + /* Make an IO error at the end of fx_file_extended_best_effort while flushing sectors. */ + _fx_utility_logical_sector_flush_error_request = 3; + status = fx_file_extended_best_effort_allocate( &my_file, 2048 * 5, &actual_64); + return_if_fail( status == FX_IO_ERROR); +#endif + + /* Make an IO error in fx_unicode_file_create while calling _fx_directory_search. */ + length = fx_unicode_length_get( unicode_name_A); + _fx_utility_logical_sector_read_error_request = 14; + status = fx_unicode_file_create( &ram_disk, unicode_name_A, length, destination_name); + return_if_fail( status == FX_IO_ERROR); + + /* Make an IO error in fx_unicode_directory_create while calling _fx_directory_search. */ + length = fx_unicode_length_get( unicode_name_A); + _fx_utility_logical_sector_read_error_request = 23; + status = fx_unicode_directory_create( &ram_disk, unicode_name_A, length, destination_name); + return_if_fail( status == FX_IO_ERROR); + +#ifndef FX_DISABLE_CACHE + /* Make an IO error in fx_unicode_file_create while calling _fx_unicode_directory_entry_change. */ + length = fx_unicode_length_get( unicode_name_A3); + _fx_utility_logical_sector_read_error_request = 25; + status = fx_unicode_file_create( &ram_disk, unicode_name_A3, length, destination_name); + return_if_fail( status == FX_IO_ERROR); + + /* Make an IO error in fx_unicode_directory_create while calling _fx_unicode_directory_entry_change. */ + unicode_name_A3[0]++; + length = fx_unicode_length_get( unicode_name_A3); + _fx_utility_logical_sector_read_error_request = 32; + status = fx_unicode_directory_create( &ram_disk, unicode_name_A3, length, destination_name); + return_if_fail( status == FX_IO_ERROR); +#endif + + status = fx_file_close( &my_file); + status += fx_file_create( &ram_disk, "HELLO"); + status += fx_file_open( &ram_disk, &my_file, "HELLO", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + /* Make an IO error in fx_file_extended_allocate while calling _fx_directory_entry_write. */ + _fx_utility_logical_sector_read_error_request = 2; + status = fx_file_extended_allocate( &my_file, 200); + return_if_fail( status == FX_IO_ERROR); + +#ifndef FX_DISABLE_CACHE + /* Make an IO error in fx_file_extended_allocate while calling _fx_utility_logical_sector_flush. */ + _fx_utility_logical_sector_flush_error_request = 3; + status = fx_file_extended_allocate( &my_file, 200); + return_if_fail( status == FX_IO_ERROR); +#endif + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors - FAT32 + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Enable fault tolerant. */ + status = fx_fault_tolerant_enable( &ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + length = fx_unicode_length_get( unicode_name_1); + status = fx_unicode_file_create( &ram_disk, unicode_name_1, length , destination_name); + return_if_fail( status == FX_SUCCESS); + + old_length = fx_unicode_length_get( unicode_name_1); + new_length = fx_unicode_length_get( unicode_name_2); + + /* Make an IO error at the end of fx_unicode_file_rename while calling _fx_directory_search. */ + _fx_utility_logical_sector_read_error_request = 14; + status = fx_unicode_file_rename( &ram_disk, unicode_name_1, old_length, unicode_name_2, new_length, destination_name); + return_if_fail( status == FX_IO_ERROR); + + length = fx_unicode_length_get( unicode_name_A); + status = fx_unicode_directory_create( &ram_disk, unicode_name_A, length, destination_name); + return_if_fail( status == FX_SUCCESS); + + old_length = fx_unicode_length_get( unicode_name_A); + new_length = fx_unicode_length_get( unicode_name_A1); + + /* Make an IO error at the end of fx_unicode_directory_rename while calling _fx_directory_search. */ + _fx_utility_logical_sector_read_error_request = 20; + status = fx_unicode_directory_rename( &ram_disk, unicode_name_A, old_length, unicode_name_A1, new_length, destination_name); + return_if_fail( status == FX_IO_ERROR); + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors - FAT32 + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Enable fault tolerant. */ + status = fx_fault_tolerant_enable( &ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + length = fx_unicode_length_get( unicode_name_1); + status = fx_unicode_file_create( &ram_disk, unicode_name_1, length , destination_name); + return_if_fail( status == FX_SUCCESS); + + old_length = fx_unicode_length_get( unicode_name_1); + new_length = fx_unicode_length_get( unicode_name_2); + + /* Make an IO error at the end of fx_unicode_file_rename while calling _fx_unicode_directory_entry_change. */ + _fx_utility_logical_sector_read_error_request = 17; + status = fx_unicode_file_rename( &ram_disk, unicode_name_1, old_length, unicode_name_2, new_length, destination_name); + return_if_fail( status == FX_IO_ERROR); + + length = fx_unicode_length_get( unicode_name_A); + status = fx_unicode_directory_create( &ram_disk, unicode_name_A, length, destination_name); + return_if_fail( status == FX_SUCCESS); + + old_length = fx_unicode_length_get( unicode_name_A); + new_length = fx_unicode_length_get( unicode_name_A1); + +#ifndef FX_DISABLE_CACHE + /* Make an IO error at the end of fx_unicode_directory_rename while calling _fx_unicode_directory_entry_change. */ + _fx_utility_logical_sector_read_error_request = 24; + status = fx_unicode_directory_rename( &ram_disk, unicode_name_A, old_length, unicode_name_A1, new_length, destination_name); + return_if_fail( status == FX_IO_ERROR); + + status = fx_directory_create( &ram_disk, "src"); + return_if_fail( status == FX_SUCCESS); + + /* Make an IO error at the end of fx_file_delete while calling _fx_utility_logical_sector_flush. */ + _fx_utility_logical_sector_flush_error_request = 1; + status += fx_directory_delete( &ram_disk, "src"); + return_if_fail( status == FX_IO_ERROR); +#endif + + status = fx_directory_create( &ram_disk, "dev"); + return_if_fail( status == FX_SUCCESS); + + /* Make an IO error at the end of fx_directory_attributes_set while calling _fx_directory_entry_write. */ + _fx_utility_logical_sector_read_error_request = 2; + status = fx_directory_attributes_set( &ram_disk, "dev", FX_DIRECTORY | FX_ARCHIVE | FX_SYSTEM | FX_READ_ONLY | FX_HIDDEN); + return_if_fail( status == FX_IO_ERROR); + + status = fx_file_create( &ram_disk, "test.bat"); + return_if_fail( status == FX_SUCCESS); + + /* Make an IO error at the end of fx_file_attributes_set while calling _fx_directory_entry_write. */ + _fx_utility_logical_sector_read_error_request = 5; + status = fx_file_attributes_set( &ram_disk, "test.bat", FX_READ_ONLY); + return_if_fail( status == FX_IO_ERROR); + + status = fx_file_create( &ram_disk, "new_file"); + status += fx_file_open( &ram_disk, &my_file, "new_file", FX_OPEN_FOR_WRITE); + status += fx_file_write( &my_file, "1234567890", 10); + return_if_fail( status == FX_SUCCESS); + + /* Make an IO error in fx_media_flush while calling _fx_directory_entry_write. */ + _fx_utility_logical_sector_read_error_request = 1; + status = fx_media_flush( &ram_disk); + return_if_fail( status == FX_IO_ERROR); + +#ifndef FX_DISABLE_CACHE + /* Make an IO error in fx_media_flush while calling _fx_utility_logical_sector_flush. */ + _fx_utility_logical_sector_flush_error_request = 1; + status = fx_media_flush( &ram_disk); + return_if_fail( status == FX_IO_ERROR); +#endif + + status = fx_directory_create( &ram_disk, "root"); +#ifndef FX_STANDALONE_ENABLE + status += fx_directory_local_path_set( &ram_disk, &local_path, "root"); +#else + status += fx_directory_default_set(&ram_disk, "/root"); +#endif + return_if_fail( status == FX_SUCCESS); + + /* Create enough directory to overflow one single cluster. */ + length = fx_unicode_length_get( unicode_name_1); + for ( UINT i = 0; i < 26; i++) + { + status = fx_unicode_directory_create( &ram_disk, unicode_name_1, length, destination_name); + unicode_name_1[0]++; + return_if_fail( status == FX_SUCCESS); + } + + length = fx_unicode_length_get( unicode_name_2); + for ( UINT i = 0; i < 6; i++) + { + status = fx_unicode_directory_create( &ram_disk, unicode_name_2, length, destination_name); + unicode_name_2[0]++; + return_if_fail( status == FX_SUCCESS); + } + + /* Make an IO error in _fx_unicode_directory_entry_read while calling _fx_utility_FAT_entry_read to access fat chain. */ + _fx_utility_fat_entry_read_error_request = 3; + length = fx_unicode_length_get( unicode_name_3); + status = fx_unicode_directory_create( &ram_disk, unicode_name_3, length, destination_name); + return_if_fail( status == FX_SUCCESS); + + status = fx_directory_create( &ram_disk, "root"); +#ifndef FX_STANDALONE_ENABLE + status += fx_directory_local_path_set( &ram_disk, &local_path, "root"); +#else + status += fx_directory_default_set(&ram_disk, "/root"); +#endif + return_if_fail( status == FX_SUCCESS); + + memset( long_unicode_name, 1, 270); + long_unicode_name[270] = 0; + long_unicode_name[271] = 0; + length = fx_unicode_length_get( long_unicode_name); + return_if_fail( length = 135); + + /* Created six directories in a subdir including 12 dir_entries each to overflow a single cluster. */ + temp = long_unicode_name[0]; + for ( UINT i = 0; i < 6; i++) + { + status = fx_unicode_directory_create( &ram_disk, long_unicode_name, length, destination_name); + return_if_fail( status == FX_SUCCESS); + long_unicode_name[0]++; + } + + /* Access the directory divided into two clusters and make an IO error while getting next cluster in _fx_unicode_directory_entry_read. */ + /* We still create the directory successfully. */ + _fx_utility_fat_entry_read_error_request = 3; + status = fx_unicode_directory_create( &ram_disk, long_unicode_name, length, destination_name); + return_if_fail( status == FX_SUCCESS); + + /* Switch to root directory. */ +#ifndef FX_STANDALONE_ENABLE + status += fx_directory_local_path_set( &ram_disk, &local_path, "/"); +#else + status += fx_directory_default_set(&ram_disk, "/"); +#endif + status += fx_unicode_directory_create( &ram_disk, long_unicode_name, length, destination_name); + long_unicode_name[0]++; + return_if_fail( status == FX_SUCCESS); + + /* There is no sectors in root directory. */ + temp = ram_disk.fx_media_root_sectors; + ram_disk.fx_media_root_sectors = 0; + status = fx_unicode_directory_create( &ram_disk, long_unicode_name, length, destination_name); + ram_disk.fx_media_root_sectors = temp; + return_if_fail( status == FX_FILE_CORRUPT); + + status = fx_file_close( &my_file); + return_if_fail( status == FX_SUCCESS); + + status = fx_file_create( &ram_disk, "config.h"); + status += fx_file_open( &ram_disk, &my_file, "config.h", FX_OPEN_FOR_WRITE); + status += fx_file_write( &my_file, read_buffer, 2048 * 2); + status += fx_file_seek( &my_file, 2047); + + /* Make an IO error in fx_file_read while calling _fx_utility_FAT_entry_read. */ + _fx_utility_fat_entry_read_error_request = 1; + status += fx_file_read( &my_file, read_buffer, 10, &actual); + return_if_fail( status == FX_IO_ERROR); + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors - FAT32 + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + length = fx_unicode_length_get( unicode_name_14); + for ( UINT i = 0; i < 2; i++) + { + status = fx_unicode_file_create( &ram_disk, unicode_name_14, length, destination_name); + return_if_fail( status == FX_SUCCESS); + unicode_name_14[0]++; + } + + /* Make an IO error in fx_unicode_directory_entry_change while calling _fx_utility_logical_sector_write. */ + _fx_utility_logical_sector_write_error_request = 6; + length = fx_unicode_length_get( unicode_name_14); + status = fx_unicode_file_create( &ram_disk, unicode_name_14, length, destination_name); + unicode_name_14[0]++; + return_if_fail( status == FX_IO_ERROR); + + length = fx_unicode_length_get( unicode_name_14); + for ( UINT i = 0; i < 2; i++) + { + status = fx_unicode_file_create( &ram_disk, unicode_name_14, length, destination_name); + return_if_fail( status == FX_SUCCESS); + unicode_name_14[0]++; + } + + /* Make an IO error in fx_unicode_directory_entry_change while reading next logical sector. */ + _fx_utility_logical_sector_read_error_request = 38; + status = fx_unicode_file_create( &ram_disk, unicode_name_14, length, destination_name); + return_if_fail( status == FX_IO_ERROR); + unicode_name_14[0]++; + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors - FAT32 + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Enable fault tolerant. */ + status = fx_fault_tolerant_enable( &ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + status = fx_file_create( &ram_disk, "src"); + status += fx_file_open( &ram_disk, &my_file, "src", FX_OPEN_FOR_WRITE); + status += fx_file_allocate( &my_file, 2048 * 2); + status += fx_file_seek( &my_file, 2047); + return_if_fail( status == FX_SUCCESS); + + /* Make an IO error in _fx_fault_tolerant_cleanup_FAT_chain while calling _fx_utility_FAT_entry_write. */ + _fx_utility_fat_entry_write_error_request = 3; + status += fx_file_write( &my_file, "hello", 5); + status += fx_file_close( &my_file); + return_if_fail( status == FX_IO_ERROR); + + status = fx_file_create( &ram_disk, "world"); + status += fx_file_open( &ram_disk, &my_file, "world", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + /* Make IO error in _fx_fault_tolerant_transaction_end while flushing first cluster. */ + _fx_ram_driver_io_error_request = 5; + status = fx_file_write( &my_file, "hello", 5); + return_if_fail( status == FX_IO_ERROR); + + status = fx_file_close( &my_file); + return_if_fail( status == FX_SUCCESS); + +#ifndef FX_DISABLE_CACHE + /* Make IO error in _fx_fault_tolerant_transaction_end while writing back log file. */ + _fx_ram_driver_io_error_request = 11; + status = fx_directory_create( &ram_disk, "dev"); + return_if_fail( status == FX_IO_ERROR); +#endif + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors - FAT32 + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Enable fault tolerant. */ + status = fx_fault_tolerant_enable( &ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + status = fx_file_create( &ram_disk, "test.txt"); + status += fx_file_open( &ram_disk, &my_file, "test.txt", FX_OPEN_FOR_WRITE); + status += fx_file_extended_best_effort_allocate( &my_file, 2048 * 4, &actual_64); + + /* Attempt to cover the branch in _fx_file_write which needs file_ptr -> fx_file_current_relative_cluster < file_ptr -> fx_file_consecutive_cluster. */ + /* Current offset is in the cluster in the middle of some consecutive cluster at the head of file. */ + status = fx_file_seek( &my_file, 2048 + 20); + status += fx_file_write( &my_file, "hello", 5); + return_if_fail( status == FX_SUCCESS); + + status = fx_file_close( &my_file); + return_if_fail( status == FX_SUCCESS); + + status = fx_file_create( &ram_disk, "test2.txt"); + status += fx_file_open( &ram_disk, &my_file, "test2.txt", FX_OPEN_FOR_WRITE); + status += fx_file_extended_best_effort_allocate( &my_file, 2048 * 4, &actual_64); + return_if_fail( status == FX_SUCCESS); + + status = fx_file_close( &my_file); + return_if_fail( status == FX_SUCCESS); + + /* Covered the branch in fx_file_write.c while adding dir log. */ + ram_disk.fx_media_fault_tolerant_state |= FX_FAULT_TOLERANT_STATE_STARTED; + dir_entry.fx_dir_entry_long_name_present = 0; + dir_entry.fx_dir_entry_short_name[0] = 'A'; + dir_entry.fx_dir_entry_short_name[1] = 0; + dir_entry.fx_dir_entry_name = (CHAR *)buffer; + dir_entry.fx_dir_entry_name[0] = 'A'; + dir_entry.fx_dir_entry_name[1] = 0; + dir_entry.fx_dir_entry_byte_offset = 512; + dir_entry.fx_dir_entry_log_sector = ram_disk.fx_media_data_sector_start + 1; + dir_entry.fx_dir_entry_long_name_shorted = 1; + _fx_directory_entry_write( &ram_disk, &dir_entry); + + status += fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors - FAT32 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + status = fx_media_read( &ram_disk, 1, (VOID *)read_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Format the disk as FAT32 and assign the FAT entry of root cluster as itself. */ + read_buffer[8] = 2; + read_buffer[9] = 0; + read_buffer[10] = 0; + read_buffer[11] = 0; + status = fx_media_write( &ram_disk, 1, (VOID *)read_buffer); + return_if_fail( status == FX_SUCCESS); + + status = fx_media_close( &ram_disk); + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_FAT_READ_ERROR); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Enable fault tolerant. */ + status = fx_fault_tolerant_enable( &ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + status = fx_file_create( &ram_disk, "test"); + status += fx_file_open( &ram_disk, &my_file, "test", FX_OPEN_FOR_WRITE); + status += fx_file_allocate( &my_file, 2048 * 6); + status += fx_file_seek( &my_file, 1); + + /* Make a mistake(cluster < FX_FAT_ENTRY_START) while looping the link of FAT to find the previous cluster of copy_head_cluster. */ + ram_disk.fx_media_fat_cache[12].fx_fat_cache_entry_cluster = 3; + ram_disk.fx_media_fat_cache[12].fx_fat_cache_entry_value = 1; + status = fx_file_write( &my_file, read_buffer, 2048 * 3); + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Enable fault tolerant. */ + status = fx_fault_tolerant_enable( &ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + status = fx_file_create( &ram_disk, "test"); + status += fx_file_open( &ram_disk, &my_file, "test", FX_OPEN_FOR_WRITE); + status += fx_file_allocate( &my_file, 2048 * 6); + status += fx_file_seek( &my_file, 1); + + /* Make a mistake(cluster > media_ptr -> fx_media_fat_reserved) while looping the link of FAT to find the previous cluster of copy_head_cluster. */ + ram_disk.fx_media_fat_cache[12].fx_fat_cache_entry_cluster = 3; + ram_disk.fx_media_fat_cache[12].fx_fat_cache_entry_value = ram_disk.fx_media_fat_reserved + 1; + status = fx_file_write( &my_file, read_buffer, 2048 * 3); + + /* Make an IO error in fx_file_rename while calling _fx_directory_entry_write. */ + _fx_utility_logical_sector_read_error_request = 24; + status = fx_file_create( &ram_disk, "test1"); + status += fx_file_rename( &ram_disk, "test1", "test2"); + return_if_fail( status == FX_IO_ERROR); + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format a FAT32 disk with 512 bytes sector. */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 512, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Make an IO error while reading FAT chain. */ + _fx_ram_driver_io_error_request = 1; + status = fx_fault_tolerant_enable( &ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + _fx_ram_driver_io_error_request = 0; + return_if_fail( status == FX_FAT_READ_ERROR); + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format a FAT16 disk with 512 bytes sector. */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 512, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Make an IO error while reading FAT chain. */ + ram_disk.fx_media_cluster_search_start = 0; + status = fx_fault_tolerant_enable( &ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_enable_3_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Enable 3 Test...........................N/A\n"); + + test_control_return(255); +} +#endif diff --git a/test/regression_test/filex_fault_tolerant_enable_4_test.c b/test/regression_test/filex_fault_tolerant_enable_4_test.c new file mode 100644 index 0000000..4ba3a5a --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_enable_4_test.c @@ -0,0 +1,282 @@ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_utility.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_enable_4_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT directory_write_interrupt = FX_FALSE; +static CHAR write_buffer[2048]; +static UCHAR read_buffer[4096]; + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_enable_4_test_application_define(void *first_unused_memory) +#endif +{ +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Enable 4 Test..........................."); + + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail(status == FX_SUCCESS); + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail(status == FX_SUCCESS); + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + return_if_fail(status == FX_SUCCESS); +#ifndef FX_STANDALONE_ENABLE + /* Create the main thread. */ + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + directory_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail(status == FX_SUCCESS); + + /**** Modify the checksum of log content so that the log will not apply. ****/ + _fx_utility_logical_sector_read( &ram_disk, 1 + ram_disk.fx_media_sectors_per_FAT + ram_disk.fx_media_root_sectors, read_buffer, 8, FX_DATA_SECTOR); + read_buffer[FX_FAULT_TOLERANT_LOG_CONTENT_OFFSET]++; + _fx_utility_logical_sector_write( &ram_disk, 1 + ram_disk.fx_media_sectors_per_FAT + ram_disk.fx_media_root_sectors, read_buffer, 8, FX_DATA_SECTOR); + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail(status == FX_SUCCESS); + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + fx_file_seek(&my_file, 0); + + /* Write something to the file. */ + fx_file_write(&my_file, (void *) write_buffer, 2048); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the Directory write operation after record the redo log. */ + if ((sector_type == FX_DIRECTORY_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + directory_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_enable_4_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Enable 4................................N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_enable_test.c b/test/regression_test/filex_fault_tolerant_enable_test.c new file mode 100644 index 0000000..ee383ad --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_enable_test.c @@ -0,0 +1,333 @@ +/* This FileX test concentrates on the Fault-Tolerant enable feature. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; +Check Fault tolerrant Log file: +Step1: Format and open the media; +Step2: Check the fault tolerant enable flag and boot index value; +Step3: Enable fault tolerant feature; +Step4: Check the fault tolerant enable flag, boot index value and fault tolerant header value; +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#include "tx_thread.h" +#include "tx_timer.h" +#endif +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_enable_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_enable_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status, temp1; +UINT i = 0, j; +ULONG start_cluster; +ULONG clusters; +ULONG bytes_per_cluster; +ULONG value; +ULONG start_sector, temp; +UCHAR buffer[2048]; +UCHAR *source_buffer; +ULONG checksum; +UCHAR flag; +UINT size; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Enable Test............................."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + return_if_fail(ram_disk.fx_media_fault_tolerant_enabled != FX_TRUE); + + /* Read the boot sector. */ + source_buffer = ((UCHAR *) ram_disk.fx_media_driver_info); + + /* Copy the RAM sector into the destination. */ + _fx_utility_memory_copy(source_buffer, buffer, ram_disk.fx_media_bytes_per_sector); + + /* Check whether the boot index is used. */ + start_cluster = _fx_utility_32_unsigned_read(buffer + FX_FAULT_TOLERANT_BOOT_INDEX); + return_if_fail( start_cluster == 0); + + /* Atempt to call fx_fault_tolerant_enable with invalid parameters. */ + temp = ram_disk.fx_media_sectors_per_cluster; + ram_disk.fx_media_sectors_per_cluster = 0; + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + ram_disk.fx_media_sectors_per_cluster = temp; + return_if_fail( status == FX_MEDIA_INVALID); + + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, 0); + return_if_fail( status == FX_NOT_ENOUGH_MEMORY); + + /* Make an IO ERROR while reading boot sector. */ + _fx_ram_driver_io_error_request = 1; + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_BOOT_ERROR); + + /* What will happen if there is no space? */ + temp = ram_disk.fx_media_available_clusters; + ram_disk.fx_media_available_clusters = 0; + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_NO_MORE_SPACE); + ram_disk.fx_media_available_clusters = temp; + + temp = ram_disk.fx_media_total_clusters; + ram_disk.fx_media_total_clusters = 0; + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_NO_MORE_SPACE); + ram_disk.fx_media_total_clusters = temp; + + /* Make IO error in _fx_fault_tolerant_reset_log_file. */ + if ( i == 0) + { + _fx_ram_driver_io_error_request = 4; + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_IO_ERROR); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Check the fault tolerant feature flag. */ + return_if_fail( ram_disk.fx_media_fault_tolerant_enabled == FX_TRUE); + + /* Atempt to exceed the space limitation in _fx_fault_tolerant_add_FAT_log. */ + temp = ram_disk.fx_media_fault_tolerant_file_size; + temp1 = ram_disk.fx_media_fault_tolerant_state; + ram_disk.fx_media_fault_tolerant_state |= FX_FAULT_TOLERANT_STATE_STARTED; + ram_disk.fx_media_fault_tolerant_state &= (UCHAR)~FX_FAULT_TOLERANT_STATE_SET_FAT_CHAIN; + ram_disk.fx_media_fault_tolerant_file_size = 0xffff0000; + status = _fx_utility_FAT_entry_write( &ram_disk, 1, 2); + ram_disk.fx_media_fault_tolerant_file_size = temp; + ram_disk.fx_media_fault_tolerant_state = (UCHAR)temp1; + return_if_fail( status == FX_NO_MORE_SPACE); + + /* Read the boot sector. */ + source_buffer = ((UCHAR *) ram_disk.fx_media_driver_info); + + /* Copy the RAM sector into the destination. */ + _fx_utility_memory_copy(source_buffer, buffer, ram_disk.fx_media_bytes_per_sector); + + /* Check whether the boot index is used. */ + start_cluster = _fx_utility_32_unsigned_read(buffer + FX_FAULT_TOLERANT_BOOT_INDEX); + return_if_fail( start_cluster != 0); + + /* Get the FAT table address. */ + source_buffer = ((UCHAR *) ram_disk.fx_media_driver_info) + ((ram_disk.fx_media_reserved_sectors + ram_disk.fx_media_hidden_sectors) * ram_disk.fx_media_bytes_per_sector); + + bytes_per_cluster = ram_disk.fx_media_bytes_per_sector * ram_disk.fx_media_sectors_per_cluster; + clusters = (FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + bytes_per_cluster - 1) / bytes_per_cluster; + + for (j = 0; j < clusters; j++) + { + status = _fx_utility_FAT_entry_read(&ram_disk, start_cluster + j, &value); + return_if_fail (status == FX_SUCCESS); + + if (j < (clusters - 1)) + { + return_if_fail (value == (start_cluster + j + 1)); + } + else + { + return_if_fail (value == ram_disk.fx_media_fat_last); + } + } + + /* Get the start sector. */ + start_sector = (start_cluster - FX_FAT_ENTRY_START) * ram_disk.fx_media_sectors_per_cluster + ram_disk.fx_media_data_sector_start; + + /* Read the log sector. */ + source_buffer = ((UCHAR *) ram_disk.fx_media_driver_info) + ((start_sector + ram_disk.fx_media_hidden_sectors) * ram_disk.fx_media_bytes_per_sector); + + /* Copy the one cluster into the destination. */ + _fx_utility_memory_copy(source_buffer, buffer, 1 * ram_disk.fx_media_sectors_per_cluster * ram_disk.fx_media_bytes_per_sector); + + /* Verify ID field. */ + return_if_fail(_fx_utility_32_unsigned_read(buffer) == FX_FAULT_TOLERANT_ID); + + /* Get the header size. */ + size = _fx_utility_16_unsigned_read(buffer + 4); + + /* Verify Size field. */ + return_if_fail( size == FX_FAULT_TOLERANT_LOG_HEADER_SIZE + FX_FAULT_TOLERANT_FAT_CHAIN_SIZE); + + /* Calculate checksum of header. */ + checksum = _fx_fault_tolerant_calculate_checksum(buffer, FX_FAULT_TOLERANT_LOG_HEADER_SIZE); + return_if_fail( checksum == 0); + + /* Verify checksum of undo logs. */ + checksum = _fx_fault_tolerant_calculate_checksum(buffer + FX_FAULT_TOLERANT_FAT_CHAIN_OFFSET, FX_FAULT_TOLERANT_FAT_CHAIN_SIZE); + return_if_fail( checksum == 0); + + /* Check undo log. */ + flag = *(buffer + FX_FAULT_TOLERANT_FAT_CHAIN_OFFSET + 2); + return_if_fail( flag == 0); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_enable_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Enable Test.............................N/A\n"); + + test_control_return(255); +} +#endif diff --git a/test/regression_test/filex_fault_tolerant_file_allocate_directory_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_allocate_directory_interrupt_test.c new file mode 100644 index 0000000..fae2aa2 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_allocate_directory_interrupt_test.c @@ -0,0 +1,737 @@ +/* This FileX test concentrates on the Fault-Tolerant direcotry write interrupt operation. */ +/* + For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check directory interrupt for fx_file_allocate(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 24 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to allocate 1500 bytes for TEST1.TXT; +Step7: Terminate the new thread to simulate poweroff when update the directory after redo log generated. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(redo operation); +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_allocate_directory_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) && defined (FX_UPDATE_FILE_SIZE_ON_ALLOCATE) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT directory_write_interrupt = FX_FALSE; +static CHAR read_buffer[2048]; +static UINT read_buffer_size = 2048; + +#define TEST_COUNT 3 + + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_allocate_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Allocate DIR Interrupt Test........"); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " 0123456789012345678901\n", 24); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 24)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + directory_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 1524)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (directory_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + +UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Allocate 1500 size... */ + fx_file_allocate(&my_file, 1500); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the Directory write operation after record the redo log. */ + if ((sector_type == FX_DIRECTORY_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + directory_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_allocate_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Allocate DIR Interrupt Test........N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_allocate_fat_crossover_test.c b/test/regression_test/filex_fault_tolerant_file_allocate_fat_crossover_test.c new file mode 100644 index 0000000..6e812f5 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_allocate_fat_crossover_test.c @@ -0,0 +1,321 @@ +/* This FileX test concentrates on the bug as per + * JIRA: https://expresslogic.atlassian.net/browse/FIL-4. + * In fx_fault_tolerant_cleanup_FAT_chain.c, a group of the FAT entries are updated in the cache + * before it is flushed into media system. The design requires the FAT entries updated in reverse + * order in FAT chain. Due to the FAT cache, the FAT entries may be updated in any order. + * That will break the fault tolerant design. The expected issue is, the FAT chain could + * not be released completely.*/ +/* +For FAT 32 one cluster size is 512 bytes and one sector per cluster; + +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and allocate 118 clusters for A.TXT. +Step6: Create a new thread and allocate cluster 127 and 128 for A.TXT. +Step7: Terminate the new thread to simulate poweroff while writing the fat entry of cluster 127 +(the fat entry of cluster 128 is flushed first). +Step8: Open the media. +Step9: Enable fault tolerant feature to revert the FAT chain(revert operation). +Step10: Perform media check. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include "fx_utility.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_allocate_fat_crossover_test_application_define(void *first_unused_memory); + +#ifdef FX_ENABLE_FAULT_TOLERANT + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT fat_write_interrupt = FX_FALSE; +static UCHAR scratch_memory[16 * 1024]; +static UINT interrupt_count_down; + + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_allocate_fat_crossover_test_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG errors_detected; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Allocate Fat Crossover Test........"); + + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 512, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + status += fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail(FX_SUCCESS == status); + + /* One cluster for A.TXT. */ + status = fx_file_create(&ram_disk, "A.TXT"); + status += fx_file_open(&ram_disk, &my_file, "A.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_allocate(&my_file, 512 * 118); + status += fx_file_close(&my_file); + return_if_fail(FX_SUCCESS == status); + + /* Ensure no lost clusters. */ + status = fx_media_check(&ram_disk, scratch_memory, sizeof(scratch_memory), FX_LOST_CLUSTER_ERROR, &errors_detected); + return_if_fail((status == FX_SUCCESS) && (0 == errors_detected)); + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + fat_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + return_if_fail(FX_TRUE == fat_write_interrupt); + + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + status += fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail(FX_SUCCESS == status); + + status = fx_media_check(&ram_disk, scratch_memory, sizeof(scratch_memory), FX_LOST_CLUSTER_ERROR, &errors_detected); + return_if_fail(FX_SUCCESS == status); + return_if_fail(errors_detected == 0); + + fx_media_close(&ram_disk); + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "A.TXT", FX_OPEN_FOR_WRITE); + return_if_fail(FX_SUCCESS == status); + + interrupt_count_down = 2; + + /* The FAT sector of the fat entries of cluster 127 is not the FAT sector for cluster 128. + Since the record of cluster 128 is located at fat_cache[0], the fat sector of 128 will be flushed first. + Terminate the new thread to simulate poweroff while writing the fat entry of cluster 127. + */ + fx_file_allocate(&my_file, 512 * 2); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the FAT write operation after record the undo log. */ + if (sector_type == FX_FAT_SECTOR) + { + + interrupt_count_down--; + if (interrupt_count_down == 0) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + fat_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_allocate_fat_crossover_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Allocate Fat Crossover Test........N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_allocate_fat_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_allocate_fat_interrupt_test.c new file mode 100644 index 0000000..7a1d051 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_allocate_fat_interrupt_test.c @@ -0,0 +1,741 @@ +/* This FileX test concentrates on the Fault-Tolerant FAT write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check fat interrupt for fx_file_allocate(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 24 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to allocate 1500 bytes for TEST1.TXT; +Step7: Terminate the new thread to simulate poweroff when update the fat between undo log generated and redo log not generated. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(undo operation); +Step10: Check the test files. +*/ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_allocate_fat_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) && defined (FX_UPDATE_FILE_SIZE_ON_ALLOCATE) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *pointer; +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT exit_flag =0; +static UINT fat_write_interrupt = FX_FALSE; +static CHAR read_buffer[2048]; +static UINT read_buffer_size = 2048; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_allocate_fat_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + // pthread_create(&ptid0, NULL, &ftest_0_entry, NULL); + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Allocate FAT Interrupt Test........"); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + exit_flag = 0; + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " 0123456789012345678901\n", 24); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 24)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + fat_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 24)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (fat_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + exit_flag =1; + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + UINT status; +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Allocate 1500 size... */ + status = fx_file_allocate(&my_file, 1500); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the FAT write operation after record the undo log. */ + if ((sector_type == FX_FAT_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_UNDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + fat_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + exit_flag++; + pthread_cancel(ptid1); +#endif + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_allocate_fat_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Allocate FAT Interrupt Test........N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_allocate_redo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_allocate_redo_log_interrupt_test.c new file mode 100644 index 0000000..cf0eefc --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_allocate_redo_log_interrupt_test.c @@ -0,0 +1,737 @@ +/* This FileX test concentrates on the Fault-Tolerant redo LOG write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check redo log interrupt for fx_file_allocate(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 24 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to allocate 1500 bytes for TEST1.TXT; +Step7: Terminate the new thread to simulate poweroff when update the redo log. +Step8: Open the media; +Step9: Enable fault tolerant feature to do undo operation; +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_allocate_redo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) && defined (FX_UPDATE_FILE_SIZE_ON_ALLOCATE) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT log_write_interrupt = FX_FALSE; +static CHAR read_buffer[2048]; +static UINT read_buffer_size = 2048; + +#define TEST_COUNT 3 + + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_allocate_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Allocate Redo LOG Interrupt Test..."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " 0123456789012345678901\n", 24); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 24)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 24)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (log_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + +UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Allocate 1500 size... */ + fx_file_allocate(&my_file, 1500); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the redo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_allocate_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Allocate Redo LOG Interrupt Test...N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_allocate_test.c b/test/regression_test/filex_fault_tolerant_file_allocate_test.c new file mode 100644 index 0000000..4fa4554 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_allocate_test.c @@ -0,0 +1,329 @@ +/* This FileX test concentrates on the Fault-Tolerant the file allocate related API test. + * The offset of file is not changed after file allocate operation. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_allocate_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT i; + +#define SEEK_COUNT 5 +#define FAT_COUNT 3 /* FAT12, 16, 32. */ +#define TEST_COUNT FAT_COUNT * SEEK_COUNT + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_allocate_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG64 allocated; +ULONG64 available_bytes_before_write; +ULONG64 available_bytes_after_write; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Allocate Test......................"); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i < 5) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i < 10) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i < 15) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 4, // Total sectors + 512, // Sector size + 4, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Get available bytes before write. */ + fx_media_extended_space_available(&ram_disk, &available_bytes_before_write); + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Get available bytes before write. */ + fx_media_extended_space_available(&ram_disk, &available_bytes_after_write); + if (available_bytes_before_write != (available_bytes_after_write + + ram_disk.fx_media_fault_tolerant_clusters * ram_disk.fx_media_bytes_per_sector * ram_disk.fx_media_sectors_per_cluster)) + { + + printf("ERROR!\n"); + test_control_return(38); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Get available bytes before write. */ + fx_media_extended_space_available(&ram_disk, &available_bytes_before_write); + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + if (i & 1) + { + status = fx_file_allocate(&my_file, 2048); + } + else + { + status = fx_file_extended_best_effort_allocate(&my_file, 2048, &allocated); + } + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Get available bytes after write. */ + fx_media_extended_space_available(&ram_disk, &available_bytes_after_write); + { + if ((available_bytes_before_write - available_bytes_after_write) != 2048) + { + + printf("ERROR!\n"); + test_control_return(37); + } + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_allocate_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Allocate Test......................N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_allocate_undo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_allocate_undo_log_interrupt_test.c new file mode 100644 index 0000000..7ad5576 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_allocate_undo_log_interrupt_test.c @@ -0,0 +1,737 @@ +/* This FileX test concentrates on the Fault-Tolerant undo LOG write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check undo log interrupt for fx_file_allocate(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 24 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to allocate 1500 bytes for TEST1.TXT; +Step7: Terminate the new thread to simulate poweroff when update the undo log. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(undo operation); +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_allocate_undo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) && defined (FX_UPDATE_FILE_SIZE_ON_ALLOCATE) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT log_write_interrupt = FX_FALSE; +static CHAR read_buffer[2048]; +static UINT read_buffer_size = 2048; + +#define TEST_COUNT 3 + + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_allocate_undo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Allocate Undo LOG Interrupt Test..."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " 0123456789012345678901\n", 24); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 24)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 24)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (log_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + +UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Allocate 1500 size... */ + fx_file_allocate(&my_file, 1500); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the undo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_allocate_undo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Allocate Undo LOG Interrupt Test...N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_attributes_set_directory_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_attributes_set_directory_interrupt_test.c new file mode 100644 index 0000000..112d1d7 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_attributes_set_directory_interrupt_test.c @@ -0,0 +1,425 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check directory interrupt for fx_file_truncate_release(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create TEST.TXT file; +Step4: Check the attributes for TEST.TXT file; +Step5: Create new thread to set the attributes as FX_ARCHIVE | FX_READ_ONLY for TEST.TXT; +Step6: Terminate the new thread to simulate poweroff when update the directory. +Step7: Open the media; +Step8: Enable fault tolerant feature to recover the data(redo operation); +Step9: Check the atrrbutes for TEST.TXT file. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_attributes_set_directory_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT directory_write_interrupt = FX_FALSE; + +#define TEST_COUNT 3 + + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_attributes_set_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT attributes; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Attri Set DIR Interrupt Test......."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Invalidate the media cache. */ + fx_media_cache_invalidate(&ram_disk); + + /* Pickup the attributes of the file. */ + status = fx_file_attributes_read(&ram_disk, "TEST.TXT", &attributes); + + /* Check the attributes read status. */ + if ((status != FX_SUCCESS) || (attributes != FX_ARCHIVE)) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + directory_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(6); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Invalidate the media cache. */ + fx_media_cache_invalidate(&ram_disk); + + /* Pickup the attributes of the file again. */ + status = fx_file_attributes_read(&ram_disk, "TEST.TXT", &attributes); + + /* Check the attributes read status. */ + if ((status != FX_SUCCESS) || (attributes != (FX_ARCHIVE | FX_READ_ONLY))) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (directory_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Now write the attributes out for the file. */ + fx_file_attributes_set(&ram_disk, "TEST.TXT", FX_ARCHIVE | FX_READ_ONLY); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the Directory write operation after record the redo log. */ + if ((sector_type == FX_DIRECTORY_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + directory_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_attributes_set_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Attri Set DIR Interrupt Test.......N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_attributes_set_redo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_attributes_set_redo_log_interrupt_test.c new file mode 100644 index 0000000..3686b11 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_attributes_set_redo_log_interrupt_test.c @@ -0,0 +1,425 @@ +/* This FileX test concentrates on the Fault-Tolerant redo Log write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check redo log interrupt for fx_file_truncate_release(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create TEST.TXT file; +Step4: Check the attributes for TEST.TXT file; +Step5: Create new thread to set the attributes as FX_ARCHIVE | FX_READ_ONLY for TEST.TXT; +Step6: Terminate the new thread to simulate poweroff when update the redo log. +Step7: Open the media; +Step8: Enable fault tolerant feature to recover the data(undo operation); +Step9: Check the atrrbutes for TEST.TXT file. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_attributes_set_redo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT log_write_interrupt = FX_FALSE; + +#define TEST_COUNT 3 + + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_attributes_set_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT attributes; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Attri Set Redo LOG Interrupt Test.."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Invalidate the media cache. */ + fx_media_cache_invalidate(&ram_disk); + + /* Pickup the attributes of the file. */ + status = fx_file_attributes_read(&ram_disk, "TEST.TXT", &attributes); + + /* Check the attributes read status. */ + if ((status != FX_SUCCESS) || (attributes != FX_ARCHIVE)) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(6); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Invalidate the media cache. */ + fx_media_cache_invalidate(&ram_disk); + + /* Pickup the attributes of the file again. */ + status = fx_file_attributes_read(&ram_disk, "TEST.TXT", &attributes); + + /* Check the attributes read status. */ + if ((status != FX_SUCCESS) || (attributes != FX_ARCHIVE )) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (log_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Now write the attributes out for the file. */ + fx_file_attributes_set(&ram_disk, "TEST.TXT", FX_ARCHIVE | FX_READ_ONLY); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the redo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) == FX_FAULT_TOLERANT_LOG_REDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_attributes_set_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Attri Set Redo LOG Interrupt Test..N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_best_allocate_directory_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_best_allocate_directory_interrupt_test.c new file mode 100644 index 0000000..abf7ab0 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_best_allocate_directory_interrupt_test.c @@ -0,0 +1,737 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check directory interrupt for fx_file_best_effort_allocate(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 24 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to allocate 1500 bytes for TEST1.TXT; +Step7: Terminate the new thread to simulate poweroff when update the directory after record the redo log. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(redo operation); +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_best_allocate_directory_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT directory_write_interrupt = FX_FALSE; +static CHAR read_buffer[2048]; +static UINT read_buffer_size = 2048; + +#define TEST_COUNT 3 + + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_best_allocate_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Best Allo DIR Interrupt Test......."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " 0123456789012345678901\n", 24); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 24)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + directory_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 1524)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (directory_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif +UINT status; +ULONG actual_size_allocated; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Allocate the size... */ + fx_file_best_effort_allocate(&my_file, 1500, &actual_size_allocated); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the Directory write operation after record the redo log. */ + if ((sector_type == FX_DIRECTORY_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + directory_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_best_allocate_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Best Allo DIR Interrupt Test.......N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_best_allocate_fat_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_best_allocate_fat_interrupt_test.c new file mode 100644 index 0000000..9f6c860 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_best_allocate_fat_interrupt_test.c @@ -0,0 +1,737 @@ +/* This FileX test concentrates on the Fault-Tolerant FAT write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check fat interrupt for fx_file_best_effort_allocate(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 24 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to allocate 1500 bytes for TEST1.TXT; +Step7: Terminate the new thread to simulate poweroff when update the fat between undo log generated and redo log not generated. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(undo operation); +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_best_allocate_fat_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT fat_write_interrupt = FX_FALSE; +static CHAR read_buffer[2048]; +static UINT read_buffer_size = 2048; + +#define TEST_COUNT 3 + + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_best_allocate_fat_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Best Allo FAT Interrupt Test......."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " 0123456789012345678901\n", 24); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 24)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + fat_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); + +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 24)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (fat_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + UINT status; + ULONG actual_size_allocated; + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Allocate the size... */ + fx_file_best_effort_allocate(&my_file, 1500, &actual_size_allocated); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the FAT write operation after record the redo log. */ + if ((sector_type == FX_FAT_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_UNDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + fat_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_best_allocate_fat_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Best Allo FAT Interrupt Test.......N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_best_allocate_redo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_best_allocate_redo_log_interrupt_test.c new file mode 100644 index 0000000..69f62fb --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_best_allocate_redo_log_interrupt_test.c @@ -0,0 +1,737 @@ +/* This FileX test concentrates on the Fault-Tolerant redo log write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check redo log interrupt for fx_file_best_effort_allocate(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 24 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to allocate 1500 bytes for TEST1.TXT; +Step7: Terminate the new thread to simulate poweroff when update the redo log. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(undo operation); +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_best_allocate_redo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT log_write_interrupt = FX_FALSE; +static CHAR read_buffer[2048]; +static UINT read_buffer_size = 2048; + +#define TEST_COUNT 3 + + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_best_allocate_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Best Allo Redo LOG Interrupt Test.."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " 0123456789012345678901\n", 24); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 24)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 24)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (log_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif +UINT status; +ULONG actual_size_allocated; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + error_couter ++; + +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Allocate the size... */ + fx_file_best_effort_allocate(&my_file, 1500, &actual_size_allocated); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the redo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_best_allocate_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Best Allo Redo LOG Interrupt Test..N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_best_allocate_undo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_best_allocate_undo_log_interrupt_test.c new file mode 100644 index 0000000..8e13fc3 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_best_allocate_undo_log_interrupt_test.c @@ -0,0 +1,735 @@ +/* This FileX test concentrates on the Fault-Tolerant undo log write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check undo log interrupt for fx_file_best_effort_allocate(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 24 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to allocate 1500 bytes for TEST1.TXT; +Step7: Terminate the new thread to simulate poweroff when update the undo log. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(undo operation); +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_best_allocate_undo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT log_write_interrupt = FX_FALSE; +static CHAR read_buffer[2048]; +static UINT read_buffer_size = 2048; + +#define TEST_COUNT 3 + + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_best_allocate_undo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Best Allo Undo LOG Interrupt Test.."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " 0123456789012345678901\n", 24); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 24)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 24)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (log_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + UINT status; + ULONG actual_size_allocated; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Allocate the size... */ + fx_file_best_effort_allocate(&my_file, 1500, &actual_size_allocated); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the undo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_UNDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_best_allocate_undo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Best Allo Undo LOG Interrupt Test..N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_best_effort_allocate_fat_crossover_test.c b/test/regression_test/filex_fault_tolerant_file_best_effort_allocate_fat_crossover_test.c new file mode 100644 index 0000000..4b1972a --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_best_effort_allocate_fat_crossover_test.c @@ -0,0 +1,322 @@ +/* This FileX test concentrates on the bug as per + * JIRA: https://expresslogic.atlassian.net/browse/FIL-4. + * In fx_fault_tolerant_cleanup_FAT_chain.c, a group of the FAT entries are updated in the cache + * before it is flushed into media system. The design requires the FAT entries updated in reverse + * order in FAT chain. Due to the FAT cache, the FAT entries may be updated in any order. + * That will break the fault tolerant design. The expected issue is, the FAT chain could + * not be released completely.*/ +/* +For FAT 32 one cluster size is 512 bytes and one sector per cluster; + +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and allocate 118 clusters for A.TXT. +Step6: Create a new thread and allocate cluster 127 and 128 for A.TXT(by fx_file_best_effort_allocate). +Step7: Terminate the new thread to simulate poweroff while writing the fat entry of cluster 127 +(the fat entry of cluster 128 is flushed first). +Step8: Open the media. +Step9: Enable fault tolerant feature to revert the FAT chain(revert operation). +Step10: Perform media check. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include "fx_utility.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_best_effort_allocate_fat_crossover_test_application_define(void *first_unused_memory); + +#ifdef FX_ENABLE_FAULT_TOLERANT + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT fat_write_interrupt = FX_FALSE; +static UCHAR scratch_memory[16 * 1024]; +static UINT interrupt_count_down; + + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_best_effort_allocate_fat_crossover_test_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG errors_detected; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Best Effort Allocate FAT Crossover Test."); + + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 512, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + status += fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail(FX_SUCCESS == status); + + /* One cluster for A.TXT. */ + status = fx_file_create(&ram_disk, "A.TXT"); + status += fx_file_open(&ram_disk, &my_file, "A.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_allocate(&my_file, 512 * 118); + status += fx_file_close(&my_file); + return_if_fail(FX_SUCCESS == status); + + /* Ensure no lost clusters. */ + status = fx_media_check(&ram_disk, scratch_memory, sizeof(scratch_memory), FX_LOST_CLUSTER_ERROR, &errors_detected); + return_if_fail((status == FX_SUCCESS) && (0 == errors_detected)); + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + fat_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + return_if_fail(FX_TRUE == fat_write_interrupt); + + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + status += fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail(FX_SUCCESS == status); + + status = fx_media_check(&ram_disk, scratch_memory, sizeof(scratch_memory), FX_LOST_CLUSTER_ERROR, &errors_detected); + return_if_fail(FX_SUCCESS == status); + return_if_fail(errors_detected == 0); + + fx_media_close(&ram_disk); + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + UINT status; + ULONG actual_size; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "A.TXT", FX_OPEN_FOR_WRITE); + return_if_fail(FX_SUCCESS == status); + + interrupt_count_down = 2; + + /* The FAT sector of the fat entries of cluster 127 is not the FAT sector for cluster 128. + Since the record of cluster 128 is located at fat_cache[0], the fat sector of 128 will be flushed first. + Terminate the new thread to simulate poweroff while writing the fat entry of cluster 127. + */ + fx_file_best_effort_allocate(&my_file, 512 * 2, &actual_size); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the FAT write operation after record the undo log. */ + if (sector_type == FX_FAT_SECTOR) + { + + interrupt_count_down--; + if (interrupt_count_down == 0) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + fat_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_best_effort_allocate_fat_crossover_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Best Effort Allocate FAT Crossover Test.N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_corruption_test.c b/test/regression_test/filex_fault_tolerant_file_corruption_test.c new file mode 100644 index 0000000..1ff409f --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_corruption_test.c @@ -0,0 +1,386 @@ +/* This test case is design to reproduce file corruption bug. + * https://expresslogic.zendesk.com/agent/tickets/1252 */ +#include +#include "fx_api.h" +#include "fx_fault_tolerant.h" +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_corruption_test_application_define(void *first_unused_memory); + +#if (defined(FX_ENABLE_FAULT_TOLERANT) && !defined(FX_STANDALONE_ENABLE)) +/*#define DEBUG*/ + +#define MEDIA_MEMORY_SIZE 4096 +#define NO_OF_FATS 1U +#define DIRECTORY_ENTRIES 1024U +#define HIDDEN_SECTORS 0 +/* The TOTAL_SECTORS is defined to 12800 in the ticket which the media is formatted to FAT16. */ +#define TOTAL_SECTORS 128000U +#define SECTOR_SIZE 2048U +#define SECTORS_PER_CLUSTER 1U +#define HEADS 1U +#define SECTORS_PER_TRACK 1U +#define NO_OF_FILES 17 + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, + UINT sector_type, + UCHAR *block_ptr, + UINT *operation_ptr); +extern VOID (*driver_fault_tolerant_enable_callback)(FX_MEDIA *media_ptr, + UCHAR *fault_tolerant_memory_buffer, + ULONG log_size); +static UINT my_driver_write_callback(FX_MEDIA *media_ptr, + UINT sector_type, + UCHAR *block_ptr, + UINT *operation_ptr); +static VOID my_fault_tolerant_enable_callback(FX_MEDIA *media_ptr, + UCHAR *fault_tolerant_memory_buffer, + ULONG log_size); +static void ftest_0_entry(ULONG thread_input); +static void ftest_1_entry(ULONG thread_input); +static void restart_thread_0(); +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +static FX_MEDIA ram_disk; +static UINT loop; +static UINT restart; +static UCHAR file_created[NO_OF_FILES]; +static UCHAR write_buffer[1024 * 1024]; +static UCHAR read_buffer[1024 * 1024]; + +#ifdef __ghs +extern TX_MUTEX __ghLockMutex; +#endif +extern FX_MEDIA *_fx_system_media_opened_ptr; +extern ULONG _fx_system_media_opened_count; +extern TX_MUTEX *_tx_mutex_created_ptr; +extern ULONG _tx_mutex_created_count; + +#define RAND ((rand() % 4000) + 1) +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE +#define DEMO_STACK_SIZE 4096 +#ifdef DEBUG +#define TOTAL_LOOP 0xFFFFFFFF +#define WRITE_INTERRUPT_RATE 10000 +#else +#define TOTAL_LOOP 100000 +#define WRITE_INTERRUPT_RATE 1000000 +#endif + + +static FX_FILE my_file[NO_OF_FILES]; +static UINT FILE_SIZE[NO_OF_FILES] = {1024*5, 1024*2, 1024*5, 1024*50, 1024*1, 1024*40, 1024*5, 1024*10, 1024*24, 1024*50, 1024*16, 1024*12, 1024*5, 1024*1, 1024*32, 1024*9, 1024*19}; +static UCHAR local_buffer2[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_corruption_test_application_define(void *first_unused_memory) +#endif +{ +UCHAR *pointer; +UINT status; + + loop = 0; + restart = 0; + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; + + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + pointer, DEMO_STACK_SIZE, + 3, 3, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += MEDIA_MEMORY_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + + /* Initialize the FileX system. */ + fx_system_initialize(); + + printf("FileX Test: Fault Tolerant File Corruption Test...................."); + + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + MEDIA_MEMORY_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + NO_OF_FATS, // Number of FATs + DIRECTORY_ENTRIES, // Directory Entries + HIDDEN_SECTORS, // Hidden sectors + TOTAL_SECTORS, // Total sectors + SECTOR_SIZE, // Sector size + SECTORS_PER_CLUSTER, // Sectors per cluster + HEADS, // Heads + SECTORS_PER_TRACK); // Sectors per track + return_if_fail(status == FX_SUCCESS); +} + +static void ftest_0_entry(ULONG thread_input) +{ +CHAR fileName[16]; +LONG actual = 0; +UINT status; +UINT i; +UINT itr; +UCHAR first_boot = FX_TRUE; +#ifndef DEBUG +LONG start = (LONG)time(NULL); +#endif /* DEBUG */ + + FX_PARAMETER_NOT_USED(thread_input); + + memset(&ram_disk, 0x0, sizeof(ram_disk)); + memset(cache_buffer, 0x0, MEDIA_MEMORY_SIZE); + driver_write_callback = FX_NULL; + + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, + cache_buffer, MEDIA_MEMORY_SIZE); + return_if_fail(status == FX_SUCCESS); + + driver_fault_tolerant_enable_callback = my_fault_tolerant_enable_callback; + + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail(status == FX_SUCCESS); + +#ifdef DEBUG + printf("\nRestart at %u, LOOP = %u\n", restart, loop); +#endif /* DEBUG */ + + for (;loop < TOTAL_LOOP; loop++) + { + +#ifndef DEBUG + /* Break if the test runs more than 60 seconds. + * In case the CPU is busy, TOTAL_LOOP maybe too large for the test. */ + if ((LONG)time(NULL) - start > 60) + { + break; + } +#endif /* DEBUG */ + + for(i = 0; i < NO_OF_FILES; i++) + { + sprintf(fileName, "TEST%d.TXT", i); + status = fx_file_open(&ram_disk, &my_file[i], fileName, FX_OPEN_FOR_READ); + + if (!file_created[i]) + { + return_if_fail(status == FX_NOT_FOUND); + } + else + { + return_if_fail(status == FX_SUCCESS); + + memset(read_buffer, 0x0, FILE_SIZE[i]); + + for (itr = 0; itr < (FILE_SIZE[i] / 32); itr++) + { + memcpy(&write_buffer[itr * 32], local_buffer2, 32); + } + + status = fx_file_read(&my_file[i], read_buffer, FILE_SIZE[i], (ULONG *)&actual); + if(status == FX_SUCCESS) + { + if (memcmp(read_buffer, write_buffer, FILE_SIZE[i])) + { + + /* Only one file could be not written after boot up. */ + return_if_fail(first_boot == FX_TRUE); + first_boot = FX_FALSE; + } + } + else + { + + /* Only one file could be empty after boot up. */ + return_if_fail(first_boot == FX_TRUE); + first_boot = FX_FALSE; + } + + status = fx_file_close(&my_file[i]); + return_if_fail(status == FX_SUCCESS); + } + } + + for(i = 0; i < NO_OF_FILES; i++) + { + + sprintf(fileName, "TEST%d.TXT", i); + status = fx_file_open(&ram_disk, &my_file[i], fileName, FX_OPEN_FOR_WRITE); + if (!file_created[i]) + { + return_if_fail(status == FX_NOT_FOUND); + + /* File has not been created since format. */ + status = fx_file_create(&ram_disk, fileName); + return_if_fail(status == FX_SUCCESS); + file_created[i] = FX_TRUE; + + status = fx_file_open(&ram_disk, &my_file[i], fileName, FX_OPEN_FOR_WRITE); + return_if_fail(status == FX_SUCCESS); + } + else + { + status = fx_file_truncate_release(&my_file[i], 0); + return_if_fail(status == FX_SUCCESS); + } + + + for (itr = 0; itr < (FILE_SIZE[i] / 32); itr++) + { + memcpy(&write_buffer[itr * 32], local_buffer2, 32); + } + + fx_file_seek(&my_file[i], 0); + status = fx_file_write(&my_file[i], write_buffer, FILE_SIZE[i]); + return_if_fail(status == FX_SUCCESS); + + status = fx_file_close(&my_file[i]); + return_if_fail(status == FX_SUCCESS); + } + + driver_write_callback = my_driver_write_callback; + } + + printf("SUCCESS!\n"); + test_control_return(0); +} + +static void ftest_1_entry(ULONG thread_input) +{ + +UINT ticks; + + FX_PARAMETER_NOT_USED(thread_input); + + for (;;) + { + + ticks = ((tx_time_get() % (ULONG)(RAND)) + 10); +#ifdef DEBUG + printf("\nSleep %u ticks\n", ticks); +#endif /* DEBUG */ + tx_thread_sleep(ticks); + + restart_thread_0(); + } +} + +static void restart_thread_0() +{ +TX_MUTEX *mutex_ptr; + + tx_thread_terminate(&ftest_0); + tx_thread_reset(&ftest_0); + + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Delete all mutexes (except for system mutex). */ + while(_tx_mutex_created_ptr) + { + + /* Setup working mutex pointer. */ + mutex_ptr = _tx_mutex_created_ptr; + +#ifdef __ghs + + /* Determine if the mutex is the GHS system mutex. If so, don't delete! */ + if (mutex_ptr == &__ghLockMutex) + { + + /* Move to next mutex. */ + mutex_ptr = mutex_ptr -> tx_mutex_created_next; + } + + /* Determine if there are no more mutexes to delete. */ + if (_tx_mutex_created_count == 1) + break; +#endif + + /* Delete mutex. */ + tx_mutex_delete(mutex_ptr); + } + + restart++; + tx_thread_resume(&ftest_0); +} + +static UINT my_driver_write_callback(FX_MEDIA *media_ptr, + UINT sector_type, + UCHAR *block_ptr, + UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(media_ptr); + FX_PARAMETER_NOT_USED(sector_type); + FX_PARAMETER_NOT_USED(block_ptr); + FX_PARAMETER_NOT_USED(operation_ptr); + + if ((rand() % WRITE_INTERRUPT_RATE) < 1) + { +#ifdef DEBUG + printf("\nWrite interrupted\n"); +#endif /* DEBUG */ + + /* Restart test by waking up thread 1. */ + tx_thread_wait_abort(&ftest_1); + tx_thread_suspend(&ftest_0); + } + return(FX_SUCCESS); +} + +static VOID my_fault_tolerant_enable_callback(FX_MEDIA *media_ptr, + UCHAR *fault_tolerant_memory_buffer, + ULONG log_size) +{ + + FX_PARAMETER_NOT_USED(media_ptr); + FX_PARAMETER_NOT_USED(fault_tolerant_memory_buffer); + FX_PARAMETER_NOT_USED(log_size); + +#ifdef DEBUG + if (log_size != (FX_FAULT_TOLERANT_LOG_HEADER_SIZE + FX_FAULT_TOLERANT_FAT_CHAIN_SIZE)) + { + return; + } + + if (_fx_utility_32_unsigned_read(fault_tolerant_memory_buffer + 16) == media_ptr -> fx_media_fat_last) + { + printf("\nInvalid insertion_front detected.\n"); + } +#endif /* DEBUG */ +} + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_corruption_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Corruption Test....................N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_create_directory_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_create_directory_interrupt_test.c new file mode 100644 index 0000000..8b937f6 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_create_directory_interrupt_test.c @@ -0,0 +1,764 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check directory interrupt for fx_file_create() : +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 26 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to create TEST3.TXT; +Step7: Terminate the new thread to simulate poweroff when update directory. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(redo operation); +Step10: Check the test files. +*/ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_create_directory_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT directory_write_interrupt = FX_FALSE; +static CHAR read_buffer[1024]; +static UINT read_buffer_size = 1024; + +#define TEST_COUNT 3 + + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_create_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Create DIR Interrupt Test.........."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " 012345678901234567890123\n", 26); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + directory_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST3.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(37); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_END_OF_FILE) || (actual != 0)) + { + + printf("ERROR!\n"); + test_control_return(38); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(39); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (directory_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(40); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Create a file called TEST3.TXT in the root directory. */ + fx_file_create(&ram_disk, "TEST3.TXT"); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the Directory write operation after record the redo log. */ + if ((sector_type == FX_DIRECTORY_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + directory_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_create_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Create DIR Interrupt Test..........N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_create_redo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_create_redo_log_interrupt_test.c new file mode 100644 index 0000000..8da4eeb --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_create_redo_log_interrupt_test.c @@ -0,0 +1,730 @@ +/* This FileX test concentrates on the Fault-Tolerant redo LOG write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check redo log interrupt for fx_file_create() : +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 26 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to create TEST3.TXT; +Step7: Terminate the new thread to simulate poweroff when update the redo log. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(undo operation); +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_create_redo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT log_write_interrupt = FX_FALSE; +static CHAR read_buffer[1024]; +static UINT read_buffer_size = 1024; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_create_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Create Redo LOG Interrupt Test....."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " 012345678901234567890123\n", 26); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST3.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status == FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (log_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(37); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Create a file called TEST3.TXT in the root directory. */ + fx_file_create(&ram_disk, "TEST3.TXT"); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the redo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_create_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Create Redo LOG Interrupt Test.....N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_delete_directory_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_delete_directory_interrupt_test.c new file mode 100644 index 0000000..d3798e9 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_delete_directory_interrupt_test.c @@ -0,0 +1,686 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check directory interrupt for fx_file_delete(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 26 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to delete TEST2.TXT; +Step7: Terminate the new thread to simulate poweroff when update the directory after record the redo log. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(redo operation); +Step10: Check the test files. +*/ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_delete_directory_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT directory_write_interrupt = FX_FALSE; +static CHAR read_buffer[1024]; +static UINT read_buffer_size = 1024; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_delete_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Delete DIR Interrupt Test.........."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " 012345678901234567890123\n", 26); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + directory_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status == FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (directory_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Create a file called TEST2.TXT in the root directory. */ + fx_file_delete(&ram_disk, "TEST2.TXT"); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the Directory write operation after record the redo log. */ + if ((sector_type == FX_DIRECTORY_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + directory_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_delete_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Delete DIR Interrupt Test..........N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_delete_fat_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_delete_fat_interrupt_test.c new file mode 100644 index 0000000..d49db3f --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_delete_fat_interrupt_test.c @@ -0,0 +1,687 @@ +/* This FileX test concentrates on the Fault-Tolerant FAT write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check fat interrupt for fx_file_delete(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 26 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to delete TEST2.TXT; +Step7: Terminate the new thread to simulate poweroff when update the fat after record the redo log. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(redo operation); +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_delete_fat_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT fat_write_interrupt = FX_FALSE; +static CHAR read_buffer[1024]; +static UINT read_buffer_size = 1024; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_delete_fat_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Delete FAT Interrupt Test.........."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " 012345678901234567890123\n", 26); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + fat_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); + +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status == FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (fat_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); + +} + +/* Define the test threads. */ +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Create a file called TEST2.TXT in the root directory. */ + fx_file_delete(&ram_disk, "TEST2.TXT"); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the FAT write operation after record the redo log. */ + if ((sector_type == FX_FAT_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + fat_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_delete_fat_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Delete FAT Interrupt Test..........N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_delete_fat_multiple_sectors_test.c b/test/regression_test/filex_fault_tolerant_file_delete_fat_multiple_sectors_test.c new file mode 100644 index 0000000..c3207d5 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_delete_fat_multiple_sectors_test.c @@ -0,0 +1,481 @@ +/* This FileX test concentrates on the Fault-Tolerant FAT write interrupt operation. */ +/* +For FAT 12, one cluster size is 1024 bytes; + +Check fat interrupt for fx_file_delete(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and allocate 330 clusters (8-337) for TEST.TXT; +Step4: Create and allocate 10 clusters (338-347) for TEST2.TXT; +Step5: Adjust FAT chain of TEST2.TXT; +Step6: Create new thread to delete TEST2.TXT; +Step7: Terminate the new thread to simulate poweroff when update the FAT entry 0x155 after record the redo log. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(redo operation); +Step10: Check the TEST.TXT. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_utility.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_delete_fat_multple_sectors_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT fat_write_interrupt = FX_FALSE; +static CHAR read_buffer[1024]; +static UINT read_buffer_size = 1024; + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_delete_fat_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Delete FAT Multiple Sectors Test..."); + + + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 512, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Allocate file for 335 clusters. */ + status = fx_file_allocate(&my_file, 330 * 512); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create another file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Allocate file for 10 clusters. + The cluster 341 will be occupied which spans two sectors. */ + status = fx_file_allocate(&my_file, 10 * 512); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Adjust FAT chain to trigger the issue. + Original FAT chain: 0x152->0x153->0x154->0x155(multiple sectors)->0x156->0x157... + Modified FAT chain: 0x152->0x153->0x154->0x156->0x155(multiple sectors)->0x157... */ + _fx_utility_FAT_entry_write(&ram_disk, 0x154, 0x156); + _fx_utility_FAT_entry_write(&ram_disk, 0x156, 0x155); + _fx_utility_FAT_entry_write(&ram_disk, 0x155, 0x157); + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + fat_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); + +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Validate bug here. When the FAT entry spans two sectors and not flushed at the same time, the FAT chain will be like this: + 0x152->0x153->0x154->0x156->0x155(multiple sectors)->0x150(error)... + FAT entry 0x150 belongs to TEST.TXT thus the file is corrupted. */ + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file and make sure it is not corrupted. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (fat_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Create a file called TEST.TXT in the root directory. */ + fx_file_delete(&ram_disk, "TEST2.TXT"); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ +static UCHAR *last_block_ptr = FX_NULL; + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the FAT write operation after record the redo log. */ + if ((sector_type == FX_FAT_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Skip FAT operations not spaning two sectors. */ + if (last_block_ptr != (ram_disk_memory_large + 512)) + { + last_block_ptr = block_ptr; + return FX_SUCCESS; + } + last_block_ptr = block_ptr; + if (block_ptr != (ram_disk_memory_large + 512 * 2)) + { + return FX_SUCCESS; + } + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + fat_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_delete_fat_multple_sectors_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Delete FAT Multiple Sectors Test...N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_delete_redo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_delete_redo_log_interrupt_test.c new file mode 100644 index 0000000..2e1dfc0 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_delete_redo_log_interrupt_test.c @@ -0,0 +1,718 @@ +/* This FileX test concentrates on the Fault-Tolerant redo LOG write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check redo log interrupt for fx_file_delete(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 26 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to delete TEST2.TXT; +Step7: Terminate the new thread to simulate poweroff when update the redo log; +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(undo operation); +Step10: Check the test files. +*/ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_delete_redo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT log_write_interrupt = FX_FALSE; +static CHAR read_buffer[1024]; +static UINT read_buffer_size = 1024; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_delete_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Delete Redo LOG Interrupt Test....."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " 012345678901234567890123\n", 26); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (log_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Create a file called TEST2.TXT in the root directory. */ + fx_file_delete(&ram_disk, "TEST2.TXT"); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the redo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_delete_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Delete Redo LOG Interrupt Test.....N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_delete_test.c b/test/regression_test/filex_fault_tolerant_file_delete_test.c new file mode 100644 index 0000000..9298bb3 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_delete_test.c @@ -0,0 +1,428 @@ +/* This FileX test is determined to cover filex_fault_tolerant_file_delete */ + + + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_delete_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT fat_write_interrupt = FX_FALSE; +static CHAR read_buffer[1024]; +static UINT read_buffer_size = 1024; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT(*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Create a terrible driver. */ +static UINT driver_called_counter = 0; +static void _fx_terrible_driver(FX_MEDIA *media_ptr) +{ + driver_called_counter++; + // printf("\n_fx_terrible_driver has been called %d times.", driver_called_counter); + if (driver_called_counter == 1) + { + media_ptr->fx_media_driver_status = FX_IO_ERROR; + return; + } + (*_fx_ram_driver)(media_ptr); +} + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_delete_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *)first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + + UINT status; + ULONG actual; + UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Delete Test........................"); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); + + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + +#ifndef FX_STANDALONE_ENABLE + /* Create the main thread. */ + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + /* directory_write_interrupt */ + fat_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); + +} + + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Register our terrible dirver to make IO ERROR at a particular time. */ + ram_disk.fx_media_driver_entry = _fx_terrible_driver; + + /* Create a file called TEST2.TXT in the root directory. */ + status=fx_file_delete(&ram_disk, "TEST2.TXT"); + return_value_if_fail(status == FX_IO_ERROR, 6); + + /* Unregister our terrible driver. */ + ram_disk.fx_media_driver_entry = _fx_ram_driver; +} + + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the FAT write operation after record the redo log. */ + if ((sector_type == FX_FAT_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + fat_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr->fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_delete_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Delete Test........................N/A\n"); + + test_control_return(255); +} +#endif + + diff --git a/test/regression_test/filex_fault_tolerant_file_delete_undo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_delete_undo_log_interrupt_test.c new file mode 100644 index 0000000..0fe941a --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_delete_undo_log_interrupt_test.c @@ -0,0 +1,716 @@ +/* This FileX test concentrates on the Fault-Tolerant undo LOG write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check undo log interrupt for fx_file_delete(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 26 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to delete TEST2.TXT; +Step7: Terminate the new thread to simulate poweroff when update the undo log; +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(undo operation); +Step10: Check the test files. +*/ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_delete_undo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT log_write_interrupt = FX_FALSE; +static CHAR read_buffer[1024]; +static UINT read_buffer_size = 1024; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_delete_undo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Delete Undo LOG Interrupt Test....."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " 012345678901234567890123\n", 26); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (log_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Create a file called TEST2.TXT in the root directory. */ + fx_file_delete(&ram_disk, "TEST2.TXT"); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the undo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_UNDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_delete_undo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Delete Undo LOG Interrupt Test.....N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_random_seek_test.c b/test/regression_test/filex_fault_tolerant_file_random_seek_test.c new file mode 100644 index 0000000..d28bd37 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_random_seek_test.c @@ -0,0 +1,286 @@ +/* This FileX test concentrates on the Fault-Tolerant file random seek operation. */ +/* +1. Format and open the media; +2. Enable fault tolerant feature; +3. Create and write 16K bytes into TEST.TXT; +4. Seek to the random position of the file; +5. Write random size of data into TEST.TXT; +6. Verify the data; +7. Loop 4 - 6 10000 times. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_random_seek_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE (16 * 1024) +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + +#define TOTAL_SIZE (16 * 1024) + +#define OVERWRITE_LOOP 100000 + + +/* Define the ThreadX and FileX object control blocks... */ +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +static CHAR write_buffer[TOTAL_SIZE]; +static CHAR overwrite_buffer[TOTAL_SIZE]; +static CHAR read_buffer[TOTAL_SIZE]; + +#define TEST_COUNT 4 /* FAT12, 16, 32, 16. */ + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_random_seek_test_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; +UINT j; +UINT seek_position; +UINT overwrite_position; +UINT overwrite_size; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Random Seek Test..................."); + + srand((UINT)time(0)); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + + /* Initialize the write and overwrite_buffer. */ + for (j = 0; j < sizeof(write_buffer); j++) + { + write_buffer[j] = (CHAR)(rand() & 0xFF); + overwrite_buffer[j] = (CHAR)(rand() & 0xFF); + } + + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 512, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 512, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 512, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 3) + { + /* Format the media with FAT16 with sector size 2048 bytes. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 2048, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + return_if_fail(status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail(status == FX_SUCCESS); + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail(status == FX_SUCCESS); + + /* Prepare a file for test. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + return_if_fail(status == FX_SUCCESS); + + status = fx_file_create(&ram_disk, "TEST.TXT"); + return_if_fail(status == FX_ALREADY_CREATED); + + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + return_if_fail(status == FX_SUCCESS); + + /* Write data. */ + status = fx_file_write(&my_file, &write_buffer[0], TOTAL_SIZE); + return_if_fail(status == FX_SUCCESS); + + for (j = 0; j < OVERWRITE_LOOP; j++) + { + seek_position = (UINT)(rand() % TOTAL_SIZE); + overwrite_position = (UINT)(rand() % TOTAL_SIZE); + if (seek_position > overwrite_position) + { + overwrite_size = (UINT)rand() % (TOTAL_SIZE - seek_position); + } + else + { + overwrite_size = (UINT)rand() % (TOTAL_SIZE - overwrite_position); + } + + /* Seek to the beginning of file. */ + status = fx_file_relative_seek(&my_file, seek_position, FX_SEEK_BEGIN); + return_if_fail(status == FX_SUCCESS); + + /* Overwrite the data. */ + memcpy(&write_buffer[seek_position], &overwrite_buffer[overwrite_position], + overwrite_size); + status = fx_file_write(&my_file, &overwrite_buffer[overwrite_position], overwrite_size); + return_if_fail(status == FX_SUCCESS); + + status = fx_file_relative_seek(&my_file, 0, FX_SEEK_BEGIN); + return_if_fail(status == FX_SUCCESS); + + /* Read the bytes of the test file. */ + memset(read_buffer, 0xFF, sizeof(read_buffer)); + status = fx_file_read(&my_file, read_buffer, sizeof(read_buffer), &actual); + return_if_fail((status == FX_SUCCESS) && (actual == sizeof(read_buffer))); + + /* Check the data of overwrite part. */ + return_if_fail(memcmp(&read_buffer[0], &write_buffer[0], sizeof(write_buffer)) == 0); + } + + /* Close the file. */ + status = fx_file_close(&my_file); + return_if_fail(status == FX_SUCCESS); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail(status == FX_SUCCESS); + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_random_seek_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Random Seek Test...................N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_rename_directory_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_rename_directory_interrupt_test.c new file mode 100644 index 0000000..a389201 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_rename_directory_interrupt_test.c @@ -0,0 +1,721 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check directory interrupt for fx_file_rename(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 26 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to rename TEST2.TXT to TEST21.TXT; +Step7: Terminate the new thread to simulate poweroff when update the directory after record the redo log. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(redo operation); +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_rename_directory_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT directory_write_interrupt = FX_FALSE; +static CHAR read_buffer[1024]; +static UINT read_buffer_size = 1024; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_rename_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Rename DIR Interrupt Test.........."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " 012345678901234567890123\n", 26); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + directory_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST21.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (directory_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + /* Delete the thread. */ + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Rename the file name. */ + fx_file_rename(&ram_disk, "TEST2.TXT", "TEST21.TXT"); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the directory write operation after record the redo log. */ + if ((sector_type == FX_DIRECTORY_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + directory_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_rename_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Rename DIR Interrupt Test..........N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_rename_redo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_rename_redo_log_interrupt_test.c new file mode 100644 index 0000000..6f8909a --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_rename_redo_log_interrupt_test.c @@ -0,0 +1,720 @@ +/* This FileX test concentrates on the Fault-Tolerant redo LOG write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check redo log interrupt for fx_file_rename(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 26 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to rename TEST2.TXT to TEST21.TXT; +Step7: Terminate the new thread to simulate poweroff when update the redo log. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(undo operation); +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_rename_redo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT log_write_interrupt = FX_FALSE; +static CHAR read_buffer[1024]; +static UINT read_buffer_size = 1024; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_rename_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Rename Redo LOG Interrupt Test....."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " 012345678901234567890123\n", 26); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (log_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Rename the file name. */ + fx_file_rename(&ram_disk, "TEST2.TXT", "TEST21.TXT"); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the redo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_rename_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Rename Redo LOG Interrupt Test.....N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_seek_test.c b/test/regression_test/filex_fault_tolerant_file_seek_test.c new file mode 100644 index 0000000..385753d --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_seek_test.c @@ -0,0 +1,253 @@ +/* This FileX test concentrates on the Fault-Tolerant file seek operation. */ +/* +1. Format and open the media; +2. Enable fault tolerant feature; +3. Create and write 1024 bytes into TEST.TXT; +4. Seek to the beginning of the file; +5. Write 640 bytes into TEST.TXT; +6. Verify the data; +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_seek_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE (16 * 1024) +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE +#define TOTAL_SIZE 1024 +#define OVERWRITE_SIZE 512 + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +static UCHAR *thread_buffer; +static CHAR write_buffer[2][TOTAL_SIZE]; +static CHAR read_buffer[TOTAL_SIZE]; + +#define TEST_COUNT 3 /* FAT12, 16, 32. */ + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_seek_test_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; +UINT j; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Seek Test.........................."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + + /* Initialize the write and read buffer. */ + for (j = 0; j < sizeof(write_buffer[0]); j++) + { + write_buffer[0][j] = (CHAR)(rand() & 0xFF); + write_buffer[1][j] = (CHAR)(~write_buffer[0][j] & 0xFF); + } + + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 512, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 512, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 512, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + return_if_fail(status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail(status == FX_SUCCESS); + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail(status == FX_SUCCESS); + + /* Prepare a file for test. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + return_if_fail(status == FX_SUCCESS); + + status = fx_file_create(&ram_disk, "TEST.TXT"); + return_if_fail(status == FX_ALREADY_CREATED); + + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + return_if_fail(status == FX_SUCCESS); + + /* Write data. */ + status = fx_file_write(&my_file, write_buffer[0], TOTAL_SIZE); + return_if_fail(status == FX_SUCCESS); + + /* Seek to the beginning of file. */ + status = fx_file_relative_seek(&my_file, 0, FX_SEEK_BEGIN); + return_if_fail(status == FX_SUCCESS); + + /* Overwrite the beginning data. */ + status = fx_file_write(&my_file, write_buffer[1], OVERWRITE_SIZE); + return_if_fail(status == FX_SUCCESS); + + status = fx_file_relative_seek(&my_file, 0, FX_SEEK_BEGIN); + return_if_fail(status == FX_SUCCESS); + + /* Read the bytes of the test file. */ + memset(read_buffer, 0xFF, sizeof(read_buffer)); + status = fx_file_read(&my_file, read_buffer, sizeof(read_buffer), &actual); + return_if_fail((status == FX_SUCCESS) && (actual == sizeof(read_buffer))); + + /* Check the data of overwrite part. */ + return_if_fail(memcmp(&read_buffer[0], write_buffer[1], OVERWRITE_SIZE) == 0); + + /* Check the data of remaining part. */ + return_if_fail(memcmp(&read_buffer[OVERWRITE_SIZE], + &write_buffer[0][OVERWRITE_SIZE], + (TOTAL_SIZE - OVERWRITE_SIZE)) == 0); + + /* Close the file. */ + status = fx_file_close(&my_file); + return_if_fail(status == FX_SUCCESS); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail(status == FX_SUCCESS); + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_seek_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Seek Test..........................N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_truncate_directory_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_truncate_directory_interrupt_test.c new file mode 100644 index 0000000..6af023a --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_truncate_directory_interrupt_test.c @@ -0,0 +1,737 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check directory interrupt for fx_file_truncate(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 2048 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to truncate the TEST1.TXT file from 2048 to 26; +Step7: Terminate the new thread to simulate poweroff when update the directory. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(redo operation); +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_truncate_directory_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT directory_write_interrupt = FX_FALSE; +static CHAR write_buffer[2048]; +static UINT write_buffer_size = 2048; +static CHAR read_buffer[2048]; +static UINT read_buffer_size = 2048; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_truncate_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Truncate DIR Interrupt Test........"); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, (void *) write_buffer, write_buffer_size); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != write_buffer_size)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + directory_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (directory_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif +UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Truncate the "TEST1.TXT" file from 2048 to 26. */ + fx_file_truncate(&my_file, 26); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the Directory write operation after record the redo log. */ + if ((sector_type == FX_DIRECTORY_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + directory_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_truncate_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Truncate DIR Interrupt Test........N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_truncate_redo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_truncate_redo_log_interrupt_test.c new file mode 100644 index 0000000..5fa1ad5 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_truncate_redo_log_interrupt_test.c @@ -0,0 +1,467 @@ +/* This FileX test concentrates on the Fault-Tolerant redo log write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check redo log interrupt for fx_file_truncate(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 2048 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to truncate the TEST1.TXT file from 2048 to 26; +Step7: Terminate the new thread to simulate poweroff when update the redo log. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(undo operation); +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_truncate_redo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_counter = 0; +static UINT log_write_interrupt = FX_FALSE; +static CHAR write_buffer[2048]; +static UINT write_buffer_size = 2048; +static CHAR read_buffer[2048]; +static UINT read_buffer_size = 2048; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_truncate_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Truncate Redo LOG Interrupt Test..."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Create a file called TEST.TXT in the root directory. */ + status += fx_file_create(&ram_disk, "TEST.TXT"); + + /* Open the test file. */ + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Write a string to the test file. */ + status += fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Seek to the beginning of the test file. */ + status += fx_file_seek(&my_file, 0); + return_if_fail( status == FX_SUCCESS); + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + return_if_fail( (status == FX_SUCCESS) && (actual == 28)); + + /* Close the test file. */ + status = fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + /* Apply similar operations to TEST1.TXT. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, (void *) write_buffer, write_buffer_size); + status += fx_file_seek(&my_file, 0); + return_if_fail( status == FX_SUCCESS); + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + return_if_fail( (status == FX_SUCCESS) && (actual == write_buffer_size)); + + /* Close the test file. */ + status = fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + /* Apply similar operations to TEST2.TXT. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + status += fx_file_seek(&my_file, 0); + return_if_fail( status == FX_SUCCESS); + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + return_if_fail( (status == FX_SUCCESS) && (actual == 19)); + + /* Close the test file. */ + status = fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Access TEST.TXT. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_seek(&my_file, 0); + return_if_fail( status == FX_SUCCESS); + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + return_if_fail( (status == FX_SUCCESS) && (actual == 28)); + + /* Close the test file. */ + status = fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + /* Access TEST1.TXT. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_seek(&my_file, 0); + return_if_fail( status == FX_SUCCESS); + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + return_if_fail( (status == FX_SUCCESS) && (actual == write_buffer_size)); + + /* Close the test file. */ + status = fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + /* Access TEST2.TXT. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_seek(&my_file, 0); + return_if_fail( status == FX_SUCCESS); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + return_if_fail( (status == FX_SUCCESS) && (actual == 19)); + + /* Close the test file. */ + status = fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( (status == FX_SUCCESS) && (log_write_interrupt == FX_TRUE) && (error_counter == 0)); + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif +UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + error_counter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Truncate the "TEST1.TXT" file from 2048 to 26. */ + fx_file_truncate_release(&my_file, 26); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the redo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_truncate_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Truncate Redo LOG Interrupt Test...N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_truncate_release_available_test.c b/test/regression_test/filex_fault_tolerant_file_truncate_release_available_test.c new file mode 100644 index 0000000..926e9f3 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_truncate_release_available_test.c @@ -0,0 +1,371 @@ +/* This FileX test concentrates on the Fault-Tolerant truncate release operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check directory interrupt for fx_file_truncate_release(): +1: Format and open the media; +2. Create and write 4K bytes into TEST.TXT; +3. Close file and close media; +4. Open the media; +5. Enable fault tolerant feature; +6. Check available bytes; +7. Truncate release to size 0 of TEST.TXT; +8. Write 4K into TEST.TXT; +9. Check available bytes. It should be the same as the one at (6); +10. Close the media; +11. Open the media; +12. Check available bytes. It should be the same as the one at (6). +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_truncate_release_available_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +#define FILE_SIZE 4096 + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static CHAR write_buffer[FILE_SIZE]; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_truncate_release_available_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT i; +ULONG available_bytes[3]; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Truncate Release Available Test...."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + return_if_fail(status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail(status == FX_SUCCESS); + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + return_if_fail(status == FX_SUCCESS); + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + return_if_fail(status == FX_SUCCESS); + + /* Write 4K bytes into the test file. */ + status = fx_file_write(&my_file, write_buffer, sizeof(write_buffer)); + return_if_fail(status == FX_SUCCESS); + + /* Close the test file. */ + status = fx_file_close(&my_file); + return_if_fail(status == FX_SUCCESS); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail(status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail(status == FX_SUCCESS); + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail(status == FX_SUCCESS); + + /* Get available_bytes before truncate release. */ + status = fx_media_space_available(&ram_disk, &available_bytes[0]); + return_if_fail(status == FX_SUCCESS); + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + return_if_fail(status == FX_SUCCESS); + + /* Truncate the "TEST.TXT" file from 4K to 0. */ + fx_file_truncate_release(&my_file, 0); + return_if_fail(status == FX_SUCCESS); + + /* Write 4K bytes into the test file. */ + status = fx_file_write(&my_file, write_buffer, sizeof(write_buffer)); + return_if_fail(status == FX_SUCCESS); + + /* Close the test file. */ + status = fx_file_close(&my_file); + return_if_fail(status == FX_SUCCESS); + + /* Get available_bytes before truncate release. */ + status = fx_media_space_available(&ram_disk, &available_bytes[1]); + return_if_fail(status == FX_SUCCESS); + + /* Check available bytes. */ + return_if_fail(available_bytes[0] == available_bytes[1]); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail(status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail(status == FX_SUCCESS); + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail(status == FX_SUCCESS); + + /* Get available_bytes after write. */ + status = fx_media_space_available(&ram_disk, &available_bytes[2]); + return_if_fail(status == FX_SUCCESS); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail(status == FX_SUCCESS); + + /* Check available bytes. */ + return_if_fail(available_bytes[0] == available_bytes[2]); + } + + /* for coverage */ + + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + status += fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + status += fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_allocate(&my_file, 4 * 512); + return_if_fail(status == FX_SUCCESS); + + /* Fail while setting undo log. */ + _fx_utility_logical_sector_write_error_request = 1; + status = fx_file_truncate_release(&my_file, 0); + return_if_fail(status == FX_IO_ERROR); + _fx_utility_logical_sector_write_error_request = 0; + + fx_file_close(&my_file); + fx_media_close(&ram_disk); + + for (i = 1; i < 100; i++) + { + + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + status += fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + status += fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, write_buffer, sizeof(write_buffer)); + status += fx_file_close(&my_file); + status += fx_file_create(&ram_disk, "AAA.TXT"); + status += fx_file_open(&ram_disk, &my_file, "AAA.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, write_buffer, sizeof(write_buffer)); + status += fx_file_close(&my_file); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_allocate(&my_file, 512); + return_if_fail(status == FX_SUCCESS); + + _fx_utility_fat_entry_read_error_request = i; + status = fx_file_truncate_release(&my_file, 256); + return_if_fail((status == FX_IO_ERROR) || (i > 6)); + _fx_utility_fat_entry_read_error_request = 0; + + fx_file_close(&my_file); + fx_media_close(&ram_disk); + + if (status != FX_IO_ERROR) + { + break; + } + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_truncate_release_available_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Truncate Release Available Test....N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_truncate_release_directory_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_truncate_release_directory_interrupt_test.c new file mode 100644 index 0000000..920b89f --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_truncate_release_directory_interrupt_test.c @@ -0,0 +1,738 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check directory interrupt for fx_file_truncate_release(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 2048 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to truncate the TEST1.TXT file from 2048 to 26; +Step7: Terminate the new thread to simulate poweroff when update the directory. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(redo operation); +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_truncate_release_directory_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT directory_write_interrupt = FX_FALSE; +static CHAR write_buffer[2048]; +static UINT write_buffer_size = 2048; +static CHAR read_buffer[2048]; +static UINT read_buffer_size = 2048; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_truncate_release_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Truncate R DIR Interrupt Test......"); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, (void *) write_buffer, write_buffer_size); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != write_buffer_size)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + directory_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (directory_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + +UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Truncate the "TEST1.TXT" file from 2048 to 26. */ + fx_file_truncate_release(&my_file, 26); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the Directory write operation after record the log. */ + if ((sector_type == FX_DIRECTORY_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + directory_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_truncate_release_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Truncate R DIR Interrupt Test......N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_truncate_release_fat_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_truncate_release_fat_interrupt_test.c new file mode 100644 index 0000000..020e1cc --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_truncate_release_fat_interrupt_test.c @@ -0,0 +1,738 @@ +/* This FileX test concentrates on the Fault-Tolerant FAT write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check fat interrupt for fx_file_truncate_release(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 2048 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to truncate the TEST1.TXT file from 2048 to 26; +Step7: Terminate the new thread to simulate poweroff when update the FAT after record the redo log. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(redo operation); +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_truncate_release_fat_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT fat_write_interrupt = FX_FALSE; +static CHAR write_buffer[4096]; +static UINT write_buffer_size = 4096; +static CHAR read_buffer[4096]; +static UINT read_buffer_size = 4096; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_truncate_release_fat_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Truncate R FAT Interrupt Test......"); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, (void *) write_buffer, write_buffer_size); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != write_buffer_size)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + fat_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (fat_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Truncate the "TEST1.TXT" file from 1024 to 26. */ + fx_file_truncate_release(&my_file, 26); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the FAT write operation after record the undo log. */ + if ((sector_type == FX_FAT_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + fat_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_truncate_release_fat_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Truncate R FAT Interrupt Test......N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_truncate_release_redo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_truncate_release_redo_log_interrupt_test.c new file mode 100644 index 0000000..cc874e7 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_truncate_release_redo_log_interrupt_test.c @@ -0,0 +1,753 @@ +/* This FileX test concentrates on the Fault-Tolerant log write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check redo log interrupt for fx_file_truncate_release(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 2048 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to truncate TEST1.TXT file from 2048 to 26; +Step7: Terminate the new thread to simulate poweroff when update the undo log. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(undo operation); +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_truncate_release_redo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT log_write_interrupt = FX_FALSE; +static CHAR write_buffer[2048]; +static UINT write_buffer_size = 2048; +static CHAR read_buffer[2048]; +static UINT read_buffer_size = 2048; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_truncate_release_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Truncate R Redo LOG Interrupt Test."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, (void *) write_buffer, write_buffer_size); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != write_buffer_size)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != write_buffer_size)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (log_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + +UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Truncate the "TEST1.TXT" file from 2048 to 26. */ + fx_file_truncate_release(&my_file, 26); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the redo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_truncate_release_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Truncate R Redo LOG Interrupt Test.N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_truncate_release_undo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_truncate_release_undo_log_interrupt_test.c new file mode 100644 index 0000000..e12d13f --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_truncate_release_undo_log_interrupt_test.c @@ -0,0 +1,794 @@ +/* This FileX test concentrates on the Fault-Tolerant undo log write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check undo log interrupt for fx_file_truncate_release(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 2048 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to truncate TEST1.TXT file from 2048 to 0; +Step7: Terminate the new thread to simulate poweroff when update the undo log. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(undo operation); +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_truncate_release_undo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT log_write_interrupt = FX_FALSE; +static CHAR write_buffer[2048]; +static UINT write_buffer_size = 2048; +static CHAR read_buffer[2048]; +static UINT read_buffer_size = 2048; +static UCHAR sector_check[512]; +static UCHAR fat_buffer[256 * 1100]; +static ULONG64 sector_check_index; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_truncate_release_undo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Truncate R Undo LOG Interrupt Test."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + sector_check_index = 384; + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + sector_check_index = 512; + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, (void *) write_buffer, write_buffer_size); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != write_buffer_size)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Check the fat table. */ + if (i != 3) + { + if (memcmp(fat_buffer, + &ram_disk_memory_large[ram_disk.fx_media_reserved_sectors * ram_disk.fx_media_bytes_per_sector], + (ram_disk.fx_media_data_sector_start - ram_disk.fx_media_reserved_sectors) * ram_disk.fx_media_bytes_per_sector)) + { + + /* The fat table is modified. It should be recovered by fault tolerant. */ + printf("ERROR!\n"); + test_control_return(37); + } + } + + /* Check the sector that is corrupted. */ + if (i < 2) + { + if (memcmp(sector_check, + &ram_disk_memory_large[(sector_check_index + ram_disk.fx_media_hidden_sectors) * ram_disk.fx_media_bytes_per_sector], + ram_disk.fx_media_bytes_per_sector)) + { + + /* The sector is modified. */ + printf("ERROR!\n"); + test_control_return(38); + } + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != write_buffer_size)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (log_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + +UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Store the fat table before truncate release. */ + memcpy(fat_buffer, + &ram_disk_memory_large[ram_disk.fx_media_reserved_sectors * ram_disk.fx_media_bytes_per_sector], + (ram_disk.fx_media_data_sector_start - ram_disk.fx_media_reserved_sectors) * ram_disk.fx_media_bytes_per_sector); + + /* Store a sector that was corrupted after reboot. */ + memcpy(sector_check, + &ram_disk_memory_large[(sector_check_index + ram_disk.fx_media_hidden_sectors) * ram_disk.fx_media_bytes_per_sector], + ram_disk.fx_media_bytes_per_sector); + + /* Truncate the "TEST1.TXT" file from 2048 to 0. */ + fx_file_truncate_release(&my_file, 0); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the undo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & (FX_FAULT_TOLERANT_LOG_REDO_UPDATING))) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_truncate_release_undo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Truncate R Undo LOG Interrupt Test.N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_write_available_test.c b/test/regression_test/filex_fault_tolerant_file_write_available_test.c new file mode 100644 index 0000000..c86d323 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_write_available_test.c @@ -0,0 +1,609 @@ +/* This FileX test concentrates on the Fault-Tolerant redo log write interrupt operation. + * The available bytes before/after file write should be decreased write_buffer_size only. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_write_available_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT log_write_interrupt = FX_FALSE; +static UINT write_index; +static UINT i; + +static CHAR write_buffer[2048]; +static UINT write_buffer_size = 2048; +static CHAR read_buffer[4096]; +static UINT read_buffer_size = 4096; + +#define SEEK_COUNT 5 +#define FAT_COUNT 3 /* FAT12, 16, 32. */ +#define TEST_COUNT FAT_COUNT * SEEK_COUNT + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_write_available_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +ULONG64 available_bytes_before_write; +ULONG64 available_bytes_after_write; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Write Available Test..............."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i < 5) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i < 10) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i < 15) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 4, // Total sectors + 512, // Sector size + 4, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Get available bytes before write. */ + fx_media_extended_space_available(&ram_disk, &available_bytes_before_write); + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Get available bytes before write. */ + fx_media_extended_space_available(&ram_disk, &available_bytes_after_write); + if (available_bytes_before_write != (available_bytes_after_write + + ram_disk.fx_media_fault_tolerant_clusters * ram_disk.fx_media_bytes_per_sector * ram_disk.fx_media_sectors_per_cluster)) + { + + printf("ERROR!\n"); + test_control_return(38); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Get available bytes before write. */ + fx_media_extended_space_available(&ram_disk, &available_bytes_before_write); + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Get available bytes after write. */ + fx_media_extended_space_available(&ram_disk, &available_bytes_after_write); + { + if ((available_bytes_before_write - available_bytes_after_write) != 2048) + { + + printf("ERROR!\n"); + test_control_return(37); + } + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Get available bytes before write. */ + fx_media_extended_space_available(&ram_disk, &available_bytes_before_write); + +#ifndef FX_STANDALONE_ENABLE + /* Create the main thread. */ + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + /* directory_write_interrupt */ + log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Get available bytes after write. */ + fx_media_extended_space_available(&ram_disk, &available_bytes_after_write); + { + if ((available_bytes_before_write - available_bytes_after_write) != 2048) + { + + printf("ERROR!\n"); + test_control_return(37); + } + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. FAT 12, 16, 32. */ + if ((status != FX_SUCCESS) || (actual != write_buffer_size) || memcmp(read_buffer, write_buffer, actual)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (log_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + +UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Random genearte the write data. */ + for (write_index = 0; write_index < write_buffer_size; write_index ++) + { + write_buffer[write_index] = (CHAR)rand(); + } + + /* Write 1024 bytes to the file, then update the FAT table. (bytes should be greate than one cluster). */ + fx_file_write(&my_file, (void *) write_buffer, write_buffer_size); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the redo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_write_available_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Write Available Test...............N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_write_data_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_write_data_interrupt_test.c new file mode 100644 index 0000000..9cf94bb --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_write_data_interrupt_test.c @@ -0,0 +1,514 @@ +/* This FileX test concentrates on the Fault-Tolerant data write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + 1024 1024 1024 1024 1024 1024 + |------------|------------|-----------|------------|------------|------------| + | TEST.TXT | TEST1.TXT | TEST1.TXT | TEST2.TXT | | | + |------------|------------|-----------|------------|------------|------------| + | | | | | + | | | | | + Test1 | | | | + Test2 | | | + Test3 | | + Test4 | + | + Test5 + +Check data interrupt for fx_file_write() : +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 1500 bytes into TEST1.TXT, (two cluster); +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to continue to write 1024 data into TEST1.TXT: + Test1: Seek to the file: index 0; + Test2: Seek to the file: index 1023; + Test3: Seek to the file: index 1024; + Test4: Seek to the file: index 1025; + Test5: Seek to the file: index 1500; +Step7: Terminate the new thread to simulate poweroff when write the data between undo log generated and redo log not generated. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(undo operation); +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_write_data_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT data_write_interrupt = FX_FALSE; +static UINT write_index; +static UINT data_size = 0; +static UINT i; +static CHAR write_buffer[2048]; +static UINT write_buffer_size = 2048; +static CHAR read_buffer[4096]; +static UINT read_buffer_size = 4096; +static UCHAR data_buffer[4096]; + +#define SEEK_COUNT 5 +#define FAT_COUNT 3 /* FAT12, 16, 32. */ +#define TEST_COUNT FAT_COUNT * SEEK_COUNT + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_write_data_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Write DATA Interrupt Test.........."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i < 5) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i < 10) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i < 15) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Prepare a file for test. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + status += fx_file_seek(&my_file, 0); + return_if_fail( status == FX_SUCCESS); + + /* Read the bytes of the test file. */ + memset(read_buffer, 0, read_buffer_size); + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + return_if_fail( (status == FX_SUCCESS) && (actual == 28)); + + /* Close the test file. */ + status = fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + /* Write a string to the test file. */ + + /* Random genearte the write data. */ + for (write_index = 0; write_index < 1500; write_index ++) + { + write_buffer[write_index] = (CHAR)rand(); + + /* Store the write data to compare the new data after Data write interrupt. */ + data_buffer[write_index] = (UCHAR)write_buffer[write_index]; + data_size = 1500; + } + + /* Write the data into file. */ + status = fx_file_write(&my_file, (void *) write_buffer, data_size); + status += fx_file_seek(&my_file, 0); + return_if_fail( status == FX_SUCCESS); + + /* Read the bytes of the test file. */ + memset(read_buffer, 0, read_buffer_size); + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + return_if_fail( (status == FX_SUCCESS) && (actual == data_size)); + + /* Close the test file. */ + status = fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + status += fx_file_seek(&my_file, 0); + return_if_fail( status == FX_SUCCESS); + + /* Read the bytes of the test file. */ + memset(read_buffer, 0, read_buffer_size); + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + return_if_fail( (status == FX_SUCCESS) && (actual == 19)); + + /* Close the test file. */ + status = fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + data_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_seek(&my_file, 0); + return_if_fail( status == FX_SUCCESS); + + /* Read the bytes of the test file. */ + memset(read_buffer, 0, read_buffer_size); + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + return_if_fail((status == FX_SUCCESS) && (actual == 28) && !memcmp(read_buffer, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", actual)); + + /* Close the test file. */ + status = fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_seek(&my_file, 0); + return_if_fail( status == FX_SUCCESS); + + /* Read the bytes of the test file. */ + memset(read_buffer, 0, read_buffer_size); + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + return_if_fail((status == FX_SUCCESS) && (actual == data_size) && !memcmp(read_buffer, data_buffer, actual)); + + /* Close the test file. */ + status = fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_seek(&my_file, 0); + return_if_fail( status == FX_SUCCESS); + + /* Read the bytes of the test file. */ + memset(read_buffer, 0, read_buffer_size); + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + return_if_fail((status == FX_SUCCESS) && (actual == 19) && !memcmp(read_buffer, " EXPRESSLOGIC_TEST\n", actual)); + + /* Close the test file. */ + status = fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail((status == FX_SUCCESS) && (data_write_interrupt == FX_TRUE) && (error_couter == 0)); + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Seek to the file. FAT 12, 16, 32. */ + if ((i % SEEK_COUNT) == 0) + status = fx_file_seek(&my_file, 0); + else if ((i % SEEK_COUNT) == 1) + status = fx_file_seek(&my_file, 1023); + else if ((i % SEEK_COUNT) == 2) + status = fx_file_seek(&my_file, 1024); + else if ((i % SEEK_COUNT) == 3) + status = fx_file_seek(&my_file, 1025); + else if ((i % SEEK_COUNT) == 4) + status = fx_file_seek(&my_file, 1500); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Random genearte the write data. */ + for (write_index = 0; write_index < write_buffer_size; write_index ++) + { + write_buffer[write_index] = (CHAR)rand(); + } + + /* Write 1024 bytes to the file, then update the FAT table. (bytes should be greate than one cluster). */ + fx_file_write(&my_file, (void *) write_buffer, write_buffer_size); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the Data write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_UNDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + data_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_write_data_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Write DATA Interrupt Test..........N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_write_directory_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_write_directory_interrupt_test.c new file mode 100644 index 0000000..85e4127 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_write_directory_interrupt_test.c @@ -0,0 +1,819 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + 1024 1024 1024 1024 1024 1024 + |------------|------------|-----------|------------|------------|------------| + | TEST.TXT | TEST1.TXT | TEST1.TXT | TEST2.TXT | | | + |------------|------------|-----------|------------|------------|------------| + | | | | | + | | | | | + Test1 | | | | + Test2 | | | + Test3 | | + Test4 | + | + Test5 + +Check directory interrupt for fx_file_write() : +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 1500 bytes into TEST1.TXT, (two cluster); +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to continue to write 1024 data into TEST1.TXT: + Test1: Seek to the file: index 0; + Test2: Seek to the file: index 1023; + Test3: Seek to the file: index 1024; + Test4: Seek to the file: index 1025; + Test5: Seek to the file: index 1500; +Step7: Terminate the new thread to simulate poweroff when update the directory after redo log generated. +Step8: Open the media; +Step9: Enable fault tolerant feature to cover the data(redo operation); +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_write_directory_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT directory_write_interrupt = FX_FALSE; +static CHAR write_buffer[2048]; +static UINT write_buffer_size = 2048; +static UINT write_index; +static CHAR read_buffer[4096]; +static UINT read_buffer_size = 4096; +static UCHAR data_buffer[4096]; +static UINT data_size = 0; +static UINT i; + +#define SEEK_COUNT 5 +#define FAT_COUNT 3 /* FAT12, 16, 32. */ +#define TEST_COUNT FAT_COUNT * SEEK_COUNT + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_write_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Write DIR Interrupt Test..........."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i < 5) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i < 10) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i < 15) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + + /* Random genearte the write data. */ + for (write_index = 0; write_index < 1500; write_index ++) + { + write_buffer[write_index] = (CHAR)rand(); + + /* Store the write data to compare the new data after Data write interrupt. */ + data_buffer[write_index] = (UCHAR)write_buffer[write_index]; + data_size = 1500; + } + + /* Write the data into file. */ + status = fx_file_write(&my_file, (void *) write_buffer, data_size); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != data_size)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + directory_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. FAT 12, 16, 32. */ + if ((status != FX_SUCCESS) || (actual != data_size) || memcmp(read_buffer, data_buffer, actual)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (directory_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + +UINT status; +UINT index; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Seek to the file. FAT 12, 16, 32. */ + if ((i % SEEK_COUNT) == 0) + { + status = fx_file_seek(&my_file, 0); + index = 0; + } + else if ((i % SEEK_COUNT) == 1) + { + status = fx_file_seek(&my_file, 1023); + index = 1023; + } + else if ((i % SEEK_COUNT) == 2) + { + status = fx_file_seek(&my_file, 1024); + index = 1024; + } + else if ((i % SEEK_COUNT) == 3) + { + status = fx_file_seek(&my_file, 1025); + index = 1025; + } + else if ((i % SEEK_COUNT) == 4) + { + status = fx_file_seek(&my_file, 1500); + index = 1500; + } + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Random genearte the write data. */ + for (write_index = 0; write_index < write_buffer_size; write_index ++) + { + write_buffer[write_index] = (CHAR)rand(); + + /* Store the write data to compare the new data after Data write interrupt. */ + data_buffer[index + write_index] = (UCHAR)write_buffer[write_index]; + data_size = index + write_buffer_size; + } + + /* Write 1024 bytes to the file, then update the FAT table. (bytes should be greate than one cluster). */ + fx_file_write(&my_file, (void *) write_buffer, write_buffer_size); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the Directory write operation after record the redo log. */ + if ((sector_type == FX_DIRECTORY_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + directory_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_write_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Write DIR Interrupt Test...........N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_write_fat_crossover_2_test.c b/test/regression_test/filex_fault_tolerant_file_write_fat_crossover_2_test.c new file mode 100644 index 0000000..8914ad1 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_write_fat_crossover_2_test.c @@ -0,0 +1,319 @@ +/* This FileX test concentrates on the bug as per + * JIRA: https://expresslogic.atlassian.net/browse/FIL-4. + * In fx_fault_tolerant_cleanup_FAT_chain.c, a group of the FAT entries are updated in the cache + * before it is flushed into media system. The design requires the FAT entries updated in reverse + * order in FAT chain. Due to the FAT cache, the FAT entries may be updated in any order. + * That will break the fault tolerant design. The expected issue is, the FAT chain could + * not be released completely.*/ +/* +For FAT 32 one cluster size is 512 bytes and one sector per cluster; + +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and allocate 118 clusters for A.TXT. +Step6: Create a new thread and append data to A.TXT to occupy cluster 127 and 128. +Step7: Terminate the new thread to simulate poweroff while writing the fat entry of cluster 127 +(the fat entry of cluster 128 is flushed first). +Step8: Open the media. +Step9: Enable fault tolerant feature to revert the FAT chain(revert operation). +Step10: Perform media check. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include "fx_utility.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_write_fat_crossover_2_test_application_define(void *first_unused_memory); + +#ifdef FX_ENABLE_FAULT_TOLERANT + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT fat_write_interrupt = FX_FALSE; +static UCHAR scratch_memory[16 * 1024]; +static UINT interrupt_count_down; + + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_write_fat_crossover_2_test_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG errors_detected; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Write Fat Crossover 2 Test........."); + + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 512, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + status += fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail(FX_SUCCESS == status); + + /* One cluster for A.TXT. */ + status = fx_file_create(&ram_disk, "A.TXT"); + status += fx_file_open(&ram_disk, &my_file, "A.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_allocate(&my_file, 512 * 118); + status += fx_file_close(&my_file); + return_if_fail(FX_SUCCESS == status); + + /* Ensure no lost clusters. */ + status = fx_media_check(&ram_disk, scratch_memory, sizeof(scratch_memory), FX_LOST_CLUSTER_ERROR, &errors_detected); + return_if_fail((status == FX_SUCCESS) && (0 == errors_detected)); + +#ifndef FX_STANDALONE_ENABLE + /* Create the main thread. */ + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + /* directory_write_interrupt */ + fat_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + return_if_fail(FX_TRUE == fat_write_interrupt); + + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + status += fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail(FX_SUCCESS == status); + + status = fx_media_check(&ram_disk, scratch_memory, sizeof(scratch_memory), FX_LOST_CLUSTER_ERROR, &errors_detected); + return_if_fail(FX_SUCCESS == status); + return_if_fail(errors_detected == 0); + + fx_media_close(&ram_disk); + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "A.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_seek(&my_file, 118 * 512); + return_if_fail(FX_SUCCESS == status); + + interrupt_count_down = 2; + + /* The FAT sector of the fat entries of cluster 127 is not the FAT sector for cluster 128. + Since the record of cluster 128 is located at fat_cache[0], the fat sector of 128 will be flushed first. + Terminate the new thread to simulate poweroff while writing the fat entry of cluster 127. + */ + fx_file_write(&my_file, scratch_memory, 512 * 2); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the FAT write operation after record the undo log. */ + if (sector_type == FX_FAT_SECTOR) + { + + interrupt_count_down--; + if (interrupt_count_down == 0) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + fat_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_write_fat_crossover_2_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Write Fat Crossover 2 Test.........N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_write_fat_crossover_test.c b/test/regression_test/filex_fault_tolerant_file_write_fat_crossover_test.c new file mode 100644 index 0000000..ce9450e --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_write_fat_crossover_test.c @@ -0,0 +1,445 @@ +/* This FileX test concentrates on the bug as per + * JIRA: https://expresslogic.atlassian.net/browse/FIL-4. + * In fx_fault_tolerant_cleanup_FAT_chain.c, a group of the FAT entries are updated in the cache + * before it is flushed into media system. The design requires the FAT entries updated in reverse + * order in FAT chain. Due to the FAT cache, the FAT entries may be updated in any order. + * That will break the fault tolerant design. The expected issue is, the FAT chain could + * not be released completely.*/ +/* +For FAT 32 one cluster size is 512 bytes and one sector per cluster; + +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 1M bytes into TEST.TXT; +Step4: Create a new thread and seek to beginning of file and overwrite 64K bytes into TEST.TXT; +Step5: Terminate the new thread to simulate poweroff when write the fat after redo log generated, during FAT chain cleanup. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(undo operation); +Step10: Perform media check. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include "fx_utility.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_write_fat_crossover_test_application_define(void *first_unused_memory); + +#ifdef FX_ENABLE_FAULT_TOLERANT + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT fat_write_interrupt = FX_FALSE; +static UCHAR data_buffer[64 * 1024]; +static UCHAR scratch_memory[16 * 1024]; +static UINT interrupt_count_down; + + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_write_fat_crossover_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG errors_detected; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Write FAT Crossover Test..........."); + + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 512, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, data_buffer, sizeof(data_buffer)); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Alter the FAT chain of file. Swap FAT 112 and 136. */ + _fx_utility_FAT_entry_write(&ram_disk, 111, 136); + _fx_utility_FAT_entry_write(&ram_disk, 112, ram_disk.fx_media_fat_last); + _fx_utility_FAT_entry_write(&ram_disk, 136, 113); + _fx_utility_FAT_entry_write(&ram_disk, 135, 112); + fx_media_flush(&ram_disk); + + status = fx_media_check(&ram_disk, scratch_memory, sizeof(scratch_memory), FX_LOST_CLUSTER_ERROR, &errors_detected); + + /* Determine if any were found - should not have found any errors at this point. */ + if ((status != FX_SUCCESS) || (errors_detected)) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + fat_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + status = fx_media_check(&ram_disk, scratch_memory, sizeof(scratch_memory), FX_LOST_CLUSTER_ERROR, &errors_detected); + + /* Determine if any were found - should not have found any errors at this point. */ + if ((status != FX_SUCCESS) || (errors_detected)) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (fat_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + interrupt_count_down = 2; + + /* Write 64K bytes to the file. */ + fx_file_write(&my_file, data_buffer, sizeof(data_buffer)); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the FAT write operation after record the undo log. */ + if ((sector_type == FX_FAT_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + interrupt_count_down--; + if (interrupt_count_down == 0) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + fat_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_write_fat_crossover_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Write FAT Crossover Test...........N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_write_fat_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_write_fat_interrupt_test.c new file mode 100644 index 0000000..da373f4 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_write_fat_interrupt_test.c @@ -0,0 +1,811 @@ +/* This FileX test concentrates on the Fault-Tolerant fat write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + 1024 1024 1024 1024 1024 1024 + |------------|------------|-----------|------------|------------|------------| + | TEST.TXT | TEST1.TXT | TEST1.TXT | TEST2.TXT | | | + |------------|------------|-----------|------------|------------|------------| + | | | | | + | | | | | + Test1 | | | | + Test2 | | | + Test3 | | + Test4 | + | + Test5 + +Check fat interrupt for fx_file_write() : +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 1500 bytes into TEST1.TXT, (two cluster); +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to continue to write 1024 data into TEST1.TXT: + Test1: Seek to the file: index 0; + Test2: Seek to the file: index 1023; + Test3: Seek to the file: index 1024; + Test4: Seek to the file: index 1025; + Test5: Seek to the file: index 1500; +Step7: Terminate the new thread to simulate poweroff when write the fat between undo log generated and redo log not generated. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(undo operation); +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_write_fat_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT fat_write_interrupt = FX_FALSE; +static UINT write_index; +static UINT data_size = 0; +static UINT i; + +static CHAR write_buffer[2048]; +static UINT write_buffer_size = 2048; +static CHAR read_buffer[4096]; +static UINT read_buffer_size = 4096; +static UCHAR data_buffer[4096]; + +#define SEEK_COUNT 5 +#define FAT_COUNT 3 /* FAT12, 16, 32. */ +#define TEST_COUNT FAT_COUNT * SEEK_COUNT + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_write_fat_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Write FAT Interrupt Test..........."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i < 5) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i < 10) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i < 15) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + + /* Random genearte the write data. */ + for (write_index = 0; write_index < 1500; write_index ++) + { + write_buffer[write_index] = (CHAR)rand(); + + /* Store the write data to compare the new data after Data write interrupt. */ + data_buffer[write_index] = (UCHAR)write_buffer[write_index]; + data_size = 1500; + } + + /* Write the data into file. */ + status = fx_file_write(&my_file, (void *) write_buffer, data_size); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != data_size)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + fat_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. FAT 12, 16, 32. */ + if ((status != FX_SUCCESS) || (actual != data_size) || memcmp(read_buffer, data_buffer, actual)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (fat_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Seek to the file. FAT 12, 16 and 32. */ + if ((i % SEEK_COUNT) == 0) + { + status = fx_file_seek(&my_file, 0); + } + else if ((i % SEEK_COUNT) == 1) + { + status = fx_file_seek(&my_file, 1023); + } + else if ((i % SEEK_COUNT) == 2) + { + status = fx_file_seek(&my_file, 1024); + } + else if ((i % SEEK_COUNT) == 3) + { + status = fx_file_seek(&my_file, 1025); + } + else if ((i % SEEK_COUNT) == 4) + { + status = fx_file_seek(&my_file, 1500); + } + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Random genearte the write data. */ + for (write_index = 0; write_index < write_buffer_size; write_index ++) + { + write_buffer[write_index] = (CHAR)rand(); + } + + /* Write 1024 bytes to the file, then update the FAT table. (bytes should be greate than one cluster). */ + fx_file_write(&my_file, (void *) write_buffer, write_buffer_size); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the FAT write operation after record the undo log. */ + if ((sector_type == FX_FAT_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_UNDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + fat_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_write_fat_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Write FAT Interrupt Test...........N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_write_redo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_write_redo_log_interrupt_test.c new file mode 100644 index 0000000..de499a2 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_write_redo_log_interrupt_test.c @@ -0,0 +1,800 @@ +/* This FileX test concentrates on the Fault-Tolerant redo log write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + 1024 1024 1024 1024 1024 1024 + |------------|------------|-----------|------------|------------|------------| + | TEST.TXT | TEST1.TXT | TEST1.TXT | TEST2.TXT | | | + |------------|------------|-----------|------------|------------|------------| + | | | | | + | | | | | + Test1 | | | | + Test2 | | | + Test3 | | + Test4 | + | + Test5 + +Check redo interrupt for fx_file_write() : +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 1500 bytes into TEST1.TXT, (two cluster); +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to continue to write 1024 data into TEST1.TXT: + Test1: Seek to the file: index 0; + Test2: Seek to the file: index 1023; + Test3: Seek to the file: index 1024; + Test4: Seek to the file: index 1025; + Test5: Seek to the file: index 1500; +Step7: Terminate the new thread to simulate poweroff when write redo log. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(undo operation); +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_write_redo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT log_write_interrupt = FX_FALSE; +static CHAR write_buffer[2048]; +static UINT write_buffer_size = 2048; +static UINT write_index; +static CHAR read_buffer[4096]; +static UINT read_buffer_size = 4096; +static UCHAR data_buffer[4096]; +static UINT data_size = 0; +static UINT i; + +#define SEEK_COUNT 5 +#define FAT_COUNT 3 /* FAT12, 16, 32. */ +#define TEST_COUNT FAT_COUNT * SEEK_COUNT + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_write_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Write Redo LOG Interrupt Test......"); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i < 5) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i < 10) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i < 15) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + + /* Random genearte the write data. */ + for (write_index = 0; write_index < 1500; write_index ++) + { + write_buffer[write_index] = (CHAR)rand(); + + /* Store the write data to compare the new data after Data write interrupt. */ + data_buffer[write_index] = (UCHAR)write_buffer[write_index]; + data_size = 1500; + } + + /* Write the data into file. */ + status = fx_file_write(&my_file, (void *) write_buffer, data_size); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != data_size)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. FAT 12, 16, 32. */ + if ((status != FX_SUCCESS) || (actual != data_size) || memcmp(read_buffer, data_buffer, actual)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (log_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + +UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Seek to the file. FAT 12, 16, 32. */ + if ((i % SEEK_COUNT) == 0) + status = fx_file_seek(&my_file, 0); + else if ((i % SEEK_COUNT) == 1) + status = fx_file_seek(&my_file, 1023); + else if ((i % SEEK_COUNT) == 2) + status = fx_file_seek(&my_file, 1024); + else if ((i % SEEK_COUNT) == 3) + status = fx_file_seek(&my_file, 1025); + else if ((i % SEEK_COUNT) == 4) + status = fx_file_seek(&my_file, 1500); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Random genearte the write data. */ + for (write_index = 0; write_index < write_buffer_size; write_index ++) + { + write_buffer[write_index] = (CHAR)rand(); + } + + /* Write 1024 bytes to the file, then update the FAT table. (bytes should be greate than one cluster). */ + fx_file_write(&my_file, (void *) write_buffer, write_buffer_size); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the redo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_write_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Write Redo LOG Interrupt Test......N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_file_write_undo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_file_write_undo_log_interrupt_test.c new file mode 100644 index 0000000..51b59b6 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_file_write_undo_log_interrupt_test.c @@ -0,0 +1,800 @@ +/* This FileX test concentrates on the Fault-Tolerant undo log write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + 1024 1024 1024 1024 1024 1024 + |------------|------------|-----------|------------|------------|------------| + | TEST.TXT | TEST1.TXT | TEST1.TXT | TEST2.TXT | | | + |------------|------------|-----------|------------|------------|------------| + | | | | | + | | | | | + Test1 | | | | + Test2 | | | + Test3 | | + Test4 | + | + Test5 + +Check undo interrupt for fx_file_write() : +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 1500 bytes into TEST1.TXT, (two cluster); +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to continue to write 1024 data into TEST1.TXT: + Test1: Seek to the file: index 0; + Test2: Seek to the file: index 1023; + Test3: Seek to the file: index 1024; + Test4: Seek to the file: index 1025; + Test5: Seek to the file: index 1500; +Step7: Terminate the new thread to simulate poweroff when write undo log. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(undo operation); +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_file_write_undo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT log_write_interrupt = FX_FALSE; +static UINT write_index; +static CHAR write_buffer[2048]; +static UINT write_buffer_size = 2048; +static CHAR read_buffer[4096]; +static UINT read_buffer_size = 4096; +static UCHAR data_buffer[4096]; +static UINT data_size = 0; +static UINT i; + +#define SEEK_COUNT 5 +#define FAT_COUNT 3 /* FAT12, 16, 32. */ +#define TEST_COUNT FAT_COUNT * SEEK_COUNT + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_write_undo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Write Undo LOG Interrupt Test......"); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i < 5) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i < 10) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i < 15) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + + /* Random genearte the write data. */ + for (write_index = 0; write_index < 1500; write_index ++) + { + write_buffer[write_index] = (CHAR)rand(); + + /* Store the write data to compare the new data after Data write interrupt. */ + data_buffer[write_index] = (UCHAR)write_buffer[write_index]; + data_size = 1500; + } + + /* Write the data into file. */ + status = fx_file_write(&my_file, (void *) write_buffer, data_size); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != data_size)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. FAT 12, 16, 32. */ + if ((status != FX_SUCCESS) || (actual != data_size) || memcmp(read_buffer, data_buffer, actual)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (log_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + +UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Seek to the file. FAT 12, 16, 32. */ + if ((i % SEEK_COUNT) == 0) + status = fx_file_seek(&my_file, 0); + else if ((i % SEEK_COUNT) == 1) + status = fx_file_seek(&my_file, 1023); + else if ((i % SEEK_COUNT) == 2) + status = fx_file_seek(&my_file, 1024); + else if ((i % SEEK_COUNT) == 3) + status = fx_file_seek(&my_file, 1025); + else if ((i % SEEK_COUNT) == 4) + status = fx_file_seek(&my_file, 1500); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Random genearte the write data. */ + for (write_index = 0; write_index < write_buffer_size; write_index ++) + { + write_buffer[write_index] = (CHAR)rand(); + } + + /* Write 1024 bytes to the file, then update the FAT table. (bytes should be greate than one cluster). */ + fx_file_write(&my_file, (void *) write_buffer, write_buffer_size); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the undo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_UNDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_file_write_undo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Write Undo LOG Interrupt Test......N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_log_full_test.c b/test/regression_test/filex_fault_tolerant_log_full_test.c new file mode 100644 index 0000000..962218e --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_log_full_test.c @@ -0,0 +1,293 @@ +/* This FileX test concentrates on the Fault-Tolerant redo log write interrupt operation. + * The available bytes before/after file write should be decreased write_buffer_size only. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_log_full_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static CHAR write_buffer[2048]; +static UINT i; + +#define FAT_COUNT 3 /* FAT12, 16, 32. */ +#define TEST_COUNT FAT_COUNT + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_log_full_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Log Full Tes............................"); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 4, // Total sectors + 512, // Sector size + 4, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Create a file. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Set the callback function to modify size of fault tolerant log. */ + driver_write_callback = my_driver_write; + + /* Write one cluster of information to the TEST.TXT file. */ + status = fx_file_write(&my_file, write_buffer, sizeof(write_buffer)); + + /* Check the create status. */ + if (status != FX_NO_MORE_SPACE) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Close the secondary file. */ + status = fx_file_close(&my_file); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(media_ptr); + FX_PARAMETER_NOT_USED(sector_type); + FX_PARAMETER_NOT_USED(block_ptr); + FX_PARAMETER_NOT_USED(operation_ptr); + + if (ram_disk.fx_media_fault_tolerant_state == FX_FAULT_TOLERANT_STATE_STARTED) + { + ram_disk.fx_media_fault_tolerant_file_size = FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE; + } + + /* Return. */ + return FX_SUCCESS; +} + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_log_full_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Log Full Tes............................N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_log_recover_directory_test.c b/test/regression_test/filex_fault_tolerant_log_recover_directory_test.c new file mode 100644 index 0000000..aac38d7 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_log_recover_directory_test.c @@ -0,0 +1,885 @@ +/* This FileX test concentrates on the Fault-Tolerant log recover directory operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + + 1024 1024 1024 1024 1024 1024 + |------------|------------|-----------|------------|------------|------------| + | TEST.TXT | TEST1.TXT | TEST2.TXT | | | | + |------------|------------|-----------|------------|------------|------------| + 28 1000 19 + +Check recover directory operation: +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Write 28 bytes into TEST.TXT; +Step4: Write 1000 bytes into TEST1.TXT; +Step5: Write 19 bytes into TEST2.TXT; +Step6: Create new thread to continue to write 2048 data into TEST1.TXT(greater than one cluster): +Step7: Terminate the new thread to simulate poweroff when update the directory into media and the redo log has been generated. +Step8: Create new thread and enable fault tolerant feature to recover the data(redo operation); +Step9: Terminate the new thread to simulate poweroff when recover the directory; +Step10: Open the media and enable fault tolerant feature to recover the data(redo operation); +Step11: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_log_recover_directory_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +static TX_THREAD ftest_2; +#else +static pthread_t ptid1; +static pthread_t ptid2; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_1_buffer; +static UCHAR *thread_2_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT directory_write_interrupt_1 = FX_FALSE; +static UINT directory_write_interrupt_2 = FX_FALSE; +static CHAR write_buffer[4096]; +static UINT write_buffer_size = 4096; +static UINT write_index; +static CHAR read_buffer[4096]; +static UINT read_buffer_size = 4096; +static UINT i; + +#define TEST_COUNT 3 /* FAT12, 16, 32. */ + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +static void ftest_2_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +static void * ftest_2_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write_1(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write_2(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_log_recover_directory_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_1_buffer = pointer; + pointer += DEMO_STACK_SIZE; + thread_2_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Log Recover DIR Test...................."); + + /* Random genearte the write data. */ + for (write_index = 0; write_index < write_buffer_size; write_index ++) + write_buffer[write_index] = (CHAR)rand(); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, (void *) write_buffer, 1000); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 1000)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the new thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_1_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + /* directory_write_interrupt */ + directory_write_interrupt_1 = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); + +#endif + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); + + /* Create the new thread. */ + tx_thread_create(&ftest_2, "thread 2", ftest_2_entry, 0, + thread_2_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#else + pthread_cancel(ptid1); +#endif + /* directory_write_interrupt */ + directory_write_interrupt_2 = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid2, NULL, &ftest_2_entry, NULL); + usleep(10); + pthread_join(ptid2,NULL); + +#endif + + /* Recover the log again. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28) || memcmp(read_buffer, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", actual)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 2048 + 1000) || memcmp(read_buffer, write_buffer, actual)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19) || memcmp(read_buffer, " EXPRESSLOGIC_TEST\n", actual)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (directory_write_interrupt_1 != FX_TRUE) || (directory_write_interrupt_2 != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_2); +#else + pthread_cancel(ptid2); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write directory. */ + driver_write_callback = my_driver_write_1; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Write 2048 bytes to the file, then update the directory. (bytes should be greate than one cluster). */ + fx_file_write(&my_file, (void *) (write_buffer + 1000), 2048); +} + +#ifndef FX_STANDALONE_ENABLE +static void ftest_2_entry(ULONG thread_input) +#else +static void * ftest_2_entry(void * thread_input) +#endif +{ +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Set the callback function to simulate poweoff operation when recover the directory. */ + driver_write_callback = my_driver_write_2; + + /* Enable the Fault-tolerant feature to recover the media. */ + fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); +} + +static UINT my_driver_write_1(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the directory write operation. */ + if ((sector_type == FX_DIRECTORY_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + directory_write_interrupt_1 = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} + +static UINT my_driver_write_2(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the directory write operation. */ + if (sector_type == FX_DIRECTORY_SECTOR) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + directory_write_interrupt_2 = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_2); +#else + pthread_cancel(ptid2); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_log_recover_directory_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Log Recover DIR Test....................N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_log_recover_fat_test.c b/test/regression_test/filex_fault_tolerant_log_recover_fat_test.c new file mode 100644 index 0000000..f6ce642 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_log_recover_fat_test.c @@ -0,0 +1,647 @@ +/* This FileX test concentrates on the Fault-Tolerant log recover operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + + 1024 1024 1024 1024 1024 1024 + |------------|------------|-----------|------------|------------|------------| + | TEST.TXT | TEST1.TXT | TEST2.TXT | | | | + |------------|------------|-----------|------------|------------|------------| + 28 1000 19 + +Check recover fat operation: +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Write 28 bytes into TEST.TXT; +Step4: Write 1000 bytes into TEST1.TXT; +Step5: Write 19 bytes into TEST2.TXT; +Step6: Create new thread to continue to write 2048 data into TEST1.TXT(greater than one cluster): +Step7: Terminate the new thread to simulate poweroff when write the data between undo log generated and redo log not generated. +Step8: Create new thread and enable fault tolerant feature to recover the data(undo operation); +Step9: Terminate the new thread to simulate poweroff when recover the FAT table; +Step10: Open the media and enable fault tolerant feature to recover the data(undo operation); +Step11: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_log_recover_fat_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +static TX_THREAD ftest_2; +#else +static pthread_t ptid1; +static pthread_t ptid2; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_1_buffer; +static UCHAR *thread_2_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT fat_write_interrupt_1 = FX_FALSE; +static UINT fat_write_interrupt_2 = FX_FALSE; +static UINT fat_updated_flag = FX_FALSE; +static UINT write_index; +static UINT i; +static CHAR write_buffer[2048]; +static UINT write_buffer_size = 2048; +static CHAR read_buffer[4096]; +static UINT read_buffer_size = 4096; +static UCHAR data_buffer[4096]; + +#define TEST_COUNT 3 /* FAT12, 16, 32. */ + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +static void ftest_2_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +static void * ftest_2_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write_1(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write_2(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_log_recover_fat_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_1_buffer = pointer; + pointer += DEMO_STACK_SIZE; + thread_2_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Log Recover FAT Test...................."); + + /* Random genearte the write data. */ + for (write_index = 0; write_index < write_buffer_size; write_index ++) + write_buffer[write_index] = (CHAR)rand(); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + return_if_fail( status == FX_SUCCESS); + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + return_if_fail( status == FX_SUCCESS); + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + return_if_fail( status == FX_SUCCESS); + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + return_if_fail( (status == FX_SUCCESS) && (actual == 28)); + + /* Close the test file. */ + status = fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + return_if_fail( status == FX_SUCCESS); + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, (void *) write_buffer, 1000); + return_if_fail( status == FX_SUCCESS); + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + return_if_fail( status == FX_SUCCESS); + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + return_if_fail( (status == FX_SUCCESS) && (actual == 1000)); + + /* Close the test file. */ + status = fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + return_if_fail( status == FX_SUCCESS); + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + return_if_fail( status == FX_SUCCESS); + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + return_if_fail( status == FX_SUCCESS); + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + return_if_fail( (status == FX_SUCCESS) && (actual == 19)); + + /* Close the test file. */ + status = fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + /* Create the new thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_1_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + fat_write_interrupt_1 = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); + + /* Create the new thread. */ + tx_thread_create(&ftest_2, "thread 2", ftest_2_entry, 0, + thread_2_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#else + pthread_cancel(ptid1); +#endif + /* directory_write_interrupt */ + fat_write_interrupt_2 = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid2, NULL, &ftest_2_entry, NULL); + usleep(10); + pthread_join(ptid2,NULL); +#endif + + /* Recover the log again. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + return_if_fail( status == FX_SUCCESS); + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + return_if_fail( (status == FX_SUCCESS) && (actual == 28) && (memcmp(read_buffer, " ABCDEFGHIJKLNMOPQRSTUVWXYZ\n", actual))); + + /* Close the test file. */ + status = fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + return_if_fail( status == FX_SUCCESS); + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + return_if_fail((status == FX_SUCCESS) && (actual == 1000) && !(memcmp(read_buffer, write_buffer, actual))); + + /* Close the test file. */ + status = fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + return_if_fail( status == FX_SUCCESS); + + /* Clean the buffer. */ + memset(read_buffer, 0, read_buffer_size); + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + return_if_fail((status == FX_SUCCESS) && (actual == 19) && !(memcmp(read_buffer, " EXPRESSLOGIC_TEST\n", actual))); + + /* Close the test file. */ + status = fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail((status == FX_SUCCESS) && (fat_write_interrupt_1 == FX_TRUE) && (fat_write_interrupt_2 == FX_TRUE) && !(error_couter)); + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_2); +#else + pthread_cancel(ptid2); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write_1; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Write 1024 bytes to the file, then update the FAT table. (bytes should be greate than one cluster). */ + { + fx_file_write(&my_file, (void *) (write_buffer + 1000), 2048); + } +} + +#ifndef FX_STANDALONE_ENABLE +static void ftest_2_entry(ULONG thread_input) +#else +static void * ftest_2_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Set the callback function to simulate poweoff operation when recover the FAT entry. */ + driver_write_callback = my_driver_write_2; + + /* Enable the Fault-tolerant feature to recover the media. */ + fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); +} + +static UINT my_driver_write_1(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the FAT write operation. */ + if ((sector_type == FX_FAT_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_UNDO_DONE) && + (!(_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE))) + { + + /* Updated the fat flag. */ + fat_updated_flag = FX_TRUE; + } + + /* Interrupt the redo write operation after updated the fat table. */ + if ((fat_updated_flag == FX_TRUE) && (sector_type == FX_DATA_SECTOR) && + (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + fat_write_interrupt_1 = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} + +static UINT my_driver_write_2(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the FAT write operation. */ + if (sector_type == FX_FAT_SECTOR) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + fat_write_interrupt_2 = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_2); +#else + pthread_cancel(ptid2); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_log_recover_fat_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Log Recover FAT Test....................N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_long_directory_create_directory_interrupt_test.c b/test/regression_test/filex_fault_tolerant_long_directory_create_directory_interrupt_test.c new file mode 100644 index 0000000..94f4896 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_long_directory_create_directory_interrupt_test.c @@ -0,0 +1,600 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check directory interrupt for fx_directory_create(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create directories and sub-directories; +Step4: Traverse the directory; +Step5: Create new thread to create new directory with long directory name; +Step6: Terminate the new thread to simulate poweroff when update the directory after record the redo log. +Step7: Open the media; +Step8: Enable fault tolerant feature to recover the data(redo operation); +Step9: Traverse the directory; +Step10: Recreate directory the new directory with long directory name; +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_long_directory_create_directory_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static UCHAR *pointer; + +#define TEST_COUNT 3 + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_counter = 0; +static UINT redo_complete =0; +static UINT directory_write_interrupt = FX_FALSE; +static CHAR dir_name[256] = "C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + +static UINT expected_index = 0; +static CHAR * expected_name[] = { + + "A0", + ".", + "..", + "A1", + ".", + "..", + "B0", + ".", + "..", + "B1", + ".", + "..", + "B2", + ".", + "..", + "B00", + ".", + "..", + dir_name, + ".", + "..", + "END"}; + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +static void traverse_directory(CHAR *directory_name); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_long_directory_create_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Create the main thread. */ + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Long DIR Create DIR Interrupt Test......"); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + redo_complete =0; + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a series of directories... */ + status = fx_directory_create(&ram_disk, "/A0"); + status += fx_directory_create(&ram_disk, "/B0"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(4); + } + + /* Create the next level of sub-directories. */ + status = fx_directory_create(&ram_disk, "/A0/A1"); + + status += fx_directory_create(&ram_disk, "/B0/B1"); + status += fx_directory_create(&ram_disk, "/B0/B2"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(5); + } + + /* Now create the third level of sub-directories... */ + status += fx_directory_create(&ram_disk, "/B0/B2/B00"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(6); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + + /* Traverse the directory structure recursively. */ + traverse_directory(FX_NULL); + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + directory_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(7); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + redo_complete = 1; + + /* Traverse the directory structure recursively. */ + traverse_directory(FX_NULL); + + /* Attempt to create the same directory again. */ + status = fx_directory_create(&ram_disk, dir_name); + + /* Check for errors... */ + if (status != FX_ALREADY_CREATED) + { + + /* Error creating same directory twice. Return to caller. */ + printf("ERROR!\n"); + test_control_return(9); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (directory_write_interrupt != FX_TRUE) || (error_counter)) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Create the long directory name. */ + fx_directory_create(&ram_disk, dir_name); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the Directory write operation after record the redo log. */ + if ((sector_type == FX_DIRECTORY_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + directory_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} + +/* Define the FAT12, 16 and 32 directory traversal routine for the local path test. */ + +#ifndef FX_STANDALONE_ENABLE +static void traverse_directory(CHAR *directory_name) +{ + +FX_LOCAL_PATH local_path; +FX_FILE file; +UINT status; +CHAR name[300]; +UINT skip; + + + /* Determine if we are at the start. */ + if (directory_name == FX_NULL) + { + status = fx_directory_local_path_set(&ram_disk, &local_path, "\\"); + skip = 0; + } + else + { + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name); + skip = 2; + } + + /* Get the first directory entry in the root path. */ + status = fx_directory_first_entry_find(&ram_disk, name); + + /* Loop through the directory entries for this path. */ + while (status == FX_SUCCESS) + { + + /* Compare with what is expected. */ + if (strcmp(name, expected_name[expected_index++])) + error_counter++; + + /* Determine if this name is a directory or a file. */ + status = fx_directory_name_test(&ram_disk, name); + + /* What is it? */ + if (status == FX_NOT_DIRECTORY) + { + + /* This is a file, open it to test its integrity. */ + status = fx_file_open(&ram_disk, &file, name, FX_OPEN_FOR_READ); + if (status != FX_SUCCESS) + error_counter++; + status = fx_file_close(&file); + if (status != FX_SUCCESS) + error_counter++; + + /* Pickup the next directory entry. */ + status = fx_directory_next_entry_find(&ram_disk, name); + } + else if (status == FX_SUCCESS) + { + + /* Skip the first two entries... */ + if (skip) + skip--; + else + { + /* Recursive call to traverse directory. */ + traverse_directory(name); + + /* Restore path. */ + status = fx_directory_local_path_restore(&ram_disk, &local_path); + } + + status = fx_directory_next_entry_find(&ram_disk, name); + } + } + fx_directory_local_path_clear(&ram_disk); +} +#else +static void traverse_directory(CHAR *directory_name) +{ +UINT status; +CHAR name[300]; + + /* Pickup the first entry in the root directory. */ + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, "A0")) + error_counter++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "B0")) + error_counter++; + + /* Set the current path */ + status = fx_directory_default_set(&ram_disk, "/A0"); + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, ".")) + error_counter++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "..")) + error_counter++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "A1")) + error_counter++; + + /* Set the current path */ + status = fx_directory_default_set(&ram_disk, "/B0/B2"); + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, ".")) + error_counter++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "..")) + error_counter++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "B00")) + error_counter++; + +} +#endif + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_long_directory_create_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Long DIR Create DIR Interrupt Test......N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_long_directory_create_redo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_long_directory_create_redo_log_interrupt_test.c new file mode 100644 index 0000000..0a6ebfc --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_long_directory_create_redo_log_interrupt_test.c @@ -0,0 +1,597 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check directory interrupt for fx_directory_create(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create directories and sub-directories; +Step4: Traverse the directory; +Step5: Create new thread to create new directory with long directory name; +Step6: Terminate the new thread to simulate poweroff when update redo log. +Step7: Open the media; +Step8: Enable fault tolerant feature to recover the data(undo operation); +Step9: Traverse the directory; +Step10: Recreate directory the new directory with long directory name; +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_long_directory_create_redo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static UCHAR *pointer; + +#define TEST_COUNT 3 + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_counter = 0; +static UINT redo_flag =0; +static UINT log_write_interrupt = FX_FALSE; +static CHAR dir_name[256] = "C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + +static UINT expected_index = 0; +static CHAR * expected_name[] = { + + "A0", + ".", + "..", + "A1", + ".", + "..", + "B0", + ".", + "..", + "B1", + ".", + "..", + "B2", + ".", + "..", + "B00", + ".", + "..", + dir_name, + ".", + "..", + "END"}; + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +static void traverse_directory(CHAR *directory_name); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_long_directory_create_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Create the main thread. */ + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Long DIR Create Redo Log Interrupt Test."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + redo_flag =0; + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a series of directories... */ + status = fx_directory_create(&ram_disk, "/A0"); + status += fx_directory_create(&ram_disk, "/B0"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(4); + } + + /* Create the next level of sub-directories. */ + status = fx_directory_create(&ram_disk, "/A0/A1"); + + status += fx_directory_create(&ram_disk, "/B0/B1"); + status += fx_directory_create(&ram_disk, "/B0/B2"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(5); + } + + /* Now create the third level of sub-directories... */ + status += fx_directory_create(&ram_disk, "/B0/B2/B00"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(6); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + + /* Traverse the directory structure recursively. */ + traverse_directory(FX_NULL); + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* log_write_interrupt */ + log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(7); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + redo_flag = 1; + /* Traverse the directory structure recursively. */ + traverse_directory(FX_NULL); + + /* Attempt to create the same directory again. */ + status = fx_directory_create(&ram_disk, dir_name); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating same directory twice. Return to caller. */ + printf("ERROR!\n"); + test_control_return(9); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (log_write_interrupt != FX_TRUE) || (error_counter)) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Create the long directory name. */ + fx_directory_create(&ram_disk, dir_name); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the redo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} + +/* Define the FAT12, 16 and 32 directory traversal routine for the local path test. */ +#ifndef FX_STANDALONE_ENABLE +static void traverse_directory(CHAR *directory_name) +{ + +FX_LOCAL_PATH local_path; +FX_FILE file; +UINT status; +CHAR name[300]; +UINT skip; + + + /* Determine if we are at the start. */ + if (directory_name == FX_NULL) + { + status = fx_directory_local_path_set(&ram_disk, &local_path, "\\"); + skip = 0; + } + else + { + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name); + skip = 2; + } + + /* Get the first directory entry in the root path. */ + status = fx_directory_first_entry_find(&ram_disk, name); + + /* Loop through the directory entries for this path. */ + while (status == FX_SUCCESS) + { + + /* Compare with what is expected. */ + if (strcmp(name, expected_name[expected_index++])) + error_counter++; + + /* Determine if this name is a directory or a file. */ + status = fx_directory_name_test(&ram_disk, name); + + /* What is it? */ + if (status == FX_NOT_DIRECTORY) + { + + /* This is a file, open it to test its integrity. */ + status = fx_file_open(&ram_disk, &file, name, FX_OPEN_FOR_READ); + if (status != FX_SUCCESS) + error_counter++; + status = fx_file_close(&file); + if (status != FX_SUCCESS) + error_counter++; + + /* Pickup the next directory entry. */ + status = fx_directory_next_entry_find(&ram_disk, name); + } + else if (status == FX_SUCCESS) + { + + /* Skip the first two entries... */ + if (skip) + skip--; + else + { + /* Recursive call to traverse directory. */ + traverse_directory(name); + + /* Restore path. */ + status = fx_directory_local_path_restore(&ram_disk, &local_path); + } + + status = fx_directory_next_entry_find(&ram_disk, name); + } + } + + fx_directory_local_path_clear(&ram_disk); +} +#else +static void traverse_directory(CHAR *directory_name) +{ +UINT status; +CHAR name[300]; + + /* Pickup the first entry in the root directory. */ + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, "A0")) + error_counter++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "B0")) + error_counter++; + + /* Set the current path */ + status = fx_directory_default_set(&ram_disk, "/A0"); + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, ".")) + error_counter++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "..")) + error_counter++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "A1")) + error_counter++; + + /* Set the current path */ + status = fx_directory_default_set(&ram_disk, "/B0/B2"); + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, ".")) + error_counter++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "..")) + error_counter++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "B00")) + error_counter++; + +} +#endif +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_long_directory_create_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Long DIR Create Redo Log Interrupt Test.N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_long_directory_rename_directory_interrupt_test.c b/test/regression_test/filex_fault_tolerant_long_directory_rename_directory_interrupt_test.c new file mode 100644 index 0000000..269cc6d --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_long_directory_rename_directory_interrupt_test.c @@ -0,0 +1,702 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 3072 bytes; + +Check directory interrupt for fx_directory_rename(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create directories and sub-directories; +Step4: Traverse the directory; +Step5: Create new thread to rename directory from directory_name_1 to directory_name_2; +Step6: Terminate the new thread to simulate poweroff when update the directory after record the redo log. +Step7: Open the media; +Step8: Enable fault tolerant feature to recover the data(redo operation); +Step9: Create directory directory_name_1; +Step10: Create directory directory_name_2; +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_long_directory_rename_directory_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) && (FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE >= 3072) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static UCHAR *pointer; + +#if (FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE >= 3072) +#define TEST_COUNT 3 +#else + +#define TEST_COUNT 4 +#endif + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_counter = 0; +static UINT directory_write_interrupt = FX_FALSE; +static CHAR directory_name_1[300] = "/D0/D40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; +static CHAR directory_name_2[300] = "/D0/D50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + +static UINT expected_index = 0; +static CHAR * expected_name[] = { + + "A0", + ".", + "..", + "A1", + ".", + "..", + "A00", + ".", + "..", + "A2", + ".", + "..", + "A3", + ".", + "..", + "B0", + ".", + "..", + "B1", + ".", + "..", + "B2", + ".", + "..", + "B00", + ".", + "..", + "B3", + ".", + "..", + "C0", + ".", + "..", + "C1", + ".", + "..", + "C2", + ".", + "..", + "C3", + ".", + "..", + "C00", + ".", + "..", + "D0", + ".", + "..", + "D1", + ".", + "..", + "D2", + ".", + "..", + "D3", + ".", + "..", + directory_name_1 + 4, + ".", + "..", + "END"}; + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +static void traverse_directory(CHAR *directory_name); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_long_directory_rename_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Long Dir Rename DIR Interrupt Test......"); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 384, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 384, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT16 with sector size 2048 bytes. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 16, // Total sectors + 1024, // Sector size + 2, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 3) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 384, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a series of directories... */ + status = fx_directory_create(&ram_disk, "/A0"); + status += fx_directory_create(&ram_disk, "/B0"); + status += fx_directory_create(&ram_disk, "/C0"); + status += fx_directory_create(&ram_disk, "/D0"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(4); + } + + /* Create the next level of sub-directories. */ + status = fx_directory_create(&ram_disk, "/A0/A1"); + status += fx_directory_create(&ram_disk, "/A0/A2"); + status += fx_directory_create(&ram_disk, "/A0/A3"); + + status += fx_directory_create(&ram_disk, "/B0/B1"); + status += fx_directory_create(&ram_disk, "/B0/B2"); + status += fx_directory_create(&ram_disk, "/B0/B3"); + + status += fx_directory_create(&ram_disk, "/C0/C1"); + status += fx_directory_create(&ram_disk, "/C0/C2"); + status += fx_directory_create(&ram_disk, "/C0/C3"); + + status += fx_directory_create(&ram_disk, "/D0/D1"); + status += fx_directory_create(&ram_disk, "/D0/D2"); + status += fx_directory_create(&ram_disk, "/D0/D3"); + status += fx_directory_create(&ram_disk, directory_name_1); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(5); + } + + /* Now create the third level of sub-directories... */ + status = fx_directory_create(&ram_disk, "/A0/A1/A00"); + status += fx_directory_create(&ram_disk, "/B0/B2/B00"); + status += fx_directory_create(&ram_disk, "/C0/C3/C00"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(6); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + + /* Traverse the directory structure recursively. */ + traverse_directory(FX_NULL); + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + directory_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(7); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Attempt to create the directory_name_1 again. */ + status = fx_directory_create(&ram_disk, directory_name_1); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating same directory twice. Return to caller. */ + printf("ERROR!\n"); + test_control_return(9); + } + + /* Attempt to create the directory_name_2 again. */ + status = fx_directory_create(&ram_disk, directory_name_2); + + /* Check for errors... */ + if (status != FX_ALREADY_CREATED) + { + + /* Error creating same directory twice. Return to caller. */ + printf("ERROR!\n"); + test_control_return(10); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (directory_write_interrupt != FX_TRUE) || (error_counter)) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation. */ + driver_write_callback = my_driver_write; + + /* Rename the directory directory_name_1 to directory_name_2. */ + fx_directory_rename(&ram_disk, directory_name_1, directory_name_2); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the Directory write operation after record the redo log. */ + if ((sector_type == FX_DIRECTORY_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) == FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + directory_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} + +/* Define the FAT12, 16 and 32 directory traversal routine for the local path test. */ +#ifndef FX_STANDALONE_ENABLE +static void traverse_directory(CHAR *directory_name) +{ + +FX_LOCAL_PATH local_path; +FX_FILE file; +UINT status; +CHAR name[300]; +UINT skip; + + + /* Determine if we are at the start. */ + if (directory_name == FX_NULL) + { + status = fx_directory_local_path_set(&ram_disk, &local_path, "\\"); + skip = 0; + } + else + { + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name); + skip = 2; + } + + /* Get the first directory entry in the root path. */ + status = fx_directory_first_entry_find(&ram_disk, name); + + /* Loop through the directory entries for this path. */ + while (status == FX_SUCCESS) + { + + /* Compare with what is expected. */ + if (strcmp(name, expected_name[expected_index++])) + error_counter++; + + /* Determine if this name is a directory or a file. */ + status = fx_directory_name_test(&ram_disk, name); + + /* What is it? */ + if (status == FX_NOT_DIRECTORY) + { + + /* This is a file, open it to test its integrity. */ + status = fx_file_open(&ram_disk, &file, name, FX_OPEN_FOR_READ); + if (status != FX_SUCCESS) + error_counter++; + status = fx_file_close(&file); + if (status != FX_SUCCESS) + error_counter++; + + /* Pickup the next directory entry. */ + status = fx_directory_next_entry_find(&ram_disk, name); + } + else if (status == FX_SUCCESS) + { + + /* Skip the first two entries... */ + if (skip) + skip--; + else + { + /* Recursive call to traverse directory. */ + traverse_directory(name); + + /* Restore path. */ + status = fx_directory_local_path_restore(&ram_disk, &local_path); + } + + status = fx_directory_next_entry_find(&ram_disk, name); + } + } + + fx_directory_local_path_clear(&ram_disk); +} +#else + +static void traverse_directory(CHAR *directory_name) +{ +UINT status; +CHAR name[300]; + + /* Pickup the first entry in the root directory. */ + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, "A0")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "B0")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "C0")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "D0")) + status++; + + /* Okay, now postion to the deepest sub-directory and ensure that the directory first/next + operations work properly there as well. */ + + /* Set the current path */ + status = fx_directory_default_set(&ram_disk, "/C0/C3"); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error setting the path. Return to caller. */ + printf("ERROR!\n"); + test_control_return(16); + } + + /* Pickup the first entry in this sub-sub directory. */ + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, ".")) + status++; + + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "..")) + status++; + + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "C00")) + status++; + + /* Now read one past the number of entries in order to get an error condition. */ + status += fx_directory_next_entry_find(&ram_disk, name); + + /* Determine if the test was successful. */ + if (status != FX_NO_MORE_ENTRIES) + { + + printf("ERROR!\n"); + test_control_return(18); + } + +} + +#endif + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_long_directory_rename_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Long Dir Rename DIR Interrupt Test......N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_long_directory_rename_io_error_test.c b/test/regression_test/filex_fault_tolerant_long_directory_rename_io_error_test.c new file mode 100644 index 0000000..6cd131a --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_long_directory_rename_io_error_test.c @@ -0,0 +1,547 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 3072 bytes; + +Check directory interrupt for fx_directory_rename(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create directories and sub-directories; +Step4: Traverse the directory; +Step5: Create new thread to rename directory from directory_name_1 to directory_name_2; +Step6: Force IO error while generating fault tolerant log file. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_long_directory_rename_io_error_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) && (FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE >= 3072) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static UCHAR *pointer; + +#if (FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE >= 3072) +#define TEST_COUNT 3 +#else + +#define TEST_COUNT 4 +#endif + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_counter = 0; +static CHAR directory_name_1[300] = "/D0/D40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; +static CHAR directory_name_2[300] = "/D0/D50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + +static UINT expected_index = 0; +static CHAR * expected_name[] = { + + "A0", + ".", + "..", + "A1", + ".", + "..", + "A00", + ".", + "..", + "A2", + ".", + "..", + "A3", + ".", + "..", + "B0", + ".", + "..", + "B1", + ".", + "..", + "B2", + ".", + "..", + "B00", + ".", + "..", + "B3", + ".", + "..", + "C0", + ".", + "..", + "C1", + ".", + "..", + "C2", + ".", + "..", + "C3", + ".", + "..", + "C00", + ".", + "..", + "D0", + ".", + "..", + "D1", + ".", + "..", + "D2", + ".", + "..", + "D3", + ".", + "..", + directory_name_1 + 4, + ".", + "..", + "END"}; + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +static void traverse_directory(CHAR *directory_name); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_long_directory_rename_io_error_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Long Dir Rename IO Error Test..........."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 384, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 384, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT16 with sector size 2048 bytes. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 16, // Total sectors + 1024, // Sector size + 2, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 3) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 384, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a series of directories... */ + status = fx_directory_create(&ram_disk, "/A0"); + status += fx_directory_create(&ram_disk, "/B0"); + status += fx_directory_create(&ram_disk, "/C0"); + status += fx_directory_create(&ram_disk, "/D0"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(4); + } + + /* Create the next level of sub-directories. */ + status = fx_directory_create(&ram_disk, "/A0/A1"); + status += fx_directory_create(&ram_disk, "/A0/A2"); + status += fx_directory_create(&ram_disk, "/A0/A3"); + + status += fx_directory_create(&ram_disk, "/B0/B1"); + status += fx_directory_create(&ram_disk, "/B0/B2"); + status += fx_directory_create(&ram_disk, "/B0/B3"); + + status += fx_directory_create(&ram_disk, "/C0/C1"); + status += fx_directory_create(&ram_disk, "/C0/C2"); + status += fx_directory_create(&ram_disk, "/C0/C3"); + + status += fx_directory_create(&ram_disk, "/D0/D1"); + status += fx_directory_create(&ram_disk, "/D0/D2"); + status += fx_directory_create(&ram_disk, "/D0/D3"); + status += fx_directory_create(&ram_disk, directory_name_1); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(5); + } + + /* Now create the third level of sub-directories... */ + status = fx_directory_create(&ram_disk, "/A0/A1/A00"); + status += fx_directory_create(&ram_disk, "/B0/B2/B00"); + status += fx_directory_create(&ram_disk, "/C0/C3/C00"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(6); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + + /* Traverse the directory structure recursively. */ + traverse_directory(FX_NULL); + +#if !defined(FX_DISABLE_CACHE) + /* Rename the directory directory_name_1 to directory_name_2. */ + _fx_ram_driver_io_error_request = 4; + status = fx_directory_rename(&ram_disk, directory_name_1, directory_name_2); + + /* Determine if the result fails. */ + if (status != FX_IO_ERROR) + { + + printf("ERROR!\n"); + test_control_return(7); + } +#endif + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (error_counter)) + { + + printf("ERROR!\n"); + test_control_return(11); + } + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the FAT12, 16 and 32 directory traversal routine for the local path test. */ +#ifndef FX_STANDALONE_ENABLE +static void traverse_directory(CHAR *directory_name) +{ + +FX_LOCAL_PATH local_path; +FX_FILE file; +UINT status; +CHAR name[300]; +UINT skip; + + + /* Determine if we are at the start. */ + if (directory_name == FX_NULL) + { + status = fx_directory_local_path_set(&ram_disk, &local_path, "\\"); + skip = 0; + } + else + { + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name); + skip = 2; + } + + /* Get the first directory entry in the root path. */ + status = fx_directory_first_entry_find(&ram_disk, name); + + /* Loop through the directory entries for this path. */ + while (status == FX_SUCCESS) + { + + /* Compare with what is expected. */ + if (strcmp(name, expected_name[expected_index++])) + error_counter++; + + /* Determine if this name is a directory or a file. */ + status = fx_directory_name_test(&ram_disk, name); + + /* What is it? */ + if (status == FX_NOT_DIRECTORY) + { + + /* This is a file, open it to test its integrity. */ + status = fx_file_open(&ram_disk, &file, name, FX_OPEN_FOR_READ); + if (status != FX_SUCCESS) + error_counter++; + status = fx_file_close(&file); + if (status != FX_SUCCESS) + error_counter++; + + /* Pickup the next directory entry. */ + status = fx_directory_next_entry_find(&ram_disk, name); + } + else if (status == FX_SUCCESS) + { + + /* Skip the first two entries... */ + if (skip) + skip--; + else + { + /* Recursive call to traverse directory. */ + traverse_directory(name); + + /* Restore path. */ + status = fx_directory_local_path_restore(&ram_disk, &local_path); + } + + status = fx_directory_next_entry_find(&ram_disk, name); + } + } + + fx_directory_local_path_clear(&ram_disk); +} +#else + +static void traverse_directory(CHAR *directory_name) +{ +UINT status; +CHAR name[300]; + + /* Pickup the first entry in the root directory. */ + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, "A0")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "B0")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "C0")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "D0")) + status++; + + /* Okay, now postion to the deepest sub-directory and ensure that the directory first/next + operations work properly there as well. */ + + /* Set the current path */ + status = fx_directory_default_set(&ram_disk, "/C0/C3"); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error setting the path. Return to caller. */ + printf("ERROR!\n"); + test_control_return(16); + } + + /* Pickup the first entry in this sub-sub directory. */ + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, ".")) + status++; + + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "..")) + status++; + + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "C00")) + status++; + + /* Now read one past the number of entries in order to get an error condition. */ + status += fx_directory_next_entry_find(&ram_disk, name); + + /* Determine if the test was successful. */ + if (status != FX_NO_MORE_ENTRIES) + { + + printf("ERROR!\n"); + test_control_return(18); + } + +} + +#endif + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_long_directory_rename_io_error_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Long Dir Rename IO Error Test...........N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_long_directory_rename_redo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_long_directory_rename_redo_log_interrupt_test.c new file mode 100644 index 0000000..8f8e0b6 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_long_directory_rename_redo_log_interrupt_test.c @@ -0,0 +1,682 @@ +/* This FileX test concentrates on the Fault-Tolerant redo log write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 3072 bytes; + +Check redo log interrupt for fx_directory_rename(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create directories and sub-directories; +Step4: Traverse the directory; +Step5: Create new thread to rename directory from directory_name_1 to directory_name_2; +Step6: Terminate the new thread to simulate poweroff when update the redo log. +Step7: Open the media; +Step8: Enable fault tolerant feature to recover the data(undo operation); +Step9: Create directory directory_name_1; +Step10: Create directory directory_name_2; +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_long_directory_rename_redo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) && (FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE >= 3072) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; + +#if (FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE >= 3072) +#define TEST_COUNT 2 +#else + +#define TEST_COUNT 3 +#endif + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *pointer; +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_counter = 0; +static UINT log_write_interrupt = FX_FALSE; +static CHAR directory_name_1[256] = "/D0/D4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; +static CHAR directory_name_2[256] = "/D0/D5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + +static UINT expected_index = 0; +static CHAR * expected_name[] = { + + "A0", + ".", + "..", + "A1", + ".", + "..", + "A00", + ".", + "..", + "A2", + ".", + "..", + "A3", + ".", + "..", + "B0", + ".", + "..", + "B1", + ".", + "..", + "B2", + ".", + "..", + "B00", + ".", + "..", + "B3", + ".", + "..", + "C0", + ".", + "..", + "C1", + ".", + "..", + "C2", + ".", + "..", + "C3", + ".", + "..", + "C00", + ".", + "..", + "D0", + ".", + "..", + "D1", + ".", + "..", + "D2", + ".", + "..", + "D3", + ".", + "..", + directory_name_1 + 4, + ".", + "..", + "END"}; + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +static void traverse_directory(CHAR *directory_name); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_long_directory_rename_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Long Dir Rename Redo LOG Interrupt Test."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 384, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 384, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 384, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a series of directories... */ + status = fx_directory_create(&ram_disk, "/A0"); + status += fx_directory_create(&ram_disk, "/B0"); + status += fx_directory_create(&ram_disk, "/C0"); + status += fx_directory_create(&ram_disk, "/D0"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(4); + } + + /* Create the next level of sub-directories. */ + status = fx_directory_create(&ram_disk, "/A0/A1"); + status += fx_directory_create(&ram_disk, "/A0/A2"); + status += fx_directory_create(&ram_disk, "/A0/A3"); + + status += fx_directory_create(&ram_disk, "/B0/B1"); + status += fx_directory_create(&ram_disk, "/B0/B2"); + status += fx_directory_create(&ram_disk, "/B0/B3"); + + status += fx_directory_create(&ram_disk, "/C0/C1"); + status += fx_directory_create(&ram_disk, "/C0/C2"); + status += fx_directory_create(&ram_disk, "/C0/C3"); + + status += fx_directory_create(&ram_disk, "/D0/D1"); + status += fx_directory_create(&ram_disk, "/D0/D2"); + status += fx_directory_create(&ram_disk, "/D0/D3"); + status += fx_directory_create(&ram_disk, directory_name_1); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(5); + } + + /* Now create the third level of sub-directories... */ + status = fx_directory_create(&ram_disk, "/A0/A1/A00"); + status += fx_directory_create(&ram_disk, "/B0/B2/B00"); + status += fx_directory_create(&ram_disk, "/C0/C3/C00"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(6); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + + /* Traverse the directory structure recursively. */ + traverse_directory(FX_NULL); + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(7); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Attempt to create the directory_name_1 again. */ + status = fx_directory_create(&ram_disk, directory_name_1); + + /* Check for errors... */ + if (status != FX_ALREADY_CREATED) + { + + /* Error creating same directory twice. Return to caller. */ + printf("ERROR!\n"); + test_control_return(9); + } + + /* Attempt to create the directory_name_2 again. */ + status = fx_directory_create(&ram_disk, directory_name_2); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating same directory twice. Return to caller. */ + printf("ERROR!\n"); + test_control_return(10); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (log_write_interrupt != FX_TRUE) || (error_counter)) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Rename the directory directory_name_1 to directory_name_2. */ + fx_directory_rename(&ram_disk, directory_name_1, directory_name_2); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the redo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} + +/* Define the FAT12, 16 and 32 directory traversal routine for the local path test. */ +#ifndef FX_STANDALONE_ENABLE +static void traverse_directory(CHAR *directory_name) +{ + +FX_LOCAL_PATH local_path; +FX_FILE file; +UINT status; +CHAR name[300]; +UINT skip; + + + /* Determine if we are at the start. */ + if (directory_name == FX_NULL) + { + status = fx_directory_local_path_set(&ram_disk, &local_path, "\\"); + skip = 0; + } + else + { + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name); + skip = 2; + } + + /* Get the first directory entry in the root path. */ + status = fx_directory_first_entry_find(&ram_disk, name); + + /* Loop through the directory entries for this path. */ + while (status == FX_SUCCESS) + { + + /* Compare with what is expected. */ + if (strcmp(name, expected_name[expected_index++])) + error_counter++; + + /* Determine if this name is a directory or a file. */ + status = fx_directory_name_test(&ram_disk, name); + + /* What is it? */ + if (status == FX_NOT_DIRECTORY) + { + + /* This is a file, open it to test its integrity. */ + status = fx_file_open(&ram_disk, &file, name, FX_OPEN_FOR_READ); + if (status != FX_SUCCESS) + error_counter++; + status = fx_file_close(&file); + if (status != FX_SUCCESS) + error_counter++; + + /* Pickup the next directory entry. */ + status = fx_directory_next_entry_find(&ram_disk, name); + } + else if (status == FX_SUCCESS) + { + + /* Skip the first two entries... */ + if (skip) + skip--; + else + { + /* Recursive call to traverse directory. */ + traverse_directory(name); + + /* Restore path. */ + status = fx_directory_local_path_restore(&ram_disk, &local_path); + } + + status = fx_directory_next_entry_find(&ram_disk, name); + } + } + + fx_directory_local_path_clear(&ram_disk); +} +#else + +static void traverse_directory(CHAR *directory_name) +{ +UINT status; +CHAR name[300]; + + /* Pickup the first entry in the root directory. */ + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, "A0")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "B0")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "C0")) + status++; + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "D0")) + status++; + + /* Okay, now postion to the deepest sub-directory and ensure that the directory first/next + operations work properly there as well. */ + + /* Set the current path */ + status = fx_directory_default_set(&ram_disk, "/C0/C3"); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error setting the path. Return to caller. */ + printf("ERROR!\n"); + test_control_return(16); + } + + /* Pickup the first entry in this sub-sub directory. */ + status = fx_directory_first_entry_find(&ram_disk, name); + if (strcmp(name, ".")) + status++; + + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "..")) + status++; + + status = fx_directory_next_entry_find(&ram_disk, name); + if (strcmp(name, "C00")) + status++; + + /* Now read one past the number of entries in order to get an error condition. */ + status += fx_directory_next_entry_find(&ram_disk, name); + + /* Determine if the test was successful. */ + if (status != FX_NO_MORE_ENTRIES) + { + + printf("ERROR!\n"); + test_control_return(18); + } + +} + +#endif + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_long_directory_rename_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Long Dir Rename Redo LOG Interrupt Test.N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_long_file_create_directory_interrupt_test.c b/test/regression_test/filex_fault_tolerant_long_file_create_directory_interrupt_test.c new file mode 100644 index 0000000..451d460 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_long_file_create_directory_interrupt_test.c @@ -0,0 +1,766 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check directory interrupt for fx_file_create() : +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 26 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to create new file with long file name; +Step7: Terminate the new thread to simulate poweroff when update directory. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(redo operation); +Step10: Check the test files. +*/ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_long_file_create_directory_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT directory_write_interrupt = FX_FALSE; +static CHAR read_buffer[1024]; +static UINT read_buffer_size = 1024; +static CHAR file_name[256] = "C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + +#define TEST_COUNT 3 + + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_long_file_create_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Long File Create DIR Interrupt Test....."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " 012345678901234567890123\n", 26); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + directory_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, file_name, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(37); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_END_OF_FILE) || (actual != 0)) + { + + printf("ERROR!\n"); + test_control_return(38); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(39); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (directory_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(40); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Create a file called file name in the root directory. */ + fx_file_create(&ram_disk, file_name); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the Directory write operation after record the redo log. */ + if ((sector_type == FX_DIRECTORY_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + directory_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_long_file_create_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Long File Create DIR Interrupt Test.....N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_long_file_create_redo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_long_file_create_redo_log_interrupt_test.c new file mode 100644 index 0000000..6c86269 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_long_file_create_redo_log_interrupt_test.c @@ -0,0 +1,732 @@ +/* This FileX test concentrates on the Fault-Tolerant redo LOG write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check redo log interrupt for fx_file_create() : +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 26 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to create new file with long file name; +Step7: Terminate the new thread to simulate poweroff when update the redo log. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(undo operation); +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_long_file_create_redo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT log_write_interrupt = FX_FALSE; +static CHAR read_buffer[1024]; +static UINT read_buffer_size = 1024; +static CHAR file_name[256] = "C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_long_file_create_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Long File Create Redo LOG Interrupt Test."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " 012345678901234567890123\n", 26); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST2.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST2.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, file_name, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status == FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (log_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(37); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Create a file called file name in the root directory. */ + fx_file_create(&ram_disk, file_name); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the redo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_long_file_create_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Long File Create Redo LOG Interrupt Test.N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_loop_write_data_test.c b/test/regression_test/filex_fault_tolerant_loop_write_data_test.c new file mode 100644 index 0000000..78b5a9e --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_loop_write_data_test.c @@ -0,0 +1,401 @@ +/* This FileX test concentrates on the Fault-Tolerant data loop write operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + + 1024 1024 1024 1024 1024 1024 + |------------|------------|-----------|------------|------------|------------| + | | TEST.TXT | TEST.TXT | | | | + |------------|------------|-----------|------------|------------|------------| + +Check loop write data operation: +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create new file called "TEST.TXT"; +Step4: Check the media available bytes; +Step5: Write 1500 bytes to "TEST.TXT"(two clusters); +Step6: Check the media available bytes; +Step7: Loop to write random size bytes to "TEST.TXT"(less than two clusters); +Step8: Check the media available bytes every write operation. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_loop_write_data_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static CHAR write_buffer[2048]; +static UINT write_buffer_size = 2048; +static UINT write_index; +static CHAR read_buffer[4096]; +static UINT read_buffer_size = 4096; +static UINT i; + +#define TEST_COUNT 3 /* FAT12, 16, 32. */ + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_loop_write_data_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT loop_count; +UINT seek_index; +UINT write_size; +UINT two_cluster_size; +ULONG file_size; +ULONG current_available_bytes; +ULONG available_bytes; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Loop Write DATA Test...................."); + + /* Random genearte the write data. */ + for (write_index = 0; write_index < write_buffer_size; write_index ++) + write_buffer[write_index] = (CHAR)rand(); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Initialize the parmeters. */ + file_size = 1500; + two_cluster_size = 2048; + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Pickup the available bytes in the media. */ + status = fx_media_space_available(&ram_disk, &available_bytes); + + /* Check for available bytes error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Write a string to the test file, (two cluster). */ + status = fx_file_write(&my_file, (void *) write_buffer, file_size); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Pickup the available bytes in the media. */ + status = fx_media_space_available(&ram_disk, ¤t_available_bytes); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) && (available_bytes != (current_available_bytes + two_cluster_size * 2))) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Update the avalibale_bytes. */ + available_bytes = current_available_bytes; + + /* Loop to write the data to test file. */ + for (loop_count = 0; loop_count < 1000; loop_count ++) + { + + /* Random generate the seek index (0~file_size). */ + seek_index = (ULONG)rand() % (file_size + 1); + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, seek_index); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Random generate the write size, (0 ~ two cluster size - seek_index); */ + write_size = (UINT)rand() % (two_cluster_size - seek_index + 1); + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, (void *) write_buffer, write_size); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Update the file_size. */ + if (seek_index + write_size > file_size) + file_size = seek_index + write_size; + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != file_size)) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Pickup the available bytes in the media. */ + status = fx_media_space_available(&ram_disk, &available_bytes); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (available_bytes != current_available_bytes)) + { + + printf("ERROR!\n"); + test_control_return(13); + } + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_loop_write_data_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Loop Write DATA Test....................N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_media_check_test.c b/test/regression_test/filex_fault_tolerant_media_check_test.c new file mode 100644 index 0000000..0c48d3a --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_media_check_test.c @@ -0,0 +1,191 @@ +/* This FileX test concentrates on the basic media check operation. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_fault_tolerant.h" +#include "fx_ram_driver_test.h" +#include +extern void test_control_return(UINT status); +void filex_fault_tolerant_media_check_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define SCRATCH_MEMORY_SIZE 2000 + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static UCHAR fault_tolerant_buffer[FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE]; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR ram_disk_memory[2048 * 256]; +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR *scratch_memory; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR scratch_memory[SCRATCH_MEMORY_SIZE]; +#endif + + + + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_media_check_application_define(void *first_unused_memory) +#endif +{ +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + scratch_memory = pointer; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG errors_detected; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Media Check Test........................"); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + sizeof(cache_buffer), // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 2048, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(2); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, sizeof(cache_buffer)); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(3); + } + + /* Enable fault tolerant. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, sizeof(fault_tolerant_buffer)); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(4); + } + + /* Check the media for errors. */ + status = fx_media_check(&ram_disk, scratch_memory, SCRATCH_MEMORY_SIZE, FX_LOST_CLUSTER_ERROR, &errors_detected); + + /* Determine if any were found - should not have found any errors at this point. */ + if ((status != FX_SUCCESS) || (errors_detected)) + { + + /* Error in the media. Return to caller. */ + printf("ERROR!\n"); + test_control_return(5); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(6); + } + + printf("SUCCESS!\n"); + test_control_return(0); +} + +#else +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_media_check_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Media Check Test........................N/A\n"); + + test_control_return(255); +} +#endif diff --git a/test/regression_test/filex_fault_tolerant_media_full_test.c b/test/regression_test/filex_fault_tolerant_media_full_test.c new file mode 100644 index 0000000..a8c6804 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_media_full_test.c @@ -0,0 +1,282 @@ +/* This FileX test concentrates on the Fault-Tolerant Media Full test. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; +Check media full operation: +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create new file called "TEST.TXT"; +Step4: Get the media available bytes; +Step5: Loop to write bytes out of the file to fill the media; +Step6: Check the media available bytes. +Step7: Loop to read and check the file data from the media; +*/ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_media_full_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_media_full_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +ULONG read_value; +ULONG write_value; +ULONG available_bytes; +ULONG i, j; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Media Full Test........................."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + return_if_fail( status == FX_SUCCESS); + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + /* Pickup the available bytes in the media. */ + status = fx_media_space_available(&ram_disk, &available_bytes); + return_if_fail( (status == FX_SUCCESS) && (available_bytes >= sizeof(ULONG))); + + /* Loop to write successive bytes out to the file.... to fill the media! */ + j = 0; + write_value = 0; + while (j < available_bytes) + { + + /* Write 4 bytes to the file. */ + status = fx_file_write(&my_file, (void *) &write_value, sizeof(ULONG)); + return_if_fail( status == FX_SUCCESS); + + /* Increment byte count. */ + j = j + sizeof(ULONG); + + /* Increment write value. */ + write_value++; + } + + /* Pickup the available bytes in the media again. */ + status = fx_media_space_available(&ram_disk, &j); + return_if_fail( (status == FX_SUCCESS) && (j == 0)); + +#ifndef FX_DISABLE_CACHE + /* At this point, we should invalidate the (which also flushes the cache) media to ensure that all + dirty sectors are written. */ + status = fx_media_cache_invalidate(&ram_disk); + return_if_fail((status == FX_SUCCESS) && (ram_disk.fx_media_sector_cache_dirty_count == 0)); + + /* See if any sectors are still valid in the cache. */ + for (j = 0; j < ram_disk.fx_media_sector_cache_size; j++) + { + + /* Determine if this cache entry is still valid. */ + return_if_fail(ram_disk.fx_media_sector_cache[j].fx_cached_sector_valid == 0); + } +#endif /* FX_DISABLE_CACHE */ + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + return_if_fail( status == FX_SUCCESS); + + /* Now read in all the bytes again to make sure the file contents are really there. */ + j = 0; + read_value = 0; + write_value = 0; + while (j < available_bytes) + { + + /* Read 4 bytes from the file. */ + status = fx_file_read(&my_file, (void *) &read_value, sizeof(ULONG), &actual); + + /* Check the file read status. */ + return_if_fail( (status == FX_SUCCESS) && (actual == 4) && (read_value == write_value)); + + /* Increment byte count. */ + j = j + sizeof(ULONG); + + /* Increment write value. */ + write_value++; + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_media_full_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Media Full Test.........................N/A\n"); + + test_control_return(255); +} +#endif diff --git a/test/regression_test/filex_fault_tolerant_recover_fail_test.c b/test/regression_test/filex_fault_tolerant_recover_fail_test.c new file mode 100644 index 0000000..bd7c1cb --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_recover_fail_test.c @@ -0,0 +1,364 @@ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_recover_fail_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif + +static UINT error_couter = 0; +static UINT data_write_interrupt = FX_FALSE; +static CHAR write_buffer[2048]; +static UINT write_buffer_size = 2048; +static UINT write_index; +static CHAR read_buffer[4096]; +static UINT read_buffer_size = 4096; +static UINT data_size = 0; + +#define SEEK_COUNT 5 +#define FAT_COUNT 3 /* FAT12, 16, 32. */ +#define TEST_COUNT FAT_COUNT * SEEK_COUNT + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_recover_fail_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault tolerant recover fail test......................."); + + for (i = 1; i <= 2; i++) + { + + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk and enable fault_tolerant feature. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Prepare a file for test. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + status += fx_file_seek(&my_file, 0); + return_if_fail( status == FX_SUCCESS); + + /* Read the bytes of the test file. */ + memset(read_buffer, 0, read_buffer_size); + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + return_if_fail( (status == FX_SUCCESS) && (actual == 28)); + + /* Close the test file. */ + status = fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + /* Create a file called TEST1.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST1.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + /* Write some data into file. */ + data_size = 1500; + status = fx_file_write(&my_file, (void *) write_buffer, data_size); + status += fx_file_seek(&my_file, 0); + return_if_fail( status == FX_SUCCESS); + + /* Read the bytes of the test file. */ + memset(read_buffer, 0, read_buffer_size); + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + return_if_fail( (status == FX_SUCCESS) && (actual == data_size)); + + /* Close the test file. */ + status = fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + data_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, try to enable fault_tolerant again. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Enable the Fault-tolerant feature to recover the media. */ + /* Make a mistake when fx_fault_tolerant_recover link original chain back to the front of the insertion point. */ + _fx_utility_fat_entry_write_error_request = i; + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_IO_ERROR); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail((status == FX_SUCCESS) && (data_write_interrupt == FX_TRUE) && (error_couter == 0)); + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + status = fx_file_seek(&my_file, 1500); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Random genearte the write data. */ + for (write_index = 0; write_index < write_buffer_size; write_index ++) + { + write_buffer[write_index] = (CHAR)rand(); + } + + /* Write 1024 bytes to the file, then update the FAT table. (bytes should be greate than one cluster). */ + fx_file_write(&my_file, (void *) write_buffer, write_buffer_size); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the Data write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_UNDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + data_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_recover_fail_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault tolerant recover fail test.......................N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_unicode_directory_create_directory_interrupt_test.c b/test/regression_test/filex_fault_tolerant_unicode_directory_create_directory_interrupt_test.c new file mode 100644 index 0000000..4782839 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_unicode_directory_create_directory_interrupt_test.c @@ -0,0 +1,852 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check directory interrupt for fx_unicode_directory_create(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create directories and sub-directories; +Step4: Traverse the directory; +Step5: Create new thread to create new directory "/B/B2/B22/B222"; +Step6: Terminate the new thread to simulate poweroff when update the directory after record the redo log. +Step7: Open the media; +Step8: Enable fault tolerant feature to recover the data(redo operation); +Step9: Traverse the directory; +Step10: Recreate directory "/B/B2/B22/B222"; +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_unicode_directory_create_directory_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static UCHAR *pointer; + +#define TEST_COUNT 3 + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_counter = 0; +static UINT directory_write_interrupt = FX_FALSE; + +static UCHAR directory_name_A[] = {'A', 0, 0, 0}; +static UCHAR directory_name_B[] = {'B', 0, 0, 0}; +static UCHAR directory_name_C[] = {'C', 0, 0, 0}; +static UCHAR directory_name_A_A1[] = {'A', 0, '1', 0, 0, 0}; +static UCHAR directory_name_A_A2[] = {'A', 0, '2', 0, 0, 0}; +static UCHAR directory_name_A_A3[] = {'A', 0, '3', 0, 0, 0}; +static UCHAR directory_name_B_B1[] = {'B', 0, '1', 0, 0, 0}; +static UCHAR directory_name_B_B2[] = {'B', 0, '2', 0, 0, 0}; +static UCHAR directory_name_B_B3[] = {'B', 0, '3', 0, 0, 0}; +static UCHAR directory_name_C_C1[] = {'C', 0, '1', 0, 0, 0}; +static UCHAR directory_name_C_C2[] = {'C', 0, '2', 0, 0, 0}; +static UCHAR directory_name_C_C3[] = {'C', 0, '3', 0, 0, 0}; +static UCHAR directory_name_A_A1_A11[] = {'A', 0, '1', 0, '1', 0, 0, 0}; +static UCHAR directory_name_B_B2_B22[] = {'B', 0, '2', 0, '2', 0, 0, 0}; +static UCHAR directory_name_C_C3_C33[] = {'C', 0, '3', 0, '3', 0, 0, 0}; +static UCHAR directory_name_B_B2_B22_B222[] = {'B', 0, '2', 0, '2', 0, '2', 0, 0, 0}; +static CHAR directory_name_A_short[20]; +static CHAR directory_name_B_short[20]; +static CHAR directory_name_C_short[20]; +static CHAR directory_name_A_A1_short[20]; +static CHAR directory_name_A_A2_short[20]; +static CHAR directory_name_A_A3_short[20]; +static CHAR directory_name_B_B1_short[20]; +static CHAR directory_name_B_B2_short[20]; +static CHAR directory_name_B_B3_short[20]; +static CHAR directory_name_C_C1_short[20]; +static CHAR directory_name_C_C2_short[20]; +static CHAR directory_name_C_C3_short[20]; +static CHAR directory_name_A_A1_A11_short[20]; +static CHAR directory_name_B_B2_B22_short[20]; +static CHAR directory_name_C_C3_C33_short[20]; +static CHAR directory_name_B_B2_B22_B222_short[20]; +static CHAR directory_name_path_short[100]; + +static UINT expected_index = 0; +static CHAR **expected_name; +static CHAR *expected_name_0[] = { "A", ".", "..", "A1", ".", "..", "A11", ".", "..", "A2", ".", "..", "A3", ".", "..", "B", ".", "..", "B1", ".", + "..", "B2", ".", "..", "B22", ".", "..", "B3", ".", "..", "C", ".", "..", "C1", ".", "..", "C2", ".", "..", "C3", ".", "..", "C33", ".", "..", "END"}; +static CHAR *expected_name_1[] = { "A", ".", "..", "A1", ".", "..", "A11", ".", "..", "A2", ".", "..", "A3", ".", "..", "B", ".", "..", "B1", ".", "..", + "B2", ".", "..", "B22", ".", "..", "B222", ".", "..", "B3", ".", "..", "C", ".", "..", "C1", ".", "..", "C2", ".", "..", "C3", ".", "..", "C33", ".", + "..", "END"}; + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +static void traverse_directory(CHAR *directory_name); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_unicode_directory_create_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Create the main thread. */ + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT i; +UINT j; +UINT index; +ULONG length; +FX_LOCAL_PATH local_path; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Unicode Dir Create DIR Interrupt Test..."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Set the local path to "\\". */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, "\\"); +#else + status = fx_directory_default_set(&ram_disk, "\\"); +#endif + return_if_fail( status == FX_SUCCESS); + + /* Create a series of directories..."/A", "/B", "/C" */ + length = fx_unicode_length_get(directory_name_A); + status = fx_unicode_directory_create(&ram_disk, directory_name_A, length, directory_name_A_short); + length = fx_unicode_length_get(directory_name_B); + status += fx_unicode_directory_create(&ram_disk, directory_name_B, length, directory_name_B_short); + length = fx_unicode_length_get(directory_name_C); + status += fx_unicode_directory_create(&ram_disk, directory_name_C, length, directory_name_C_short); + return_if_fail( status == FX_SUCCESS); + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_A_short[j]) + { + directory_name_path_short[index++] = directory_name_A_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /A. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + return_if_fail( status == FX_SUCCESS); + + /* Create the next level of sub-directories. + "/A/A1", "/A/A2", "/A/A3", */ + length = fx_unicode_length_get(directory_name_A_A1); + status = fx_unicode_directory_create(&ram_disk, directory_name_A_A1, length, directory_name_A_A1_short); + length = fx_unicode_length_get(directory_name_A_A2); + status += fx_unicode_directory_create(&ram_disk, directory_name_A_A2, length, directory_name_A_A2_short); + length = fx_unicode_length_get(directory_name_A_A3); + status += fx_unicode_directory_create(&ram_disk, directory_name_A_A3, length, directory_name_A_A3_short); + return_if_fail( status == FX_SUCCESS); + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_B_short[j]) + { + directory_name_path_short[index++] = directory_name_B_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /B. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + return_if_fail( status == FX_SUCCESS); + + /* Create the next level of sub-directories. + "/B/B1", "/B/B2", "/B/B3", */ + length = fx_unicode_length_get(directory_name_B_B1); + status = fx_unicode_directory_create(&ram_disk, directory_name_B_B1, length, directory_name_B_B1_short); + length = fx_unicode_length_get(directory_name_B_B2); + status += fx_unicode_directory_create(&ram_disk, directory_name_B_B2, length, directory_name_B_B2_short); + length = fx_unicode_length_get(directory_name_B_B3); + status += fx_unicode_directory_create(&ram_disk, directory_name_B_B3, length, directory_name_B_B3_short); + return_if_fail( status == FX_SUCCESS); + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_C_short[j]) + { + directory_name_path_short[index++] = directory_name_C_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /C. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + return_if_fail( status == FX_SUCCESS); + + /* Create the next level of sub-directories. + "/C/C1", "/C/C2", "/C/C1" */ + length = fx_unicode_length_get(directory_name_C_C1); + status = fx_unicode_directory_create(&ram_disk, directory_name_C_C1, length, directory_name_C_C1_short); + length = fx_unicode_length_get(directory_name_C_C2); + status = fx_unicode_directory_create(&ram_disk, directory_name_C_C2, length, directory_name_C_C2_short); + length = fx_unicode_length_get(directory_name_C_C3); + status = fx_unicode_directory_create(&ram_disk, directory_name_C_C3, length, directory_name_C_C3_short); + return_if_fail( status == FX_SUCCESS); + + /* Now create the third level of sub-directories... + "/A/A1/A11", "/B/B2/B22", "/C/C3/C33" */ + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /A. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_A_short[j]) + { + directory_name_path_short[index++] = directory_name_A_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /A/A1. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_A_A1_short[j]) + { + directory_name_path_short[index++] = directory_name_A_A1_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /A/A1. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + length = fx_unicode_length_get(directory_name_A_A1_A11); + status += fx_unicode_directory_create(&ram_disk, directory_name_A_A1_A11, length, directory_name_A_A1_A11_short); + return_if_fail( status == FX_SUCCESS); + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /B. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_B_short[j]) + { + directory_name_path_short[index++] = directory_name_B_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /B/B2. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_B_B2_short[j]) + { + directory_name_path_short[index++] = directory_name_B_B2_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /B/B2. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + length = fx_unicode_length_get(directory_name_B_B2_B22); + status += fx_unicode_directory_create(&ram_disk, directory_name_B_B2_B22, length, directory_name_B_B2_B22_short); + return_if_fail( status == FX_SUCCESS); + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /C. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_C_short[j]) + { + directory_name_path_short[index++] = directory_name_C_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /C/C3. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_C_C3_short[j]) + { + directory_name_path_short[index++] = directory_name_C_C3_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /C/C3. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + length = fx_unicode_length_get(directory_name_C_C3_C33); + status += fx_unicode_directory_create(&ram_disk, directory_name_C_C3_C33, length, directory_name_C_C3_C33_short); + return_if_fail( status == FX_SUCCESS); + + /* Set the index as zero before traverse. */ + expected_index = 0; + + /* Traverse the directory structure recursively. */ + expected_name = expected_name_0; + traverse_directory(FX_NULL); + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + directory_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Set the index as zero before traverse. */ + expected_index = 0; + + /* Traverse the directory structure recursively. */ + expected_name = expected_name_1; + traverse_directory(FX_NULL); + + /* Attempt to create the same directory again. */ + /* Set the local path to /B/B2/B22. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + length = fx_unicode_length_get(directory_name_B_B2_B22_B222); + status = fx_unicode_directory_create(&ram_disk, directory_name_B_B2_B22_B222, length, directory_name_B_B2_B22_B222_short); + return_if_fail( status == FX_ALREADY_CREATED); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( (status == FX_SUCCESS) && (directory_write_interrupt == FX_TRUE) && (error_counter == 0)); + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + +UINT index; +UINT j; +ULONG length; +FX_LOCAL_PATH local_path; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Now create the fourth level of sub-directories... + "/B/B2/B22/B222" */ + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /B. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_B_short[j]) + { + directory_name_path_short[index++] = directory_name_B_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /B/B2. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_B_B2_short[j]) + { + directory_name_path_short[index++] = directory_name_B_B2_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /B/B2/B22. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_B_B2_B22_short[j]) + { + directory_name_path_short[index++] = directory_name_B_B2_B22_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /B/B2/B22. */ +#ifndef FX_STANDALONE_ENABLE + fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + length = fx_unicode_length_get(directory_name_B_B2_B22_B222); + fx_unicode_directory_create(&ram_disk, directory_name_B_B2_B22_B222, length, directory_name_B_B2_B22_B222_short); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the Directory write operation after record the redo log. */ + if ((sector_type == FX_DIRECTORY_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + directory_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} + +/* Define the FAT12, 16 and 32 directory traversal routine for the local path test. */ +static void traverse_directory(CHAR *directory_name) +{ + +FX_LOCAL_PATH local_path; +FX_FILE file; +UINT status; +CHAR name[300]; +UINT skip; + + + /* Determine if we are at the start. */ + if (directory_name == FX_NULL) + { +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, "\\"); +#else + status = fx_directory_default_set(&ram_disk, "\\"); +#endif + skip = 0; + } + else + { +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name); +#else + status = fx_directory_default_set(&ram_disk, directory_name); +#endif + skip = 2; + } + + /* Get the first directory entry in the root path. */ + status = fx_directory_first_entry_find(&ram_disk, name); + + /* Loop through the directory entries for this path. */ + while (status == FX_SUCCESS) + { + + /* Compare with what is expected. */ + if (strcmp(name, expected_name[expected_index++])) + error_counter++; + + /* Determine if this name is a directory or a file. */ + status = fx_directory_name_test(&ram_disk, name); + + /* What is it? */ + if (status == FX_NOT_DIRECTORY) + { + + /* This is a file, open it to test its integrity. */ + status = fx_file_open(&ram_disk, &file, name, FX_OPEN_FOR_READ); + if (status != FX_SUCCESS) + error_counter++; + status = fx_file_close(&file); + if (status != FX_SUCCESS) + error_counter++; + + /* Pickup the next directory entry. */ + status = fx_directory_next_entry_find(&ram_disk, name); + } + else if (status == FX_SUCCESS) + { + + /* Skip the first two entries... */ + if (skip) + skip--; + else + { + /* Recursive call to traverse directory. */ + traverse_directory(name); + + /* Restore path. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_restore(&ram_disk, &local_path); +#endif + } + + status = fx_directory_next_entry_find(&ram_disk, name); + } + } + +#ifndef FX_STANDALONE_ENABLE + fx_directory_local_path_clear(&ram_disk); +#endif +} + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_unicode_directory_create_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Unicode Dir Create DIR Interrupt Test...N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_unicode_directory_create_redo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_unicode_directory_create_redo_log_interrupt_test.c new file mode 100644 index 0000000..fef8099 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_unicode_directory_create_redo_log_interrupt_test.c @@ -0,0 +1,1098 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check redo log interrupt for fx_unicode_directory_create(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create directories and sub-directories; +Step4: Traverse the directory; +Step5: Create new thread to create new directory "/B/B2/B22/B222"; +Step6: Terminate the new thread to simulate poweroff when update the redo log. +Step7: Open the media; +Step8: Enable fault tolerant feature to recover the data(undo operation); +Step9: Traverse the directory; +Step10: Recreate directory "/B/B2/B22/B222"; +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_unicode_directory_create_redo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static UCHAR *pointer; + +#define TEST_COUNT 3 + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_counter = 0; +static UINT redo_log_write_interrupt = FX_FALSE; + +static UCHAR directory_name_A[] = {'A', 0, 0, 0}; +static UCHAR directory_name_B[] = {'B', 0, 0, 0}; +static UCHAR directory_name_C[] = {'C', 0, 0, 0}; +static UCHAR directory_name_A_A1[] = {'A', 0, '1', 0, 0, 0}; +static UCHAR directory_name_A_A2[] = {'A', 0, '2', 0, 0, 0}; +static UCHAR directory_name_A_A3[] = {'A', 0, '3', 0, 0, 0}; +static UCHAR directory_name_B_B1[] = {'B', 0, '1', 0, 0, 0}; +static UCHAR directory_name_B_B2[] = {'B', 0, '2', 0, 0, 0}; +static UCHAR directory_name_B_B3[] = {'B', 0, '3', 0, 0, 0}; +static UCHAR directory_name_C_C1[] = {'C', 0, '1', 0, 0, 0}; +static UCHAR directory_name_C_C2[] = {'C', 0, '2', 0, 0, 0}; +static UCHAR directory_name_C_C3[] = {'C', 0, '3', 0, 0, 0}; +static UCHAR directory_name_A_A1_A11[] = {'A', 0, '1', 0, '1', 0, 0, 0}; +static UCHAR directory_name_B_B2_B22[] = {'B', 0, '2', 0, '2', 0, 0, 0}; +static UCHAR directory_name_C_C3_C33[] = {'C', 0, '3', 0, '3', 0, 0, 0}; +static UCHAR directory_name_B_B2_B22_B222[] = {'B', 0, '2', 0, '2', 0, '2', 0, 0, 0}; +static CHAR directory_name_A_short[20]; +static CHAR directory_name_B_short[20]; +static CHAR directory_name_C_short[20]; +static CHAR directory_name_A_A1_short[20]; +static CHAR directory_name_A_A2_short[20]; +static CHAR directory_name_A_A3_short[20]; +static CHAR directory_name_B_B1_short[20]; +static CHAR directory_name_B_B2_short[20]; +static CHAR directory_name_B_B3_short[20]; +static CHAR directory_name_C_C1_short[20]; +static CHAR directory_name_C_C2_short[20]; +static CHAR directory_name_C_C3_short[20]; +static CHAR directory_name_A_A1_A11_short[20]; +static CHAR directory_name_B_B2_B22_short[20]; +static CHAR directory_name_C_C3_C33_short[20]; +static CHAR directory_name_B_B2_B22_B222_short[20]; +static CHAR directory_name_path_short[100]; + +static UINT expected_index = 0; +static CHAR **expected_name; +static CHAR * expected_name_0[] = { + + "A", + ".", + "..", + "A1", + ".", + "..", + "A11", + ".", + "..", + "A2", + ".", + "..", + "A3", + ".", + "..", + "B", + ".", + "..", + "B1", + ".", + "..", + "B2", + ".", + "..", + "B22", + ".", + "..", + "B3", + ".", + "..", + "C", + ".", + "..", + "C1", + ".", + "..", + "C2", + ".", + "..", + "C3", + ".", + "..", + "C33", + ".", + "..", + "END"}; + +static CHAR * expected_name_1[] = { + + "A", + ".", + "..", + "A1", + ".", + "..", + "A11", + ".", + "..", + "A2", + ".", + "..", + "A3", + ".", + "..", + "B", + ".", + "..", + "B1", + ".", + "..", + "B2", + ".", + "..", + "B22", + ".", + "..", + "B222", + ".", + "..", + "B3", + ".", + "..", + "C", + ".", + "..", + "C1", + ".", + "..", + "C2", + ".", + "..", + "C3", + ".", + "..", + "C33", + ".", + "..", + "END"}; + + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +static void traverse_directory(CHAR *directory_name); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_unicode_directory_create_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Create the main thread. */ + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT i; +UINT j; +UINT index; +ULONG length; +FX_LOCAL_PATH local_path; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Unicode Dir Create Redo Interrupt Test.."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Set the local path to "\\". */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, "\\"); +#else + status = fx_directory_default_set(&ram_disk, "\\"); +#endif + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Create a series of directories..."/A", "/B", "/C" */ + length = fx_unicode_length_get(directory_name_A); + status = fx_unicode_directory_create(&ram_disk, directory_name_A, length, directory_name_A_short); + length = fx_unicode_length_get(directory_name_B); + status += fx_unicode_directory_create(&ram_disk, directory_name_B, length, directory_name_B_short); + length = fx_unicode_length_get(directory_name_C); + status += fx_unicode_directory_create(&ram_disk, directory_name_C, length, directory_name_C_short); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(5); + } + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_A_short[j]) + { + directory_name_path_short[index++] = directory_name_A_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /A. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(6); + } + + /* Create the next level of sub-directories. + "/A/A1", "/A/A2", "/A/A3", */ + length = fx_unicode_length_get(directory_name_A_A1); + status = fx_unicode_directory_create(&ram_disk, directory_name_A_A1, length, directory_name_A_A1_short); + length = fx_unicode_length_get(directory_name_A_A2); + status += fx_unicode_directory_create(&ram_disk, directory_name_A_A2, length, directory_name_A_A2_short); + length = fx_unicode_length_get(directory_name_A_A3); + status += fx_unicode_directory_create(&ram_disk, directory_name_A_A3, length, directory_name_A_A3_short); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(7); + } + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_B_short[j]) + { + directory_name_path_short[index++] = directory_name_B_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /B. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(8); + } + + /* Create the next level of sub-directories. + "/B/B1", "/B/B2", "/B/B3", */ + length = fx_unicode_length_get(directory_name_B_B1); + status = fx_unicode_directory_create(&ram_disk, directory_name_B_B1, length, directory_name_B_B1_short); + length = fx_unicode_length_get(directory_name_B_B2); + status += fx_unicode_directory_create(&ram_disk, directory_name_B_B2, length, directory_name_B_B2_short); + length = fx_unicode_length_get(directory_name_B_B3); + status += fx_unicode_directory_create(&ram_disk, directory_name_B_B3, length, directory_name_B_B3_short); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(9); + } + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_C_short[j]) + { + directory_name_path_short[index++] = directory_name_C_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /C. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(10); + } + + /* Create the next level of sub-directories. + "/C/C1", "/C/C2", "/C/C1" */ + length = fx_unicode_length_get(directory_name_C_C1); + status = fx_unicode_directory_create(&ram_disk, directory_name_C_C1, length, directory_name_C_C1_short); + length = fx_unicode_length_get(directory_name_C_C2); + status = fx_unicode_directory_create(&ram_disk, directory_name_C_C2, length, directory_name_C_C2_short); + length = fx_unicode_length_get(directory_name_C_C3); + status = fx_unicode_directory_create(&ram_disk, directory_name_C_C3, length, directory_name_C_C3_short); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(11); + } + + /* Now create the third level of sub-directories... + "/A/A1/A11", "/B/B2/B22", "/C/C3/C33" */ + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /A. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_A_short[j]) + { + directory_name_path_short[index++] = directory_name_A_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /A/A1. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_A_A1_short[j]) + { + directory_name_path_short[index++] = directory_name_A_A1_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /A/A1. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + length = fx_unicode_length_get(directory_name_A_A1_A11); + status += fx_unicode_directory_create(&ram_disk, directory_name_A_A1_A11, length, directory_name_A_A1_A11_short); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(12); + } + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /B. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_B_short[j]) + { + directory_name_path_short[index++] = directory_name_B_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /B/B2. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_B_B2_short[j]) + { + directory_name_path_short[index++] = directory_name_B_B2_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /B/B2. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + length = fx_unicode_length_get(directory_name_B_B2_B22); + status += fx_unicode_directory_create(&ram_disk, directory_name_B_B2_B22, length, directory_name_B_B2_B22_short); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(13); + } + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /C. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_C_short[j]) + { + directory_name_path_short[index++] = directory_name_C_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /C/C3. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_C_C3_short[j]) + { + directory_name_path_short[index++] = directory_name_C_C3_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /C/C3. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + length = fx_unicode_length_get(directory_name_C_C3_C33); + status += fx_unicode_directory_create(&ram_disk, directory_name_C_C3_C33, length, directory_name_C_C3_C33_short); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(14); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + + /* Traverse the directory structure recursively. */ + expected_name = expected_name_0; + traverse_directory(FX_NULL); + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + redo_log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(15); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + + /* Traverse the directory structure recursively. */ + expected_name = expected_name_0; + traverse_directory(FX_NULL); + + /* Attempt to create the same directory again. */ + /* Set the local path to /B/B2/B22. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + length = fx_unicode_length_get(directory_name_B_B2_B22_B222); + status = fx_unicode_directory_create(&ram_disk, directory_name_B_B2_B22_B222, length, directory_name_B_B2_B22_B222_short); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating same directory twice. Return to caller. */ + printf("ERROR!\n"); + test_control_return(17); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + + /* Traverse the directory structure recursively. */ + expected_name = expected_name_1; + traverse_directory(FX_NULL); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (redo_log_write_interrupt != FX_TRUE) || (error_counter)) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + +UINT index; +UINT j; +ULONG length; +FX_LOCAL_PATH local_path; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Now create the fourth level of sub-directories... + "/B/B2/B22/B222" */ + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /B. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_B_short[j]) + { + directory_name_path_short[index++] = directory_name_B_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /B/B2. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_B_B2_short[j]) + { + directory_name_path_short[index++] = directory_name_B_B2_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /B/B2/B22. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_B_B2_B22_short[j]) + { + directory_name_path_short[index++] = directory_name_B_B2_B22_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /B/B2/B22. */ +#ifndef FX_STANDALONE_ENABLE + fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + length = fx_unicode_length_get(directory_name_B_B2_B22_B222); + fx_unicode_directory_create(&ram_disk, directory_name_B_B2_B22_B222, length, directory_name_B_B2_B22_B222_short); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the redo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + redo_log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} + +/* Define the FAT12, 16 and 32 directory traversal routine for the local path test. */ +static void traverse_directory(CHAR *directory_name) +{ + +FX_LOCAL_PATH local_path; +FX_FILE file; +UINT status; +CHAR name[300]; +UINT skip; + + + /* Determine if we are at the start. */ + if (directory_name == FX_NULL) + { +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, "\\"); +#else + status = fx_directory_default_set(&ram_disk, "\\"); +#endif + skip = 0; + } + else + { +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name); +#else + status = fx_directory_default_set(&ram_disk, directory_name); +#endif + skip = 2; + } + + /* Get the first directory entry in the root path. */ + status = fx_directory_first_entry_find(&ram_disk, name); + + /* Loop through the directory entries for this path. */ + while (status == FX_SUCCESS) + { + + /* Compare with what is expected. */ + if (strcmp(name, expected_name[expected_index++])) + error_counter++; + + /* Determine if this name is a directory or a file. */ + status = fx_directory_name_test(&ram_disk, name); + + /* What is it? */ + if (status == FX_NOT_DIRECTORY) + { + + /* This is a file, open it to test its integrity. */ + status = fx_file_open(&ram_disk, &file, name, FX_OPEN_FOR_READ); + if (status != FX_SUCCESS) + error_counter++; + status = fx_file_close(&file); + if (status != FX_SUCCESS) + error_counter++; + + /* Pickup the next directory entry. */ + status = fx_directory_next_entry_find(&ram_disk, name); + } + else if (status == FX_SUCCESS) + { + + /* Skip the first two entries... */ + if (skip) + skip--; + else + { + /* Recursive call to traverse directory. */ + traverse_directory(name); + + /* Restore path. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_restore(&ram_disk, &local_path); +#endif + } + + status = fx_directory_next_entry_find(&ram_disk, name); + } + } + +#ifndef FX_STANDALONE_ENABLE + fx_directory_local_path_clear(&ram_disk); +#endif +} + + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_unicode_directory_create_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Unicode Dir Create Redo Interrupt Test..N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_unicode_directory_rename_directory_interrupt_test.c b/test/regression_test/filex_fault_tolerant_unicode_directory_rename_directory_interrupt_test.c new file mode 100644 index 0000000..07327fc --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_unicode_directory_rename_directory_interrupt_test.c @@ -0,0 +1,1086 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check directory interrupt for fx_unicode_directory_rename(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create directories and sub-directories; +Step4: Traverse the directory; +Step5: Create new thread to rename the directory "/B/B2/B22" to "/B/B2/B23"; +Step6: Terminate the new thread to simulate poweroff when update the directory after record the redo log. +Step7: Open the media; +Step8: Enable fault tolerant feature to recover the data(redo operation); +Step9: Traverse the directory; +Step10: Recreate directory "/B/B2/B22" and "/B/B2/B23"; +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_unicode_directory_rename_directory_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static UCHAR *pointer; + +#define TEST_COUNT 3 + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_counter = 0; +static UINT directory_write_interrupt = FX_FALSE; +static UCHAR directory_name_A[] = {'A', 0, 0, 0}; +static UCHAR directory_name_B[] = {'B', 0, 0, 0}; +static UCHAR directory_name_C[] = {'C', 0, 0, 0}; +static UCHAR directory_name_A_A1[] = {'A', 0, '1', 0, 0, 0}; +static UCHAR directory_name_A_A2[] = {'A', 0, '2', 0, 0, 0}; +static UCHAR directory_name_A_A3[] = {'A', 0, '3', 0, 0, 0}; +static UCHAR directory_name_B_B1[] = {'B', 0, '1', 0, 0, 0}; +static UCHAR directory_name_B_B2[] = {'B', 0, '2', 0, 0, 0}; +static UCHAR directory_name_B_B3[] = {'B', 0, '3', 0, 0, 0}; +static UCHAR directory_name_C_C1[] = {'C', 0, '1', 0, 0, 0}; +static UCHAR directory_name_C_C2[] = {'C', 0, '2', 0, 0, 0}; +static UCHAR directory_name_C_C3[] = {'C', 0, '3', 0, 0, 0}; +static UCHAR directory_name_A_A1_A11[] = {'A', 0, '1', 0, '1', 0, 0, 0}; +static UCHAR directory_name_B_B2_B22[] = {'B', 0, '2', 0, '2', 0, 0, 0}; +static UCHAR directory_name_B_B2_B23[] = {'B', 0, '2', 0, '3', 0, 0, 0}; +static UCHAR directory_name_C_C3_C33[] = {'C', 0, '3', 0, '3', 0, 0, 0}; +static CHAR directory_name_A_short[20]; +static CHAR directory_name_B_short[20]; +static CHAR directory_name_C_short[20]; +static CHAR directory_name_A_A1_short[20]; +static CHAR directory_name_A_A2_short[20]; +static CHAR directory_name_A_A3_short[20]; +static CHAR directory_name_B_B1_short[20]; +static CHAR directory_name_B_B2_short[20]; +static CHAR directory_name_B_B3_short[20]; +static CHAR directory_name_C_C1_short[20]; +static CHAR directory_name_C_C2_short[20]; +static CHAR directory_name_C_C3_short[20]; +static CHAR directory_name_A_A1_A11_short[20]; +static CHAR directory_name_B_B2_B22_short[20]; +static CHAR directory_name_B_B2_B23_short[20]; +static CHAR directory_name_C_C3_C33_short[20]; +static CHAR directory_name_path_short[100]; + +static UINT expected_index = 0; +static CHAR **expected_name; +static CHAR * expected_name_0[] = { + + "A", + ".", + "..", + "A1", + ".", + "..", + "A11", + ".", + "..", + "A2", + ".", + "..", + "A3", + ".", + "..", + "B", + ".", + "..", + "B1", + ".", + "..", + "B2", + ".", + "..", + "B22", + ".", + "..", + "B3", + ".", + "..", + "C", + ".", + "..", + "C1", + ".", + "..", + "C2", + ".", + "..", + "C3", + ".", + "..", + "C33", + ".", + "..", + "END"}; + +static CHAR * expected_name_1[] = { + + "A", + ".", + "..", + "A1", + ".", + "..", + "A11", + ".", + "..", + "A2", + ".", + "..", + "A3", + ".", + "..", + "B", + ".", + "..", + "B1", + ".", + "..", + "B2", + ".", + "..", + "B23", + ".", + "..", + "B3", + ".", + "..", + "C", + ".", + "..", + "C1", + ".", + "..", + "C2", + ".", + "..", + "C3", + ".", + "..", + "C33", + ".", + "..", + "END"}; + + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +static void traverse_directory(CHAR *directory_name); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_unicode_directory_rename_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; + + /* Create the main thread. */ + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT i; +UINT j; +UINT index; +ULONG length; +FX_LOCAL_PATH local_path; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Unicode Dir Rename DIR Interrupt Test..."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Set the local path to "\\". */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, "\\"); +#else + status = fx_directory_default_set(&ram_disk, "\\"); +#endif + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Create a series of directories..."/A", "/B", "/C" */ + length = fx_unicode_length_get(directory_name_A); + status = fx_unicode_directory_create(&ram_disk, directory_name_A, length, directory_name_A_short); + length = fx_unicode_length_get(directory_name_B); + status += fx_unicode_directory_create(&ram_disk, directory_name_B, length, directory_name_B_short); + length = fx_unicode_length_get(directory_name_C); + status += fx_unicode_directory_create(&ram_disk, directory_name_C, length, directory_name_C_short); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(5); + } + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_A_short[j]) + { + directory_name_path_short[index++] = directory_name_A_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /A. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(6); + } + + /* Create the next level of sub-directories. + "/A/A1", "/A/A2", "/A/A3", */ + length = fx_unicode_length_get(directory_name_A_A1); + status = fx_unicode_directory_create(&ram_disk, directory_name_A_A1, length, directory_name_A_A1_short); + length = fx_unicode_length_get(directory_name_A_A2); + status += fx_unicode_directory_create(&ram_disk, directory_name_A_A2, length, directory_name_A_A2_short); + length = fx_unicode_length_get(directory_name_A_A3); + status += fx_unicode_directory_create(&ram_disk, directory_name_A_A3, length, directory_name_A_A3_short); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(7); + } + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_B_short[j]) + { + directory_name_path_short[index++] = directory_name_B_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /B. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(8); + } + + /* Create the next level of sub-directories. + "/B/B1", "/B/B2", "/B/B3", */ + length = fx_unicode_length_get(directory_name_B_B1); + status = fx_unicode_directory_create(&ram_disk, directory_name_B_B1, length, directory_name_B_B1_short); + length = fx_unicode_length_get(directory_name_B_B2); + status += fx_unicode_directory_create(&ram_disk, directory_name_B_B2, length, directory_name_B_B2_short); + length = fx_unicode_length_get(directory_name_B_B3); + status += fx_unicode_directory_create(&ram_disk, directory_name_B_B3, length, directory_name_B_B3_short); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(9); + } + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_C_short[j]) + { + directory_name_path_short[index++] = directory_name_C_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /C. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(10); + } + + /* Create the next level of sub-directories. + "/C/C1", "/C/C2", "/C/C1" */ + length = fx_unicode_length_get(directory_name_C_C1); + status = fx_unicode_directory_create(&ram_disk, directory_name_C_C1, length, directory_name_C_C1_short); + length = fx_unicode_length_get(directory_name_C_C2); + status = fx_unicode_directory_create(&ram_disk, directory_name_C_C2, length, directory_name_C_C2_short); + length = fx_unicode_length_get(directory_name_C_C3); + status = fx_unicode_directory_create(&ram_disk, directory_name_C_C3, length, directory_name_C_C3_short); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(11); + } + + /* Now create the third level of sub-directories... + "/A/A1/A11", "/B/B2/B22", "/C/C3/C33" */ + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /A. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_A_short[j]) + { + directory_name_path_short[index++] = directory_name_A_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /A/A1. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_A_A1_short[j]) + { + directory_name_path_short[index++] = directory_name_A_A1_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /A/A1. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + length = fx_unicode_length_get(directory_name_A_A1_A11); + status += fx_unicode_directory_create(&ram_disk, directory_name_A_A1_A11, length, directory_name_A_A1_A11_short); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(12); + } + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /B. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_B_short[j]) + { + directory_name_path_short[index++] = directory_name_B_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /B/B2. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_B_B2_short[j]) + { + directory_name_path_short[index++] = directory_name_B_B2_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /B/B2. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + length = fx_unicode_length_get(directory_name_B_B2_B22); + status += fx_unicode_directory_create(&ram_disk, directory_name_B_B2_B22, length, directory_name_B_B2_B22_short); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(13); + } + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /C. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_C_short[j]) + { + directory_name_path_short[index++] = directory_name_C_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /C/C3. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_C_C3_short[j]) + { + directory_name_path_short[index++] = directory_name_C_C3_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /C/C3. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + length = fx_unicode_length_get(directory_name_C_C3_C33); + status += fx_unicode_directory_create(&ram_disk, directory_name_C_C3_C33, length, directory_name_C_C3_C33_short); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(14); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + + /* Traverse the directory structure recursively. */ + expected_name = expected_name_0; + traverse_directory(FX_NULL); + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + directory_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(15); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + + /* Traverse the directory structure recursively. */ + expected_name = expected_name_1; + traverse_directory(FX_NULL); + + /* Set the local path to /B/B2. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + + /* Attempt to create the same directory again "/B/B2/B22". */ + length = fx_unicode_length_get(directory_name_B_B2_B22); + status = fx_unicode_directory_create(&ram_disk, directory_name_B_B2_B22, length, directory_name_B_B2_B22_short); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating same directory twice. Return to caller. */ + printf("ERROR!\n"); + test_control_return(17); + } + + /* Attempt to create the same directory again "/B/B2/B23". */ + length = fx_unicode_length_get(directory_name_B_B2_B23); + status = fx_unicode_directory_create(&ram_disk, directory_name_B_B2_B23, length, directory_name_B_B2_B23_short); + + /* Check for errors... */ + if (status != FX_ALREADY_CREATED) + { + + /* Error creating same directory twice. Return to caller. */ + printf("ERROR!\n"); + test_control_return(18); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (directory_write_interrupt != FX_TRUE) || (error_counter)) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + +UINT index; +UINT j; +ULONG directory_name_B_B2_B22_length; +ULONG directory_name_B_B2_B23_length; +FX_LOCAL_PATH local_path; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Set the local path to /B/B2. */ + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /B. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_B_short[j]) + { + directory_name_path_short[index++] = directory_name_B_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /B/B2. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_B_B2_short[j]) + { + directory_name_path_short[index++] = directory_name_B_B2_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /B/B2. */ +#ifndef FX_STANDALONE_ENABLE + fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + directory_name_B_B2_B22_length = fx_unicode_length_get(directory_name_B_B2_B22); + directory_name_B_B2_B23_length = fx_unicode_length_get(directory_name_B_B2_B23); + + /* Rename "/B/B2/B22" to "/B/B2/B23". */ + fx_unicode_directory_rename(&ram_disk, directory_name_B_B2_B22, directory_name_B_B2_B22_length, + directory_name_B_B2_B23, directory_name_B_B2_B23_length, directory_name_B_B2_B23_short); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the Directory write operation after record the redo log. */ + if ((sector_type == FX_DIRECTORY_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + directory_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} + +/* Define the FAT12, 16 and 32 directory traversal routine for the local path test. */ +static void traverse_directory(CHAR *directory_name) +{ + +FX_LOCAL_PATH local_path; +FX_FILE file; +UINT status; +CHAR name[300]; +UINT skip; + + + /* Determine if we are at the start. */ + if (directory_name == FX_NULL) + { +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, "\\"); +#else + status = fx_directory_default_set(&ram_disk, "\\"); +#endif + skip = 0; + } + else + { +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name); +#else + status = fx_directory_default_set(&ram_disk, directory_name); +#endif + skip = 2; + } + + /* Get the first directory entry in the root path. */ + status = fx_directory_first_entry_find(&ram_disk, name); + + /* Loop through the directory entries for this path. */ + while (status == FX_SUCCESS) + { + + /* Compare with what is expected. */ + if (strcmp(name, expected_name[expected_index++])) + error_counter++; + + /* Determine if this name is a directory or a file. */ + status = fx_directory_name_test(&ram_disk, name); + + /* What is it? */ + if (status == FX_NOT_DIRECTORY) + { + + /* This is a file, open it to test its integrity. */ + status = fx_file_open(&ram_disk, &file, name, FX_OPEN_FOR_READ); + if (status != FX_SUCCESS) + error_counter++; + status = fx_file_close(&file); + if (status != FX_SUCCESS) + error_counter++; + + /* Pickup the next directory entry. */ + status = fx_directory_next_entry_find(&ram_disk, name); + } + else if (status == FX_SUCCESS) + { + + /* Skip the first two entries... */ + if (skip) + skip--; + else + { + /* Recursive call to traverse directory. */ + traverse_directory(name); + + /* Restore path. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_restore(&ram_disk, &local_path); +#endif + } + + status = fx_directory_next_entry_find(&ram_disk, name); + } + } + +#ifndef FX_STANDALONE_ENABLE + fx_directory_local_path_clear(&ram_disk); +#endif +} + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_unicode_directory_rename_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Unicode Dir Rename DIR Interrupt Test...N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_unicode_directory_rename_redo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_unicode_directory_rename_redo_log_interrupt_test.c new file mode 100644 index 0000000..fa79ec8 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_unicode_directory_rename_redo_log_interrupt_test.c @@ -0,0 +1,1036 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check redo log interrupt for fx_unicode_directory_rename(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create directories and sub-directories; +Step4: Traverse the directory; +Step5: Create new thread to rename the directory "/B/B2/B22" to "/B/B2/B23"; +Step6: Terminate the new thread to simulate poweroff when update the redo log. +Step7: Open the media; +Step8: Enable fault tolerant feature to recover the data(undo operation); +Step9: Traverse the directory; +Step10: Recreate directory "/B/B2/B22" and "/B/B2/B23"; +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_unicode_directory_rename_redo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static UCHAR *pointer; + +#define TEST_COUNT 3 + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_counter = 0; +static UINT redo_log_write_interrupt = FX_FALSE; +static UCHAR directory_name_A[] = {'A', 0, 0, 0}; +static UCHAR directory_name_B[] = {'B', 0, 0, 0}; +static UCHAR directory_name_C[] = {'C', 0, 0, 0}; +static UCHAR directory_name_A_A1[] = {'A', 0, '1', 0, 0, 0}; +static UCHAR directory_name_A_A2[] = {'A', 0, '2', 0, 0, 0}; +static UCHAR directory_name_A_A3[] = {'A', 0, '3', 0, 0, 0}; +static UCHAR directory_name_B_B1[] = {'B', 0, '1', 0, 0, 0}; +static UCHAR directory_name_B_B2[] = {'B', 0, '2', 0, 0, 0}; +static UCHAR directory_name_B_B3[] = {'B', 0, '3', 0, 0, 0}; +static UCHAR directory_name_C_C1[] = {'C', 0, '1', 0, 0, 0}; +static UCHAR directory_name_C_C2[] = {'C', 0, '2', 0, 0, 0}; +static UCHAR directory_name_C_C3[] = {'C', 0, '3', 0, 0, 0}; +static UCHAR directory_name_A_A1_A11[] = {'A', 0, '1', 0, '1', 0, 0, 0}; +static UCHAR directory_name_B_B2_B22[] = {'B', 0, '2', 0, '2', 0, 0, 0}; +static UCHAR directory_name_B_B2_B23[] = {'B', 0, '2', 0, '3', 0, 0, 0}; +static UCHAR directory_name_C_C3_C33[] = {'C', 0, '3', 0, '3', 0, 0, 0}; +static CHAR directory_name_A_short[20]; +static CHAR directory_name_B_short[20]; +static CHAR directory_name_C_short[20]; +static CHAR directory_name_A_A1_short[20]; +static CHAR directory_name_A_A2_short[20]; +static CHAR directory_name_A_A3_short[20]; +static CHAR directory_name_B_B1_short[20]; +static CHAR directory_name_B_B2_short[20]; +static CHAR directory_name_B_B3_short[20]; +static CHAR directory_name_C_C1_short[20]; +static CHAR directory_name_C_C2_short[20]; +static CHAR directory_name_C_C3_short[20]; +static CHAR directory_name_A_A1_A11_short[20]; +static CHAR directory_name_B_B2_B22_short[20]; +static CHAR directory_name_B_B2_B23_short[20]; +static CHAR directory_name_C_C3_C33_short[20]; +static CHAR directory_name_path_short[100]; + +static UINT expected_index = 0; +static CHAR **expected_name; +static CHAR * expected_name_0[] = { + + "A", + ".", + "..", + "A1", + ".", + "..", + "A11", + ".", + "..", + "A2", + ".", + "..", + "A3", + ".", + "..", + "B", + ".", + "..", + "B1", + ".", + "..", + "B2", + ".", + "..", + "B22", + ".", + "..", + "B3", + ".", + "..", + "C", + ".", + "..", + "C1", + ".", + "..", + "C2", + ".", + "..", + "C3", + ".", + "..", + "C33", + ".", + "..", + "END"}; + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +static void traverse_directory(CHAR *directory_name); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_unicode_directory_rename_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Create the main thread. */ + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT i; +UINT j; +UINT index; +ULONG length; +FX_LOCAL_PATH local_path; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Unicode Dir Rename Redo Interrupt Test.."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Set the local path to "\\". */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, "\\"); +#else + status = fx_directory_default_set(&ram_disk, "\\"); +#endif + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Create a series of directories..."/A", "/B", "/C" */ + length = fx_unicode_length_get(directory_name_A); + status = fx_unicode_directory_create(&ram_disk, directory_name_A, length, directory_name_A_short); + length = fx_unicode_length_get(directory_name_B); + status += fx_unicode_directory_create(&ram_disk, directory_name_B, length, directory_name_B_short); + length = fx_unicode_length_get(directory_name_C); + status += fx_unicode_directory_create(&ram_disk, directory_name_C, length, directory_name_C_short); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(5); + } + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_A_short[j]) + { + directory_name_path_short[index++] = directory_name_A_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /A. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(6); + } + + /* Create the next level of sub-directories. + "/A/A1", "/A/A2", "/A/A3", */ + length = fx_unicode_length_get(directory_name_A_A1); + status = fx_unicode_directory_create(&ram_disk, directory_name_A_A1, length, directory_name_A_A1_short); + length = fx_unicode_length_get(directory_name_A_A2); + status += fx_unicode_directory_create(&ram_disk, directory_name_A_A2, length, directory_name_A_A2_short); + length = fx_unicode_length_get(directory_name_A_A3); + status += fx_unicode_directory_create(&ram_disk, directory_name_A_A3, length, directory_name_A_A3_short); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(7); + } + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_B_short[j]) + { + directory_name_path_short[index++] = directory_name_B_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /B. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(8); + } + + /* Create the next level of sub-directories. + "/B/B1", "/B/B2", "/B/B3", */ + length = fx_unicode_length_get(directory_name_B_B1); + status = fx_unicode_directory_create(&ram_disk, directory_name_B_B1, length, directory_name_B_B1_short); + length = fx_unicode_length_get(directory_name_B_B2); + status += fx_unicode_directory_create(&ram_disk, directory_name_B_B2, length, directory_name_B_B2_short); + length = fx_unicode_length_get(directory_name_B_B3); + status += fx_unicode_directory_create(&ram_disk, directory_name_B_B3, length, directory_name_B_B3_short); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(9); + } + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_C_short[j]) + { + directory_name_path_short[index++] = directory_name_C_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /C. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(10); + } + + /* Create the next level of sub-directories. + "/C/C1", "/C/C2", "/C/C1" */ + length = fx_unicode_length_get(directory_name_C_C1); + status = fx_unicode_directory_create(&ram_disk, directory_name_C_C1, length, directory_name_C_C1_short); + length = fx_unicode_length_get(directory_name_C_C2); + status = fx_unicode_directory_create(&ram_disk, directory_name_C_C2, length, directory_name_C_C2_short); + length = fx_unicode_length_get(directory_name_C_C3); + status = fx_unicode_directory_create(&ram_disk, directory_name_C_C3, length, directory_name_C_C3_short); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(11); + } + + /* Now create the third level of sub-directories... + "/A/A1/A11", "/B/B2/B22", "/C/C3/C33" */ + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /A. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_A_short[j]) + { + directory_name_path_short[index++] = directory_name_A_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /A/A1. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_A_A1_short[j]) + { + directory_name_path_short[index++] = directory_name_A_A1_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /A/A1. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + length = fx_unicode_length_get(directory_name_A_A1_A11); + status += fx_unicode_directory_create(&ram_disk, directory_name_A_A1_A11, length, directory_name_A_A1_A11_short); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(12); + } + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /B. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_B_short[j]) + { + directory_name_path_short[index++] = directory_name_B_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /B/B2. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_B_B2_short[j]) + { + directory_name_path_short[index++] = directory_name_B_B2_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /B/B2. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + length = fx_unicode_length_get(directory_name_B_B2_B22); + status += fx_unicode_directory_create(&ram_disk, directory_name_B_B2_B22, length, directory_name_B_B2_B22_short); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(13); + } + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /C. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_C_short[j]) + { + directory_name_path_short[index++] = directory_name_C_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /C/C3. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_C_C3_short[j]) + { + directory_name_path_short[index++] = directory_name_C_C3_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /C/C3. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + length = fx_unicode_length_get(directory_name_C_C3_C33); + status += fx_unicode_directory_create(&ram_disk, directory_name_C_C3_C33, length, directory_name_C_C3_C33_short); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating sub-sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(14); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + + /* Traverse the directory structure recursively. */ + expected_name = expected_name_0; + traverse_directory(FX_NULL); + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* redo_log_write_interrupt */ + redo_log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(15); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Set the index as zero before traverse. */ + expected_index = 0; + + /* Traverse the directory structure recursively. */ + expected_name = expected_name_0; + traverse_directory(FX_NULL); + + /* Set the local path to /B/B2. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + status = fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + + /* Attempt to create the same directory again "/B/B2/B22". */ + length = fx_unicode_length_get(directory_name_B_B2_B22); + status = fx_unicode_directory_create(&ram_disk, directory_name_B_B2_B22, length, directory_name_B_B2_B22_short); + + /* Check for errors... */ + if (status != FX_ALREADY_CREATED) + { + + /* Error creating same directory twice. Return to caller. */ + printf("ERROR!\n"); + test_control_return(17); + } + + /* Attempt to create the same directory again "/B/B2/B23". */ + length = fx_unicode_length_get(directory_name_B_B2_B23); + status = fx_unicode_directory_create(&ram_disk, directory_name_B_B2_B23, length, directory_name_B_B2_B23_short); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating same directory twice. Return to caller. */ + printf("ERROR!\n"); + test_control_return(18); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (redo_log_write_interrupt != FX_TRUE) || (error_counter)) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + +UINT index; +UINT j; +ULONG directory_name_B_B2_B22_length; +ULONG directory_name_B_B2_B23_length; +FX_LOCAL_PATH local_path; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Set the local path to /B/B2. */ + + /* Initailize the directory structure. */ + index = 0; + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /B. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_B_short[j]) + { + directory_name_path_short[index++] = directory_name_B_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index++] = '/'; + + /* Construct the directory structure /B/B2. */ + for(j = 0; j < 20; j ++) + { + + /* Check the value. */ + if (directory_name_B_B2_short[j]) + { + directory_name_path_short[index++] = directory_name_B_B2_short[j]; + } + else + { + break; + } + } + + directory_name_path_short[index] = FX_NULL; + + /* Set the local path to /B/B2. */ +#ifndef FX_STANDALONE_ENABLE + fx_directory_local_path_set(&ram_disk, &local_path, directory_name_path_short); +#else + fx_directory_default_set(&ram_disk, directory_name_path_short); +#endif + directory_name_B_B2_B22_length = fx_unicode_length_get(directory_name_B_B2_B22); + directory_name_B_B2_B23_length = fx_unicode_length_get(directory_name_B_B2_B23); + + /* Rename "/B/B2/B22" to "/B/B2/B23". */ + fx_unicode_directory_rename(&ram_disk, directory_name_B_B2_B22, directory_name_B_B2_B22_length, + directory_name_B_B2_B23, directory_name_B_B2_B23_length, directory_name_B_B2_B23_short); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the redo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + redo_log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} + +/* Define the FAT12, 16 and 32 directory traversal routine for the local path test. */ +static void traverse_directory(CHAR *directory_name) +{ + +FX_LOCAL_PATH local_path; +FX_FILE file; +UINT status; +CHAR name[300]; +UINT skip; + + + /* Determine if we are at the start. */ + if (directory_name == FX_NULL) + { +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, "\\"); +#else + status = fx_directory_default_set(&ram_disk, "\\"); +#endif + skip = 0; + } + else + { +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, directory_name); +#else + status = fx_directory_default_set(&ram_disk, directory_name); +#endif + skip = 2; + } + + /* Get the first directory entry in the root path. */ + status = fx_directory_first_entry_find(&ram_disk, name); + + /* Loop through the directory entries for this path. */ + while (status == FX_SUCCESS) + { + + /* Compare with what is expected. */ + if (strcmp(name, expected_name[expected_index++])) + error_counter++; + + /* Determine if this name is a directory or a file. */ + status = fx_directory_name_test(&ram_disk, name); + + /* What is it? */ + if (status == FX_NOT_DIRECTORY) + { + + /* This is a file, open it to test its integrity. */ + status = fx_file_open(&ram_disk, &file, name, FX_OPEN_FOR_READ); + if (status != FX_SUCCESS) + error_counter++; + status = fx_file_close(&file); + if (status != FX_SUCCESS) + error_counter++; + + /* Pickup the next directory entry. */ + status = fx_directory_next_entry_find(&ram_disk, name); + } + else if (status == FX_SUCCESS) + { + + /* Skip the first two entries... */ + if (skip) + skip--; + else + { + /* Recursive call to traverse directory. */ + traverse_directory(name); + + /* Restore path. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_restore(&ram_disk, &local_path); +#endif + } + + status = fx_directory_next_entry_find(&ram_disk, name); + } + } + +#ifndef FX_STANDALONE_ENABLE + fx_directory_local_path_clear(&ram_disk); +#endif +} + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_unicode_directory_rename_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Unicode Dir Rename Redo Interrupt Test..N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_unicode_file_create_directory_interrupt_test.c b/test/regression_test/filex_fault_tolerant_unicode_file_create_directory_interrupt_test.c new file mode 100644 index 0000000..1eeffbe --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_unicode_file_create_directory_interrupt_test.c @@ -0,0 +1,780 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check directory interrupt for fx_unicode_file_create() : +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 26 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to create TEST3.TXT; +Step7: Terminate the new thread to simulate poweroff when update directory. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(redo operation); +Step10: Check the test files. +*/ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_unicode_file_create_directory_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +#define TEST_COUNT 3 + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT directory_write_interrupt = FX_FALSE; +static CHAR read_buffer[1024]; +static UINT read_buffer_size = 1024; + + +static UCHAR file_name_TEST_TXT[] = {'T', 0, 'E', 0, 'S', 0, 'T', 0, '.', 'T', 0, 'X', 0, 'T', 0, 0, 0}; +static UCHAR file_name_TEST1_TXT[] = {'T', 0, 'E', 0, 'S', 0, 'T', 0, '1', 0, '.', 'T', 0, 'X', 0, 'T', 0, 0, 0}; +static UCHAR file_name_TEST2_TXT[] = {'T', 0, 'E', 0, 'S', 0, 'T', 0, '2', 0, '.', 'T', 0, 'X', 0, 'T', 0, 0, 0}; +static UCHAR file_name_TEST3_TXT[] = {'T', 0, 'E', 0, 'S', 0, 'T', 0, '3', 0, '.', 'T', 0, 'X', 0, 'T', 0, 0, 0}; +static CHAR file_name_TEST_TXT_short[20]; +static CHAR file_name_TEST1_TXT_short[20]; +static CHAR file_name_TEST2_TXT_short[20]; +static CHAR file_name_TEST3_TXT_short[20]; + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_unicode_file_create_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; +UINT length; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Unicode File Create DIR Interrupt Test.."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + length = fx_unicode_length_get(file_name_TEST_TXT); + status = fx_unicode_file_create(&ram_disk, file_name_TEST_TXT, length, file_name_TEST_TXT_short); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + length = fx_unicode_length_get(file_name_TEST1_TXT); + status = fx_unicode_file_create(&ram_disk, file_name_TEST1_TXT, length, file_name_TEST1_TXT_short); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST1_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " 012345678901234567890123\n", 26); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + length = fx_unicode_length_get(file_name_TEST2_TXT); + status = fx_unicode_file_create(&ram_disk, file_name_TEST2_TXT, length, file_name_TEST2_TXT_short); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST2_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + directory_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST1_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST2_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST3_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(37); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_END_OF_FILE) || (actual != 0)) + { + + printf("ERROR!\n"); + test_control_return(38); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(39); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (directory_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(40); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + UINT length; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Create a file called TEST3.TXT in the root directory. */ + length = fx_unicode_length_get(file_name_TEST3_TXT); + fx_unicode_file_create(&ram_disk, file_name_TEST3_TXT, length, file_name_TEST3_TXT_short); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the Directory write operation after record the redo log. */ + if ((sector_type == FX_DIRECTORY_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + directory_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_unicode_file_create_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Unicode File Create DIR Interrupt Test..N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_unicode_file_create_redo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_unicode_file_create_redo_log_interrupt_test.c new file mode 100644 index 0000000..25126db --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_unicode_file_create_redo_log_interrupt_test.c @@ -0,0 +1,759 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check redo log interrupt for fx_unicode_file_create() : +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 26 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to create TEST3.TXT; +Step7: Terminate the new thread to simulate poweroff when update redo log. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(undo operation); +Step10: Check the test files. +*/ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_unicode_file_create_redo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +#define TEST_COUNT 3 + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT redo_log_write_interrupt = FX_FALSE; +static CHAR read_buffer[1024]; +static UINT read_buffer_size = 1024; + + +static UCHAR file_name_TEST_TXT[] = {'T', 0, 'E', 0, 'S', 0, 'T', 0, '.', 'T', 0, 'X', 0, 'T', 0, 0, 0}; +static UCHAR file_name_TEST1_TXT[] = {'T', 0, 'E', 0, 'S', 0, 'T', 0, '1', 0, '.', 'T', 0, 'X', 0, 'T', 0, 0, 0}; +static UCHAR file_name_TEST2_TXT[] = {'T', 0, 'E', 0, 'S', 0, 'T', 0, '2', 0, '.', 'T', 0, 'X', 0, 'T', 0, 0, 0}; +static UCHAR file_name_TEST3_TXT[] = {'T', 0, 'E', 0, 'S', 0, 'T', 0, '3', 0, '.', 'T', 0, 'X', 0, 'T', 0, 0, 0}; +static CHAR file_name_TEST_TXT_short[20]; +static CHAR file_name_TEST1_TXT_short[20]; +static CHAR file_name_TEST2_TXT_short[20]; +static CHAR file_name_TEST3_TXT_short[20]; + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_unicode_file_create_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; +UINT length; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Unicode File Create Redo Interrupt Test."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + length = fx_unicode_length_get(file_name_TEST_TXT); + status = fx_unicode_file_create(&ram_disk, file_name_TEST_TXT, length, file_name_TEST_TXT_short); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + length = fx_unicode_length_get(file_name_TEST1_TXT); + status = fx_unicode_file_create(&ram_disk, file_name_TEST1_TXT, length, file_name_TEST1_TXT_short); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST1_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " 012345678901234567890123\n", 26); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + length = fx_unicode_length_get(file_name_TEST2_TXT); + status = fx_unicode_file_create(&ram_disk, file_name_TEST2_TXT, length, file_name_TEST2_TXT_short); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST2_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* redo_log_write_interrupt */ + redo_log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST1_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST2_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST3_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status == FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Create a file called TEST3.TXT in the root directory. */ + length = fx_unicode_length_get(file_name_TEST3_TXT); + status = fx_unicode_file_create(&ram_disk, file_name_TEST3_TXT, length, file_name_TEST3_TXT_short); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(37); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (redo_log_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(38); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + UINT length; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Create a file called TEST3.TXT in the root directory. */ + length = fx_unicode_length_get(file_name_TEST3_TXT); + fx_unicode_file_create(&ram_disk, file_name_TEST3_TXT, length, file_name_TEST3_TXT_short); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the redo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + redo_log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_unicode_file_create_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Unicode File Create Redo Interrupt Test.N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_unicode_file_rename_directory_interrupt_test.c b/test/regression_test/filex_fault_tolerant_unicode_file_rename_directory_interrupt_test.c new file mode 100644 index 0000000..0031739 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_unicode_file_rename_directory_interrupt_test.c @@ -0,0 +1,768 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check directory interrupt for fx_unicode_file_rename(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 26 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to rename TEST2.TXT to TEST3.TXT; +Step7: Terminate the new thread to simulate poweroff when update the directory after record the redo log. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(redo operation); +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_unicode_file_rename_directory_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + +#define TEST_COUNT 3 + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT directory_write_interrupt = FX_FALSE; +static CHAR read_buffer[1024]; +static UINT read_buffer_size = 1024; + +static UCHAR file_name_TEST_TXT[] = {'T', 0, 'E', 0, 'S', 0, 'T', 0, '.', 'T', 0, 'X', 0, 'T', 0, 0, 0}; +static UCHAR file_name_TEST1_TXT[] = {'T', 0, 'E', 0, 'S', 0, 'T', 0, '1', 0, '.', 'T', 0, 'X', 0, 'T', 0, 0, 0}; +static UCHAR file_name_TEST2_TXT[] = {'T', 0, 'E', 0, 'S', 0, 'T', 0, '2', 0, '.', 'T', 0, 'X', 0, 'T', 0, 0, 0}; +static UCHAR file_name_TEST3_TXT[] = {'T', 0, 'E', 0, 'S', 0, 'T', 0, '3', 0, '.', 'T', 0, 'X', 0, 'T', 0, 0, 0}; +static CHAR file_name_TEST_TXT_short[20]; +static CHAR file_name_TEST1_TXT_short[20]; +static CHAR file_name_TEST2_TXT_short[20]; +static CHAR file_name_TEST3_TXT_short[20]; + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_unicode_file_rename_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; +UINT length; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Unicode Rename DIR Interrupt Test.."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + length = fx_unicode_length_get(file_name_TEST_TXT); + status = fx_unicode_file_create(&ram_disk, file_name_TEST_TXT, length, file_name_TEST_TXT_short); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + length = fx_unicode_length_get(file_name_TEST1_TXT); + status = fx_unicode_file_create(&ram_disk, file_name_TEST1_TXT, length, file_name_TEST1_TXT_short); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST1_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " 012345678901234567890123\n", 26); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + length = fx_unicode_length_get(file_name_TEST2_TXT); + status = fx_unicode_file_create(&ram_disk, file_name_TEST2_TXT, length, file_name_TEST2_TXT_short); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST2_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + directory_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file "TEST.TXT". */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file "TEST1.TXT". */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST1_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Shortname should be same, file_name_TEST2_TXT_short should be same as file_name_TEST3_TXT_short. */ + if (strcmp(file_name_TEST2_TXT_short, file_name_TEST3_TXT_short)) + { + printf("ERROR!\n"); + test_control_return(32); + } + + /* Open the test file "TEST2.TXT". */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST2_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Create a file called TEST2.TXT in the root directory again. */ + length = fx_unicode_length_get(file_name_TEST2_TXT); + status = fx_unicode_file_create(&ram_disk, file_name_TEST2_TXT, length, file_name_TEST2_TXT_short); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(37); + } + + /* Create a file called TEST3.TXT in the root directory again. */ + length = fx_unicode_length_get(file_name_TEST3_TXT); + status = fx_unicode_file_create(&ram_disk, file_name_TEST3_TXT, length, file_name_TEST3_TXT_short); + + /* Check the create status. */ + if (status != FX_ALREADY_CREATED) + { + + printf("ERROR!\n"); + test_control_return(38); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (directory_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(39); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + UINT test2_length; + UINT test3_length; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write Direcotry entry. */ + driver_write_callback = my_driver_write; + + /* Rename the file name from "TEST2.TXT" to "TEST3.TXT". */ + test2_length = fx_unicode_length_get(file_name_TEST2_TXT); + test3_length = fx_unicode_length_get(file_name_TEST3_TXT); + fx_unicode_file_rename(&ram_disk, file_name_TEST2_TXT, test2_length, file_name_TEST3_TXT, test3_length, file_name_TEST3_TXT_short); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the directory write operation after record the redo log. */ + if ((sector_type == FX_DIRECTORY_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + directory_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_unicode_file_rename_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Unicode Rename DIR Interrupt Test..N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_unicode_file_rename_redo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_unicode_file_rename_redo_log_interrupt_test.c new file mode 100644 index 0000000..e596fc6 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_unicode_file_rename_redo_log_interrupt_test.c @@ -0,0 +1,768 @@ +/* This FileX test concentrates on the Fault-Tolerant directory write interrupt operation. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; + +Check redo log interrupt for fx_unicode_file_rename(): +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create and write 28 bytes into TEST.TXT; +Step4: Create and write 26 bytes into TEST1.TXT; +Step5: Create and write 19 bytes into TEST2.TXT; +Step6: Create new thread to rename TEST2.TXT to TEST3.TXT; +Step7: Terminate the new thread to simulate poweroff when update redo log. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(undo operation); +Step10: Check the test files. +*/ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_unicode_file_rename_redo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT_DATA) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + + +#define TEST_COUNT 3 + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static UINT redo_log_write_interrupt = FX_FALSE; +static CHAR read_buffer[1024]; +static UINT read_buffer_size = 1024; + +static UCHAR file_name_TEST_TXT[] = {'T', 0, 'E', 0, 'S', 0, 'T', 0, '.', 'T', 0, 'X', 0, 'T', 0, 0, 0}; +static UCHAR file_name_TEST1_TXT[] = {'T', 0, 'E', 0, 'S', 0, 'T', 0, '1', 0, '.', 'T', 0, 'X', 0, 'T', 0, 0, 0}; +static UCHAR file_name_TEST2_TXT[] = {'T', 0, 'E', 0, 'S', 0, 'T', 0, '2', 0, '.', 'T', 0, 'X', 0, 'T', 0, 0, 0}; +static UCHAR file_name_TEST3_TXT[] = {'T', 0, 'E', 0, 'S', 0, 'T', 0, '3', 0, '.', 'T', 0, 'X', 0, 'T', 0, 0, 0}; +static CHAR file_name_TEST_TXT_short[20]; +static CHAR file_name_TEST1_TXT_short[20]; +static CHAR file_name_TEST2_TXT_short[20]; +static CHAR file_name_TEST3_TXT_short[20]; + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_unicode_file_rename_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UINT i; +UINT length; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Unicode Rename Redo Interrupt Test."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + length = fx_unicode_length_get(file_name_TEST_TXT); + status = fx_unicode_file_create(&ram_disk, file_name_TEST_TXT, length, file_name_TEST_TXT_short); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST1.TXT in the root directory. */ + length = fx_unicode_length_get(file_name_TEST1_TXT); + status = fx_unicode_file_create(&ram_disk, file_name_TEST1_TXT, length, file_name_TEST1_TXT_short); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST1_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " 012345678901234567890123\n", 26); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a file called TEST2.TXT in the root directory. */ + length = fx_unicode_length_get(file_name_TEST2_TXT); + status = fx_unicode_file_create(&ram_disk, file_name_TEST2_TXT, length, file_name_TEST2_TXT_short); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST2_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " EXPRESSLOGIC_TEST\n", 19); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* redo_log_write_interrupt */ + redo_log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* After write interrupt, reread the files. */ + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Enable the Fault-tolerant feature to recover the media. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file "TEST.TXT". */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the test file "TEST1.TXT". */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST1_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 26)) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Shortname should be same, file_name_TEST2_TXT_short should be same as file_name_TEST3_TXT_short. */ + if (strcmp(file_name_TEST2_TXT_short, file_name_TEST3_TXT_short)) + { + printf("ERROR!\n"); + test_control_return(32); + } + + /* Open the test file "TEST2.TXT". */ + status = fx_file_open(&ram_disk, &my_file, file_name_TEST2_TXT_short, FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Read the bytes of the test file. */ + status = fx_file_read(&my_file, read_buffer, read_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 19)) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Create a file called TEST2.TXT in the root directory again. */ + length = fx_unicode_length_get(file_name_TEST2_TXT); + status = fx_unicode_file_create(&ram_disk, file_name_TEST2_TXT, length, file_name_TEST2_TXT_short); + + /* Check the create status. */ + if (status != FX_ALREADY_CREATED) + { + + printf("ERROR!\n"); + test_control_return(37); + } + + /* Create a file called TEST3.TXT in the root directory again. */ + length = fx_unicode_length_get(file_name_TEST3_TXT); + status = fx_unicode_file_create(&ram_disk, file_name_TEST3_TXT, length, file_name_TEST3_TXT_short); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(38); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (redo_log_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(39); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + UINT test2_length; + UINT test3_length; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write Direcotry entry. */ + driver_write_callback = my_driver_write; + + /* Rename the file name from "TEST2.TXT" to "TEST3.TXT". */ + test2_length = fx_unicode_length_get(file_name_TEST2_TXT); + test3_length = fx_unicode_length_get(file_name_TEST3_TXT); + fx_unicode_file_rename(&ram_disk, file_name_TEST2_TXT, test2_length, file_name_TEST3_TXT, test3_length, file_name_TEST3_TXT_short); +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the redo log write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + redo_log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_unicode_file_rename_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant File Unicode Rename Redo Interrupt Test.N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_fault_tolerant_write_large_data_directory_interrupt_test.c b/test/regression_test/filex_fault_tolerant_write_large_data_directory_interrupt_test.c new file mode 100644 index 0000000..9779c1d --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_write_large_data_directory_interrupt_test.c @@ -0,0 +1,498 @@ +/* This FileX test concentrates on the Fault-Tolerant write large data directory interrupt test. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; +Check write large data fat interrupt operation: +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create new file called "TEST.TXT"; +Step4: Get the media available bytes; +Step5: Write large bytes out of the file to fill the media +Step7: Terminate the new thread to simulate poweroff when write the directory after record the redo log. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(redo operation); +Step10: Check the test files. +*/ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_write_large_data_directory_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static ULONG write_data_size; +static ULONG available_bytes; +static UINT directory_write_interrupt = FX_FALSE; +static ULONG i, j; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void *ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_write_large_data_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +ULONG data_value; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Write Large Data Dir Interrupt test....."); + + /* Generate a random number for write data. */ + srand((ULONG)time(NULL)); + data_value = (ULONG)(rand() & 0xFF) | (ULONG)((rand() & 0xFF) << 8) | (ULONG)((rand() & 0xFF) << 16) | (ULONG)((rand() & 0xFF) << 24); + + /* Genearte the write data. */ + for (j = 0; j < large_data_buffer_size / sizeof(ULONG); j ++) + { + ((ULONG*)large_data_buffer)[j] = data_value++; + } + + /* Roll back the value for later verification use. */ + data_value -= (large_data_buffer_size / sizeof(ULONG)); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 60000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 400000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Pickup the available bytes in the media. */ + status = fx_media_space_available(&ram_disk, &available_bytes); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (available_bytes < sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + write_data_size = available_bytes; + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + directory_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(8); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Pickup the available bytes in the media again. */ + status = fx_media_space_available(&ram_disk, &j); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (j != 0)) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Now read in all the bytes again to make sure the file contents are really there. */ + status = fx_file_read(&my_file, (void *) large_data_buffer, large_data_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != write_data_size)) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (directory_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Write the data to fill the media one time. */ + status = fx_file_write(&my_file, (void *) large_data_buffer, write_data_size); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the Direcotry write operation after record the redo log. */ + if ((sector_type == FX_DIRECTORY_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + directory_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_write_large_data_directory_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Write Large Data Dir Interrupt test.....N/A\n"); + + test_control_return(255); +} +#endif diff --git a/test/regression_test/filex_fault_tolerant_write_large_data_fat_chain_cleanup_interrupt_test.c b/test/regression_test/filex_fault_tolerant_write_large_data_fat_chain_cleanup_interrupt_test.c new file mode 100644 index 0000000..2f0e5f3 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_write_large_data_fat_chain_cleanup_interrupt_test.c @@ -0,0 +1,524 @@ +/* This FileX test concentrates on the Fault-Tolerant write large data fat interrupt test. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; +Check write large data fat interrupt operation: +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create new file called "TEST.TXT"; +Step4: Get the media available bytes; +Step5: Write large bytes out of the file to fill the media +Step7: Terminate the new thread to simulate poweroff when cleanup the fat chain after redo log generated. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(redo operation); +Step10: Check the test files. +*/ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_write_large_data_fat_chain_cleanup_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static ULONG write_data_size; +static ULONG available_bytes; +static UINT fat_chain_cleanup_interrupt = FX_FALSE; +static ULONG i, j; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_write_large_data_fat_chain_cleanup_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +ULONG data_value; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Write Large Data Fat Chain Cleanup Test."); + + /* Generate a random number for write data. */ + srand((ULONG)time(NULL)); + data_value = (ULONG)(rand() & 0xFF) | (ULONG)((rand() & 0xFF) << 8) | (ULONG)((rand() & 0xFF) << 16) | (ULONG)((rand() & 0xFF) << 24); + + /* Genearte the write data. */ + for (j = 0; j < large_data_buffer_size / sizeof(ULONG); j ++) + { + ((ULONG*)large_data_buffer)[j] = data_value++; + } + + /* Roll back the value for later verification use. */ + data_value -= (large_data_buffer_size / sizeof(ULONG)); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 60000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 400000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write 2500 data to test file, take up two cluster. */ + status = fx_file_write(&my_file, (void *) large_data_buffer, 2500); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(5); + } + + /* Pickup the available bytes in the media. */ + status = fx_media_space_available(&ram_disk, &available_bytes); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (available_bytes < sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + write_data_size = available_bytes; + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + fat_chain_cleanup_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(8); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Pickup the available bytes in the media again. */ + status = fx_media_space_available(&ram_disk, &j); + { + + /* Check for available bytes error, two cluster. */ + if ((status != FX_SUCCESS) || (j != 2048 * 2)) + { + + printf("ERROR!\n"); + test_control_return(10); + } + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Now read in all the bytes again to make sure the file contents are really there. */ + status = fx_file_read(&my_file, (void *) large_data_buffer, large_data_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != write_data_size)) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (fat_chain_cleanup_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } + + /* Write the data to fill the media one time. */ + status = fx_file_write(&my_file, (void *) large_data_buffer, write_data_size); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the FAT chain cleanup operation after record the redo log. */ + if ((sector_type == FX_FAT_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + fat_chain_cleanup_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_write_large_data_fat_chain_cleanup_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Write Large Data Fat Chain Cleanup Test.N/A\n"); + + test_control_return(255); +} +#endif diff --git a/test/regression_test/filex_fault_tolerant_write_large_data_fat_interrupt_test.c b/test/regression_test/filex_fault_tolerant_write_large_data_fat_interrupt_test.c new file mode 100644 index 0000000..464aabe --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_write_large_data_fat_interrupt_test.c @@ -0,0 +1,523 @@ +/* This FileX test concentrates on the Fault-Tolerant write large data fat interrupt test. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; +Check write large data fat interrupt operation: +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create new file called "TEST.TXT"; +Step4: Get the media available bytes; +Step5: Write large bytes out of the file to fill the media +Step7: Terminate the new thread to simulate poweroff when update the fat between undo log generated and redo log not generated. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(undo operation); +Step10: Check the test files. +*/ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_write_large_data_fat_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif + +static UINT error_couter = 0; +static ULONG write_data_size; +static ULONG available_bytes; +static UINT fat_write_interrupt = FX_FALSE; +static ULONG i, j; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_write_large_data_fat_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +ULONG data_value; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Write Large Data Fat Interrupt test....."); + + /* Generate a random number for write data. */ + srand((ULONG)time(NULL)); + data_value = (ULONG)(rand() & 0xFF) | (ULONG)((rand() & 0xFF) << 8) | (ULONG)((rand() & 0xFF) << 16) | (ULONG)((rand() & 0xFF) << 24); + + /* Genearte the write data. */ + for (j = 0; j < large_data_buffer_size / sizeof(ULONG); j ++) + { + ((ULONG*)large_data_buffer)[j] = data_value++; + } + + /* Roll back the value for later verification use. */ + data_value -= (large_data_buffer_size / sizeof(ULONG)); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 60000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 400000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Pickup the available bytes in the media. */ + status = fx_media_space_available(&ram_disk, &available_bytes); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (available_bytes < sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + write_data_size = available_bytes; + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* directory_write_interrupt */ + fat_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(8); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Pickup the available bytes in the media again. */ + status = fx_media_space_available(&ram_disk, &j); + + /* Check for available bytes error. */ + if (i != 3) + { + if ((status != FX_SUCCESS) || (j != available_bytes)) + { + + printf("ERROR!\n"); + test_control_return(10); + } + } + else + { + if ((status != FX_SUCCESS) || (j != 0)) + { + + printf("ERROR!\n"); + test_control_return(10); + } + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Now read in all the bytes again to make sure the file contents are really there. */ + status = fx_file_read(&my_file, (void *) large_data_buffer, large_data_buffer_size, &actual); + + /* Check the file read status. */ + if (i != 3) + { + if ((status != FX_END_OF_FILE) || (actual != 0)) + { + + printf("ERROR!\n"); + test_control_return(13); + } + } + else + { + if ((status != FX_SUCCESS) || (actual != write_data_size)) + { + + printf("ERROR!\n"); + test_control_return(13); + } + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (fat_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Write the data to fill the media one time. */ + status = fx_file_write(&my_file, (void *) large_data_buffer, write_data_size); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the FAT write operation after record the undo log. */ + if ((sector_type == FX_FAT_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_UNDO_DONE)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + fat_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_write_large_data_fat_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Write Large Data Fat Interrupt test.....N/A\n"); + + test_control_return(255); +} +#endif diff --git a/test/regression_test/filex_fault_tolerant_write_large_data_interrupt_test.c b/test/regression_test/filex_fault_tolerant_write_large_data_interrupt_test.c new file mode 100644 index 0000000..52a7f53 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_write_large_data_interrupt_test.c @@ -0,0 +1,495 @@ +/* This FileX test concentrates on the Fault-Tolerant write large data interrupt test. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; +Check write large data fat interrupt operation: +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create new file called "TEST.TXT"; +Step4: Get the media available bytes; +Step5: Write large bytes out of the file to fill the media +Step7: Terminate the new thread to simulate poweroff when update the data between undo log generated and redo log not generated. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(undo operation); +Step10: Check the test files. +*/ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_write_large_data_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static ULONG write_data_size; +static ULONG available_bytes; +static UINT data_write_interrupt = FX_FALSE; +static ULONG i, j; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_write_large_data_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +ULONG data_value; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Write Large Data Interrupt test........."); + + /* Generate a random number for write data. */ + srand((ULONG)time(NULL)); + data_value = (ULONG)(rand() & 0xFF) | (ULONG)((rand() & 0xFF) << 8) | (ULONG)((rand() & 0xFF) << 16) | (ULONG)((rand() & 0xFF) << 24); + + /* Genearte the write data. */ + for (j = 0; j < large_data_buffer_size / sizeof(ULONG); j ++) + { + ((ULONG*)large_data_buffer)[j] = data_value++; + } + + /* Roll back the value for later verification use. */ + data_value -= (large_data_buffer_size / sizeof(ULONG)); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 60000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 400000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Pickup the available bytes in the media. */ + status = fx_media_space_available(&ram_disk, &available_bytes); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (available_bytes < sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + write_data_size = available_bytes; + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* data_write_interrupt */ + data_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(8); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Pickup the available bytes in the media again. */ + status = fx_media_space_available(&ram_disk, &j); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (j != available_bytes)) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Now read in all the bytes again to make sure the file contents are really there. */ + status = fx_file_read(&my_file, (void *) large_data_buffer, large_data_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_END_OF_FILE) || (actual != 0)) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (data_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + UINT status; + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Write the data to fill the media one time. */ + status = fx_file_write(&my_file, (void *) large_data_buffer, write_data_size); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the data write operation after record the undo log. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_UNDO_DONE) && + (!(_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_DONE))) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + data_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_write_large_data_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Write Large Data Interrupt test.........N/A\n"); + + test_control_return(255); +} +#endif diff --git a/test/regression_test/filex_fault_tolerant_write_large_data_redo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_write_large_data_redo_log_interrupt_test.c new file mode 100644 index 0000000..4a6041f --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_write_large_data_redo_log_interrupt_test.c @@ -0,0 +1,499 @@ +/* This FileX test concentrates on the Fault-Tolerant write large data redo log interrupt test. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; +Check write large data fat interrupt operation: +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create new file called "TEST.TXT"; +Step4: Get the media available bytes; +Step5: Write large bytes out of the file to fill the media +Step7: Terminate the new thread to simulate poweroff when update the fat redo log. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(undo operation); +Step10: Check the test files. +*/ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_write_large_data_redo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static ULONG write_data_size; +static ULONG available_bytes; +static UINT redo_log_write_interrupt = FX_FALSE; +static ULONG i, j; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_write_large_data_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +ULONG data_value; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Write Large Data Redo Log Interrupt test."); + + /* Generate a random number for write data. */ + srand((ULONG)time(NULL)); + data_value = (ULONG)(rand() & 0xFF) | (ULONG)((rand() & 0xFF) << 8) | (ULONG)((rand() & 0xFF) << 16) | (ULONG)((rand() & 0xFF) << 24); + + /* Genearte the write data. */ + for (j = 0; j < large_data_buffer_size / sizeof(ULONG); j ++) + { + ((ULONG*)large_data_buffer)[j] = data_value++; + } + + /* Roll back the value for later verification use. */ + data_value -= (large_data_buffer_size / sizeof(ULONG)); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 60000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 400000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Pickup the available bytes in the media. */ + status = fx_media_space_available(&ram_disk, &available_bytes); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (available_bytes < sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + write_data_size = available_bytes; + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* redo_log_write_interrupt */ + redo_log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(8); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Pickup the available bytes in the media again. */ + status = fx_media_space_available(&ram_disk, &j); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (j != available_bytes)) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Now read in all the bytes again to make sure the file contents are really there. */ + status = fx_file_read(&my_file, (void *) large_data_buffer, large_data_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_END_OF_FILE) || (actual != 0)) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (redo_log_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Write the data to fill the media one time. */ + status = fx_file_write(&my_file, (void *) large_data_buffer, write_data_size); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the redo log write operation after record the undo log. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_REDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + redo_log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_write_large_data_redo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Write Large Data Redo Log Interrupt test.N/A\n"); + + test_control_return(255); +} +#endif diff --git a/test/regression_test/filex_fault_tolerant_write_large_data_test.c b/test/regression_test/filex_fault_tolerant_write_large_data_test.c new file mode 100644 index 0000000..ff9d31b --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_write_large_data_test.c @@ -0,0 +1,362 @@ +/* This FileX test concentrates on the Fault-Tolerant write large data test. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; +Check media full operation: +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create new file called "TEST.TXT"; +Step4: Get the media available bytes; +Step5: Write large bytes out of the file to fill the media; +Step6: Check the media available bytes. +Step7: Loop to read and check the file data from the media; +*/ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_fault_tolerant.h" +#include +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_write_large_data_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_write_large_data_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +ULONG available_bytes; +ULONG i, j; +ULONG data_value; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Write Large Data Test..................."); + + /* Generate a random number for write data. */ + srand((ULONG)time(NULL)); + data_value = (ULONG)(rand() & 0xFF) | (ULONG)((rand() & 0xFF) << 8) | (ULONG)((rand() & 0xFF) << 16) | (ULONG)((rand() & 0xFF) << 24); + + /* Genearte the write data. */ + for (j = 0; j < large_data_buffer_size / sizeof(ULONG); j ++) + { + ((ULONG*)large_data_buffer)[j] = data_value++; + } + + /* Roll back the value for later verification use. */ + data_value -= (large_data_buffer_size / sizeof(ULONG)); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 60000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 400000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Pickup the available bytes in the media. */ + status = fx_media_space_available(&ram_disk, &available_bytes); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (available_bytes < sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Write the data to fill the media one time. */ + status = fx_file_write(&my_file, (void *) large_data_buffer, available_bytes); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Pickup the available bytes in the media again. */ + status = fx_media_space_available(&ram_disk, &j); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (j != 0)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + +#ifndef FX_DISABLE_CACHE + /* At this point, we should invalidate the (which also flushes the cache) media to ensure that all + dirty sectors are written. */ + status = fx_media_cache_invalidate(&ram_disk); + + /* Check for flush errors. */ + if ((status != FX_SUCCESS) || (ram_disk.fx_media_sector_cache_dirty_count)) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* See if any sectors are still valid in the cache. */ + for (j = 0; j < ram_disk.fx_media_sector_cache_size; j++) + { + + /* Determine if this cache entry is still valid. */ + if (ram_disk.fx_media_sector_cache[j].fx_cached_sector_valid) + { + + printf("ERROR!\n"); + test_control_return(10); + } + } +#endif /* FX_DISABLE_CACHE */ + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Now read in all the bytes again to make sure the file contents are really there. */ + status = fx_file_read(&my_file, (void *) large_data_buffer, large_data_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != available_bytes)) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(14); + } + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_write_large_data_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Write Large Data Test...................N/A\n"); + + test_control_return(255); +} +#endif diff --git a/test/regression_test/filex_fault_tolerant_write_large_data_undo_log_interrupt_test.c b/test/regression_test/filex_fault_tolerant_write_large_data_undo_log_interrupt_test.c new file mode 100644 index 0000000..34428e9 --- /dev/null +++ b/test/regression_test/filex_fault_tolerant_write_large_data_undo_log_interrupt_test.c @@ -0,0 +1,498 @@ +/* This FileX test concentrates on the Fault-Tolerant write large data undo log interrupt test. */ +/* +For FAT 12, 16, 32, one cluster size is 1024 bytes; +Check write large data fat interrupt operation: +Step1: Format and open the media; +Step2: Enable fault tolerant feature; +Step3: Create new file called "TEST.TXT"; +Step4: Get the media available bytes; +Step5: Write large bytes out of the file to fill the media +Step7: Terminate the new thread to simulate poweroff when update the undo log. +Step8: Open the media; +Step9: Enable fault tolerant feature to recover the data(undo operation); +Step10: Check the test files. +*/ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#else +#define _GNU_SOURCE +#define _DEFAULT_SOURCE +#include +#include +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_fault_tolerant.h" +#include +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); +void filex_fault_tolerant_write_large_data_undo_log_interrupt_test_application_define(void *first_unused_memory); + +#if defined (FX_ENABLE_FAULT_TOLERANT) && defined (FX_FAULT_TOLERANT) + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 2048 +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +static TX_THREAD ftest_1; +#else +static pthread_t ptid1; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +static UCHAR *thread_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UINT error_couter = 0; +static ULONG write_data_size; +static ULONG available_bytes; +static UINT undo_log_write_interrupt = FX_FALSE; +static ULONG i, j; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +static void ftest_0_entry(ULONG thread_input); +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input); +#else +static void * ftest_1_entry(void * thread_input); +#endif + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); +extern UINT _filex_fault_tolerant_log_check(FX_MEDIA *media_ptr); +extern UINT (*driver_write_callback)(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr); + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_write_large_data_undo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + thread_buffer = pointer; + pointer += DEMO_STACK_SIZE; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +ULONG data_value; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Write Large Data Undo Log Interrupt test."); + + /* Generate a random number for write data. */ + srand((ULONG)time(NULL)); + data_value = (ULONG)(rand() & 0xFF) | (ULONG)((rand() & 0xFF) << 8) | (ULONG)((rand() & 0xFF) << 16) | (ULONG)((rand() & 0xFF) << 24); + + /* Genearte the write data. */ + for (j = 0; j < large_data_buffer_size / sizeof(ULONG); j ++) + { + ((ULONG*)large_data_buffer)[j] = data_value++; + } + + /* Roll back the value for later verification use. */ + data_value -= (large_data_buffer_size / sizeof(ULONG)); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 60000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 400000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Pickup the available bytes in the media. */ + status = fx_media_space_available(&ram_disk, &available_bytes); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (available_bytes < sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + write_data_size = available_bytes; + + /* Create the main thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_create(&ftest_1, "thread 1", ftest_1_entry, 0, + thread_buffer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); +#endif + + /* undo_log_write_interrupt */ + undo_log_write_interrupt = FX_FALSE; + + /* Let the other thread run. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_relinquish(); +#else + pthread_create(&ptid1, NULL, &ftest_1_entry, NULL); + usleep(10); + pthread_join(ptid1,NULL); +#endif + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(8); + } + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Pickup the available bytes in the media again. */ + status = fx_media_space_available(&ram_disk, &j); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (j != available_bytes)) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Now read in all the bytes again to make sure the file contents are really there. */ + status = fx_file_read(&my_file, (void *) large_data_buffer, large_data_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_END_OF_FILE) || (actual != 0)) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (undo_log_write_interrupt != FX_TRUE) || (error_couter)) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Delete the thread. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_delete(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} + +/* Define the test threads. */ + +#ifndef FX_STANDALONE_ENABLE +static void ftest_1_entry(ULONG thread_input) +#else + void * ftest_1_entry(void * thread_input) +#endif +{ + +#ifdef FX_STANDALONE_ENABLE + UINT oldtype; + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); +#endif + + UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Set the callback function to simulate poweoff operation when write FAT entry. */ + driver_write_callback = my_driver_write; + + /* Write the data to fill the media one time. */ + status = fx_file_write(&my_file, (void *) large_data_buffer, write_data_size); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + error_couter ++; +#ifndef FX_STANDALONE_ENABLE + return; +#else + return NULL; +#endif + } +} + +static UINT my_driver_write(FX_MEDIA *media_ptr, UINT sector_type, UCHAR *block_ptr, UINT *operation_ptr) +{ + + FX_PARAMETER_NOT_USED(block_ptr); + + /* Interrupt the undo write operation. */ + if ((sector_type == FX_DATA_SECTOR) && (_filex_fault_tolerant_log_check(media_ptr) & FX_FAULT_TOLERANT_LOG_UNDO_UPDATING)) + { + + /* Set the write interrupt operation. */ + *operation_ptr = FX_OP_WRITE_INTERRUPT; + + /* Update the flag. */ + undo_log_write_interrupt = FX_TRUE; + + /* Clean the callback function. */ + driver_write_callback = FX_NULL; + + /* Delete the media protection structure if FX_SINGLE_THREAD is not + defined. */ +#ifndef FX_SINGLE_THREAD +#ifndef FX_DONT_CREATE_MUTEX + + /* Note that the protection is never released. The mutex delete + service will handle all threads waiting access to this media + control block. */ + tx_mutex_delete(&(media_ptr -> fx_media_protect)); +#endif +#endif + + /* Clean the media data. */ + _fx_system_media_opened_ptr = FX_NULL; + _fx_system_media_opened_count = 0; + + /* Clean the media. */ + memset(media_ptr, 0, sizeof(FX_MEDIA)); + + /* Simulate poweroff. */ + /* First terminate the thread to ensure it is ready for deletion. */ +#ifndef FX_STANDALONE_ENABLE + tx_thread_terminate(&ftest_1); +#else + pthread_cancel(ptid1); +#endif + } + + /* Return. */ + return FX_SUCCESS; +} +#else + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_write_large_data_undo_log_interrupt_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Fault Tolerant Write Large Data Undo Log Interrupt test.N/A\n"); + + test_control_return(255); +} +#endif diff --git a/test/regression_test/filex_file_allocate_test.c b/test/regression_test/filex_file_allocate_test.c new file mode 100644 index 0000000..c2f7575 --- /dev/null +++ b/test/regression_test/filex_file_allocate_test.c @@ -0,0 +1,343 @@ +/* This FileX test concentrates on the file allocate test. */ +/* +Test steps: +1. Format and open the media; +2. Enable fault tolerant feature if it is compiled; +3. Create new file called "TEST.TXT"; +4. Allocate the file with available bytes and seek to the beginning of file; +5. Write 1000 bytes out of the file to fill the media; +6. Close the file; +7. Open the file and read data from it; +*/ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_fault_tolerant.h" +#include +#include +#include "fx_ram_driver_test.h" +extern void test_control_return(UINT status); + +#define DEMO_STACK_SIZE 4096 + +#define CACHE_SIZE 2048 + +#ifdef FX_ENABLE_FAULT_TOLERANT +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE +#else +#define FAULT_TOLERANT_SIZE 0 +#endif + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static UCHAR *pointer; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *cache_buffer; +#ifdef FX_ENABLE_FAULT_TOLERANT +static UCHAR *fault_tolerant_buffer; +#endif /* FX_ENABLE_FAULT_TOLERANT */ +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#ifdef FX_ENABLE_FAULT_TOLERANT +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif /* FX_ENABLE_FAULT_TOLERANT */ +#endif + + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +void filex_file_allocate_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void test_control_return(UINT status); + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_file_allocate_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer += CACHE_SIZE; +#ifdef FX_ENABLE_FAULT_TOLERANT + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; +#endif /* FX_ENABLE_FAULT_TOLERANT */ + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +ULONG file_size = 1000; +ULONG i, j; +ULONG data_value; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: File Allocate Test....................................."); + + /* Generate a random number for write data. */ + srand((ULONG)time(NULL)); + data_value = (ULONG)(rand() & 0xFF) | (ULONG)((rand() & 0xFF) << 8) | (ULONG)((rand() & 0xFF) << 16) | (ULONG)((rand() & 0xFF) << 24); + + /* Genearte the write data. */ + for (j = 0; j < large_data_buffer_size / sizeof(ULONG); j ++) + { + ((ULONG*)large_data_buffer)[j] = data_value++; + } + + /* Roll back the value for later verification use. */ + data_value -= (large_data_buffer_size / sizeof(ULONG)); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 60000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 400000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + + /* Check status. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(3); + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + status = fx_file_allocate(&my_file, file_size); + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + status = fx_file_seek(&my_file, 0); + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Write the data to fill the media one time. */ + status = fx_file_write(&my_file, (void *) large_data_buffer, file_size); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Now read in all the bytes again to make sure the file contents are really there. */ + status = fx_file_read(&my_file, (void *) large_data_buffer, large_data_buffer_size, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != file_size)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + } + + /* Output successful. */ + printf("SUCCESS!\n"); + test_control_return(0); +} diff --git a/test/regression_test/filex_file_allocate_truncate_test.c b/test/regression_test/filex_file_allocate_truncate_test.c new file mode 100644 index 0000000..a7231dc --- /dev/null +++ b/test/regression_test/filex_file_allocate_truncate_test.c @@ -0,0 +1,1150 @@ +/* This FileX test concentrates on the file allocate/truncate operation. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include +#include "fx_ram_driver_test.h" + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 128*128 + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static FX_FILE my_file1; +static FX_FILE my_file2; +static FX_FILE read_only; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + + +/* Define thread prototypes. */ + +void filex_file_allocate_truncate_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_file_allocate_truncate_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +ULONG temp; +ULONG write_value; +ULONG available_bytes; +CHAR buffer[1]; +ULONG i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: File allocate/truncate test............................"); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Attempt to truncate a file before the file is opened */ + status = fx_file_extended_truncate(&my_file, 0); + if (status != FX_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(2); + } + + /* Attempt to release a file before the file is opened */ + status = fx_file_extended_truncate_release(&my_file, 0); + if (status != FX_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(3); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(4); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Create a file called READ_ONLY.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "READ_ONLY.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Pickup the available bytes in the media. */ + status = fx_media_space_available(&ram_disk, &available_bytes); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (available_bytes < sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Open the test files. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + status = fx_file_open(&ram_disk, &read_only, "READ_ONLY.TXT", FX_OPEN_FOR_READ); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Try to release more than was allocated */ + status = fx_file_extended_truncate_release(&my_file, 0xFFFFFFFFFFFFFFFF); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(9); + } + + /* Attempt to truncate a file that is not open for writing */ + status = fx_file_extended_truncate(&read_only, 0); + if (status != FX_ACCESS_ERROR) + { + printf("ERROR!\n"); + test_control_return(10); + } + + /* Attempt to release a file that is not open for writing */ + status = fx_file_extended_truncate_release(&read_only, 0); + if (status != FX_ACCESS_ERROR) + { + printf("ERROR!\n"); + test_control_return(11); + } + + /* Attempt to truncate a file while the media is write protected */ + ram_disk.fx_media_driver_write_protect = FX_TRUE; + status = fx_file_extended_truncate(&my_file, 0); + if (status != FX_WRITE_PROTECT) + { + printf("ERROR!\n"); + test_control_return(12); + } + + /* Attempt to release a file while the media is write protected */ + status = fx_file_extended_truncate_release(&my_file, 0); + if (status != FX_WRITE_PROTECT) + { + printf("ERROR!\n"); + test_control_return(13); + } + ram_disk.fx_media_driver_write_protect = FX_FALSE; + + ram_disk.fx_media_bytes_per_sector = 0; + + /* Attempt to release a file while the media is corrupted. */ + status = fx_file_extended_truncate_release(&my_file, 0); + if (status != FX_MEDIA_INVALID) + { + printf("ERROR!\n"); + test_control_return(13); + } + ram_disk.fx_media_bytes_per_sector = 128; + + /* Attempt to truncate a file to larger than it is */ + status = fx_file_extended_truncate(&my_file, 0xFFFFFFFFFFFFFFFF); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(14); + } + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + /* send null pointer to generate an error */ + status = fx_file_allocate(FX_NULL, 1500); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(15); + } + + /* send null pointer to generate an error */ + status = fx_file_extended_truncate(FX_NULL, 0xFF); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(16); + } + + /* send null pointer to generate an error */ + status = fx_file_extended_truncate_release(FX_NULL, 0xFF); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(17); + } + + /* send null pointer to generate an error */ + status = fx_file_extended_allocate(FX_NULL, 0xFF); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(18); + } + + /* send null pointer to generate an error */ + status = fx_file_best_effort_allocate(FX_NULL, 0xFF, FX_NULL); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(19); + } +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Allocate half the size first... */ + status = fx_file_allocate(&my_file, available_bytes/2); + + /* Check the file allocate status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Pickup the available bytes in the media again. */ + status = fx_media_space_available(&ram_disk, &i); + + + /* Attempt to allocate all the bytes again... but the best effort should just get us the + remaining bytes... */ + status += fx_file_best_effort_allocate(&my_file, available_bytes, &actual); + + /* Check the best effort file allocate status. */ + if ((status != FX_SUCCESS) || (actual != i)) + { + + printf("ERROR!\n"); + test_control_return(21); + } + +#ifdef FX_UPDATE_FILE_SIZE_ON_ALLOCATE + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(22); + } + + /* send null pointer to generate an error */ + status = fx_file_best_effort_allocate(FX_NULL, 1500, FX_NULL); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(23); + } +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Loop to write successive bytes out to the file.... to fill the media! */ + i = 0; + write_value = 0; + while (i < available_bytes) + { + + /* Write 4 bytes to the file. */ + status = fx_file_write(&my_file, (void *) &write_value, sizeof(ULONG)); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Increment byte count. */ + i = i + sizeof(ULONG); + + /* Increment write value. */ + write_value++; + } + + /* Pickup the available bytes in the media again. */ + status = fx_media_space_available(&ram_disk, &i); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (i != 0)) + { + + printf("ERROR!\n"); + test_control_return(25); + } + +#ifndef FX_DISABLE_CACHE + /* At this point, we should invalidate the (which also flushes the cache) media to ensure that all + dirty sectors are written. */ + status = fx_media_cache_invalidate(&ram_disk); + + /* Check for invalidate errors. */ + if ((status != FX_SUCCESS) || (ram_disk.fx_media_sector_cache_dirty_count)) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* See if any sectors are still valid in the cache. */ + for (i = 0; i < FX_MAX_SECTOR_CACHE; i++) + { + + /* Determine if this cache entry is still valid. */ + if (ram_disk.fx_media_sector_cache[i].fx_cached_sector_valid) + { + + printf("ERROR!\n"); + test_control_return(27); + } + } +#endif + + /* Now truncate half the file... */ + status = fx_file_truncate(&my_file, available_bytes/2); + + /* Check for errors... */ + if ((status) || (my_file.fx_file_current_file_size != my_file.fx_file_current_available_size/2)) + { + + printf("ERROR!\n"); + test_control_return(28); + } + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + /* send null pointer to generate an error */ + status = fx_file_truncate(FX_NULL, 0); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(29); + } + + /* send null pointer to generate an error */ + status = fx_file_truncate_release(FX_NULL, 0); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(30); + } +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Now truncate the remainder of file... */ + status = fx_file_truncate(&my_file, 0); + + /* Check for errors... */ + if ((status) || (my_file.fx_file_current_file_size != 0)) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Pickup the available bytes in the media again. */ + status = fx_media_space_available(&ram_disk, &i); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (i != 0)) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* At this point write the file again... */ + i = 0; + write_value = 0; + while (i < available_bytes) + { + + /* Write 4 bytes to the file. */ + status = fx_file_write(&my_file, (void *) &write_value, sizeof(ULONG)); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Increment byte count. */ + i = i + sizeof(ULONG); + + /* Increment write value. */ + write_value++; + } + + /* Now truncate and release half the file... */ + status = fx_file_truncate_release(&my_file, available_bytes/2); + + /* Pickup the available bytes in the media again. */ + status += fx_media_space_available(&ram_disk, &i); + + /* Check for errors... */ + if ((status) || (my_file.fx_file_current_file_size != available_bytes/2)) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Now truncate and release the remaining half the file... */ + status = fx_file_truncate_release(&my_file, 0); + + /* Pickup the available bytes in the media again. */ + status += fx_media_space_available(&ram_disk, &i); + + /* Check for errors... */ + if ((status) || (my_file.fx_file_current_file_size != 0) || + (i != available_bytes)) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Test overflow of available bytes >4GB of this API. */ + + /* Set the available clusters to maximum value. */ + temp = ram_disk.fx_media_available_clusters; + ram_disk.fx_media_available_clusters = 0xFFFFFFFF; + + /* Pickup the available bytes in the media again. */ + status = fx_media_space_available(&ram_disk, &available_bytes); + + /* Restore the actual available clusters. */ + ram_disk.fx_media_available_clusters = temp; + + /* Check for errors... */ + if ((status) || (available_bytes != 0xFFFFFFFF)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Close the file. */ + status = fx_file_close(&my_file); + + /* Close the media. */ + status += fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(37); + } + + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 18000, // Total sectors + 128, // Sector size + 3, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(38); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(39); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_create(&ram_disk, "TEST1.TXT"); + status += fx_file_create(&ram_disk, "TEST2.TXT"); + status += fx_file_create(&ram_disk, "TEST3.TXT"); + status += fx_file_create(&ram_disk, "TEST4.TXT"); + status += fx_file_create(&ram_disk, "TEST5.TXT"); + status += fx_file_create(&ram_disk, "TEST6.TXT"); + status += fx_file_create(&ram_disk, "TEST7.TXT"); + status += fx_file_create(&ram_disk, "TEST8.TXT"); + status += fx_file_create(&ram_disk, "TEST9.TXT"); + status += fx_file_create(&ram_disk, "TEST10.TXT"); + status += fx_file_create(&ram_disk, "TEST11.TXT"); + status += fx_file_create(&ram_disk, "TEST12.TXT"); + status += fx_file_create(&ram_disk, "TEST13.TXT"); + status += fx_file_create(&ram_disk, "TEST14.TXT"); + status += fx_file_create(&ram_disk, "TEST15.TXT"); + status += fx_file_create(&ram_disk, "TEST16.TXT"); + status += fx_file_create(&ram_disk, "TEST17.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(40); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &read_only, "TEST.TXT", FX_OPEN_FOR_READ); + status += fx_file_open(&ram_disk, &my_file1, "TEST1.TXT", FX_OPEN_FOR_READ); + status += fx_file_open(&ram_disk, &my_file2, "TEST17.TXT", FX_OPEN_FOR_READ); + + /* Write 2048 bytes to the file. */ + for (i = 0; i < 4096; i++) + { + /* Write a byte to the file. */ + status += fx_file_write(&my_file, " ", 1); + status += fx_file_read(&read_only, buffer, 1, &actual); + } + + /* Allocate some additional clusters. */ + status += fx_file_allocate(&my_file, 128*6); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(41); + } + + /* Now truncate the file to a value less than the available size, but greater than the current size. */ + status = fx_file_extended_truncate_release(&my_file, 4096 + 128); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(42); + } + + /* Now truncate the file to the available size. */ + status = fx_file_extended_truncate_release(&my_file, 4096); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(43); + } + + /* Now truncate the file to half the available size. */ + status = fx_file_extended_truncate_release(&my_file, 2048); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(44); + } + + /* Now truncate the file to nothing. */ + status = fx_file_extended_truncate_release(&my_file, 0); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(45); + } + + /* Close everything down. */ + status = fx_file_close(&my_file); + status += fx_file_close(&my_file1); + status += fx_file_close(&my_file2); + status += fx_file_close(&read_only); + status += fx_media_close(&ram_disk); + + /* Check status. */ + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(46); + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 18000, // Total sectors + 128, // Sector size + 3, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(47); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(48); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(49); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &read_only, "TEST.TXT", FX_OPEN_FOR_READ); + + /* Write 2048 bytes to the file. */ + for (i = 0; i < 4096; i++) + { + /* Write a byte to the file. */ + status += fx_file_write(&my_file, " ", 1); + status += fx_file_read(&read_only, buffer, 1, &actual); + } + + /* Flush the media. */ + status += fx_media_flush(&ram_disk); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(50); + } + + /* Now truncate the file to half the available size, but force an I/O error on the directory entry write. */ + _fx_ram_driver_io_error_request = 1; + status = fx_file_extended_truncate_release(&my_file, 2048); + _fx_ram_driver_io_error_request = 0; + if (status != FX_IO_ERROR) + { + printf("ERROR!\n"); + test_control_return(51); + } + + /* Now truncate the file with FAT I/O Error. */ + _fx_utility_fat_entry_read_error_request = 1; + status = fx_file_extended_truncate_release(&my_file, 2000); + _fx_utility_fat_entry_read_error_request = 0; + if (status != FX_IO_ERROR) + { + printf("ERROR!\n"); + test_control_return(52); + } + + /* Now truncate the file with FAT I/O Error. */ + _fx_utility_fat_entry_read_error_request = 6; + status = fx_file_extended_truncate_release(&my_file, 1900); + _fx_utility_fat_entry_read_error_request = 0; + if (status != FX_IO_ERROR) + { + printf("ERROR!\n"); + test_control_return(53); + } + + /* Now truncate the file with FAT I/O Error. */ + _fx_utility_fat_entry_write_error_request = 1; + status = fx_file_extended_truncate_release(&my_file, 1700); + _fx_utility_fat_entry_write_error_request = 0; + if (status != FX_IO_ERROR) + { + printf("ERROR!\n"); + test_control_return(54); + } + + /* Now truncate the file with FAT I/O Error. */ + _fx_utility_fat_entry_write_error_request = 2; + status = fx_file_extended_truncate_release(&my_file, 1500); + _fx_utility_fat_entry_write_error_request = 0; + if (status != FX_IO_ERROR) + { + printf("ERROR!\n"); + test_control_return(55); + } + + /* Now truncate the file with FAT I/O Error. */ + _fx_utility_fat_entry_read_error_request = 7; + status = fx_file_extended_truncate_release(&my_file, 500); + _fx_utility_fat_entry_read_error_request = 0; + if (status != FX_IO_ERROR) + { + printf("ERROR!\n"); + test_control_return(56); + } + + /* Close everything down. */ + status = fx_file_close(&my_file); + status += fx_file_close(&read_only); + status += fx_media_close(&ram_disk); + + /* Check status. */ + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(54); + } + + /* Test the cluster values of 1 and fx_media_fat_reserved errors in the FAT chain. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 18000, // Total sectors + 128, // Sector size + 3, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(55); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(56); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(57); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &read_only, "TEST.TXT", FX_OPEN_FOR_READ); + + /* Write 2048 bytes to the file. */ + for (i = 0; i < 4096; i++) + { + /* Write a byte to the file. */ + status += fx_file_write(&my_file, " ", 1); + status += fx_file_read(&read_only, buffer, 1, &actual); + } + + /* Flush the media. */ + status += fx_media_flush(&ram_disk); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(58); + } + + /* Now truncate one byte of the file. */ + status = fx_file_extended_truncate_release(&my_file, 4095); + + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(59); + } + + + /* Now truncate the file but force a FAT entry of 1. */ + _fx_utility_fat_entry_read_error_request = 10005; + status = fx_file_extended_truncate_release(&my_file, 4094); + _fx_utility_fat_entry_read_error_request = 0; + if (status != FX_FILE_CORRUPT) + { + printf("ERROR!\n"); + test_control_return(60); + } + + /* Now truncate the file but force a FAT entry of max value. */ + _fx_utility_fat_entry_read_error_request = 20005; + status = fx_file_extended_truncate_release(&my_file, 4093); + _fx_utility_fat_entry_read_error_request = 0; + if (status != FX_FILE_CORRUPT) + { + printf("ERROR!\n"); + test_control_return(61); + } + + /* Now truncate the file but force a FAT entry of 1 to the traversal of releasing clusters. */ + _fx_utility_fat_entry_read_error_request = 10012; + status = fx_file_extended_truncate_release(&my_file, 4092); + _fx_utility_fat_entry_read_error_request = 0; + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(62); + } + + /* Now truncate the file but force a FAT entry of 1 in the traversal of open files. */ + _fx_utility_fat_entry_read_error_request = 10025; + status = fx_file_extended_truncate_release(&my_file, 4091); + _fx_utility_fat_entry_read_error_request = 0; + if (status != FX_FILE_CORRUPT) + { + printf("ERROR!\n"); + test_control_return(63); + } + + /* Now truncate the file but force a FAT entry of 1 in the traversal of open files. */ + _fx_utility_fat_entry_read_error_request = 20025; + status = fx_file_extended_truncate_release(&my_file, 4090); + _fx_utility_fat_entry_read_error_request = 0; + if (status != FX_FILE_CORRUPT) + { + printf("ERROR!\n"); + test_control_return(64); + } + + /* Close everything down. */ + status = fx_file_close(&my_file); + status += fx_file_close(&read_only); + status += fx_media_close(&ram_disk); + + /* Check status. */ + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(63); + } + + /* Test file extended truncate corner cases. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 18000, // Total sectors + 128, // Sector size + 3, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(64); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(65); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_create(&ram_disk, "TEST1.TXT"); + status += fx_file_create(&ram_disk, "TEST2.TXT"); + status += fx_file_create(&ram_disk, "TEST3.TXT"); + status += fx_file_create(&ram_disk, "TEST4.TXT"); + status += fx_file_create(&ram_disk, "TEST5.TXT"); + status += fx_file_create(&ram_disk, "TEST6.TXT"); + status += fx_file_create(&ram_disk, "TEST7.TXT"); + status += fx_file_create(&ram_disk, "TEST8.TXT"); + status += fx_file_create(&ram_disk, "TEST9.TXT"); + status += fx_file_create(&ram_disk, "TEST10.TXT"); + status += fx_file_create(&ram_disk, "TEST11.TXT"); + status += fx_file_create(&ram_disk, "TEST12.TXT"); + status += fx_file_create(&ram_disk, "TEST13.TXT"); + status += fx_file_create(&ram_disk, "TEST14.TXT"); + status += fx_file_create(&ram_disk, "TEST15.TXT"); + status += fx_file_create(&ram_disk, "TEST16.TXT"); + status += fx_file_create(&ram_disk, "TEST17.TXT"); + status += fx_file_create(&ram_disk, "TEST18.TXT"); + status += fx_file_create(&ram_disk, "TEST19.TXT"); + status += fx_file_create(&ram_disk, "TEST20.TXT"); + status += fx_file_create(&ram_disk, "TEST21.TXT"); + status += fx_file_create(&ram_disk, "TEST22.TXT"); + status += fx_file_create(&ram_disk, "TEST23.TXT"); + status += fx_file_create(&ram_disk, "TEST24.TXT"); + status += fx_file_create(&ram_disk, "TEST25.TXT"); + status += fx_file_create(&ram_disk, "TEST26.TXT"); + status += fx_file_create(&ram_disk, "TEST27.TXT"); + status += fx_file_create(&ram_disk, "TEST28.TXT"); + status += fx_file_create(&ram_disk, "TEST29.TXT"); + status += fx_file_create(&ram_disk, "TEST30.TXT"); + + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(67); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &read_only, "TEST.TXT", FX_OPEN_FOR_READ); + status += fx_file_open(&ram_disk, &my_file1, "TEST1.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file2, "TEST30.TXT", FX_OPEN_FOR_WRITE); + + /* Write 2048 bytes to the file. */ + for (i = 0; i < 4096; i++) + { + /* Write a byte to the file. */ + status += fx_file_write(&my_file, " ", 1); + status += fx_file_read(&read_only, buffer, 1, &actual); + } + + /* Flush the media. */ + status += fx_media_flush(&ram_disk); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(68); + } + + /* Now truncate one byte of the file. */ + status = fx_file_extended_truncate(&my_file, 4095); + + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(69); + } + + /* Seek to make the file offset less than our new truncate size. */ + status = fx_file_seek(&my_file, 4094); + + /* Truncate another byte of the file. */ + status = fx_file_extended_truncate(&my_file, 4094); + + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(70); + } + + /* Now truncate the another byte of the file, but with a FAT read error. */ + _fx_utility_fat_entry_read_error_request = 1; + status = fx_file_extended_truncate(&my_file, 4093); + _fx_utility_fat_entry_read_error_request = 0; + if (status != FX_IO_ERROR) + { + printf("ERROR!\n"); + test_control_return(71); + } + + + /* Now truncate the file but force a FAT entry of 1. */ + _fx_utility_fat_entry_read_error_request = 10003; + status = fx_file_extended_truncate(&my_file, 4092); + _fx_utility_fat_entry_read_error_request = 0; + if (status != FX_FILE_CORRUPT) + { + printf("ERROR!\n"); + test_control_return(72); + } + + /* Now truncate the file but force a FAT entry of max value. */ + _fx_utility_fat_entry_read_error_request = 20003; + status = fx_file_extended_truncate(&my_file, 4091); + _fx_utility_fat_entry_read_error_request = 0; + if (status != FX_FILE_CORRUPT) + { + printf("ERROR!\n"); + test_control_return(73); + } + + /* Close everything down. */ + status = fx_file_close(&my_file); + status += fx_file_close(&my_file1); + status += fx_file_close(&my_file2); + status += fx_file_close(&read_only); + status += fx_media_close(&ram_disk); + + /* Check status. */ + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(74); + } + else + { + + printf("SUCCESS!\n"); + test_control_return(0); + } +} + diff --git a/test/regression_test/filex_file_attributes_read_set_test.c b/test/regression_test/filex_file_attributes_read_set_test.c new file mode 100644 index 0000000..98ccf9b --- /dev/null +++ b/test/regression_test/filex_file_attributes_read_set_test.c @@ -0,0 +1,423 @@ +/* This FileX test concentrates on the file attributes read/set operation. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include +#include "fx_ram_driver_test.h" + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 128*128 + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static FX_FILE my_file1; +static FX_FILE my_file2; +static FX_FILE my_file3; +static FX_FILE my_file4; +static FX_FILE my_file5; +static FX_FILE my_file6; +static FX_FILE my_file7; +static FX_FILE my_file8; +static FX_FILE my_file9; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + + +/* Define thread prototypes. */ + +void filex_file_attributes_read_set_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_file_attributes_read_set_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT attributes; +FX_FILE open_file; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: File attributes read/set test.........................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Attempt to read file attributes before the media has been opened */ + status = fx_file_attributes_read(&ram_disk, "TEST.TXT", &attributes); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(2); + } + + /* Attempt to set file attributes before the media has been opened */ + status = fx_file_attributes_set(&ram_disk, "TEST.TXT", 0); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(3); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(4); + } + + /* Attempt to set file attributes while the media is write protected */ + ram_disk.fx_media_driver_write_protect = FX_TRUE; + status = fx_file_attributes_set(&ram_disk, "TEST.TXT", FX_READ_ONLY); + if (status != FX_WRITE_PROTECT) + { + printf("ERROR!\n"); + test_control_return(5); + } + ram_disk.fx_media_driver_write_protect = FX_FALSE; + + /* attempt to read file attributes from a file that does not exist */ + status = fx_file_attributes_read(&ram_disk, "TEST.TXT", &attributes); + if (status == FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(6); + } + + /* Attempt to set file attributes before the media has been opened */ + status = fx_file_attributes_set(&ram_disk, "TEST.TXT", attributes); + if (status == FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(7); + } + + /* attempt to read file attributes from something that is not a file */ + status = fx_directory_create(&ram_disk, "NOT_A_FILE"); + status += fx_file_attributes_read(&ram_disk, "NOT_A_FILE", &attributes); + if (status != FX_NOT_A_FILE) + { + printf("ERROR!\n"); + test_control_return(8); + } + + /* Attempt to set file attributes before the media has been opened */ + status = fx_file_attributes_set(&ram_disk, "NOT_A_FILE", FX_READ_ONLY); + if (status != FX_NOT_A_FILE) + { + printf("ERROR!\n"); + test_control_return(9); + } + + /* Attempt to set file attributes with invlaid file name. */ + status = fx_file_attributes_set(&ram_disk, "NOT_EXIST", FX_READ_ONLY); + if (status != FX_NOT_FOUND) + { + printf("ERROR!\n"); + test_control_return(9); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + +#ifndef FX_DISABLE_ERROR_CHECKING + + /* send null pointer to generate an error */ + status = fx_file_attributes_set(FX_NULL, "filename", FX_READ_ONLY); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(11); + } + + /* send null pointer to generate an error */ + status = fx_file_attributes_set(&ram_disk, "filename", 0x40); + if (status != FX_INVALID_ATTR) + { + printf("ERROR!\n"); + test_control_return(12); + } + + /* send null pointer to generate an error */ + status = fx_file_attributes_read(FX_NULL, "filename", FX_NULL); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(13); + } + +#endif + + /* Invalidate the media cache. */ + fx_media_cache_invalidate(&ram_disk); + + /* Pickup the attributes of the file. */ + status = fx_file_attributes_read(&ram_disk, "TEST.TXT", &attributes); + + /* Check the attributes read status. */ + if ((status != FX_SUCCESS) || (attributes != FX_ARCHIVE)) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Invalidate the media cache. */ + fx_media_cache_invalidate(&ram_disk); + + /* Now write the attributes out for the file. */ + status = fx_file_attributes_set(&ram_disk, "TEST.TXT", attributes | FX_READ_ONLY); + + /* Check the attributes set status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Write the attributes out to the file while another file is opened to get better code coverage */ + status = fx_file_create(&ram_disk, "open_file.txt"); + status += fx_file_open(&ram_disk, &open_file, "open_file.txt", FX_OPEN_FOR_WRITE); + status += fx_file_attributes_set(&ram_disk, "TEST.TXT", attributes | FX_READ_ONLY); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(16); + } + + /* Invalidate the media cache. */ + fx_media_cache_invalidate(&ram_disk); + + /* Pickup the attributes of the file again. */ + status = fx_file_attributes_read(&ram_disk, "TEST.TXT", &attributes); + + /* Check the attributes read status. */ + if ((status != FX_SUCCESS) || (attributes != (FX_ARCHIVE | FX_READ_ONLY))) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Open the test file... this should fail since we set the attributes to read only! */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_ACCESS_ERROR) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Attempt to set file attributes for a file that is open */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_READ); + status += fx_file_attributes_set(&ram_disk, "TEST.TXT", attributes); + if (status != FX_ACCESS_ERROR) + { + printf("ERROR!\n"); + test_control_return(19); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Now test the attributes set with multiple files open that have directory entries that reside on + different logical sectors. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Create a set of test files. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_create(&ram_disk, "TEST1.TXT"); + status += fx_file_create(&ram_disk, "TEST2.TXT"); + status += fx_file_create(&ram_disk, "TEST3.TXT"); + status += fx_file_create(&ram_disk, "TEST4.TXT"); + status += fx_file_create(&ram_disk, "TEST5.TXT"); + status += fx_file_create(&ram_disk, "TEST6.TXT"); + status += fx_file_create(&ram_disk, "TEST7.TXT"); + status += fx_file_create(&ram_disk, "TEST8.TXT"); + status += fx_file_create(&ram_disk, "TEST9.TXT"); + status += fx_file_create(&ram_disk, "TEST10.TXT"); + + /* Now open all the files except the TEST.TXT. */ + status += fx_file_open(&ram_disk, &my_file, "TEST1.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file1, "TEST2.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file2, "TEST3.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file3, "TEST4.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file4, "TEST5.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file5, "TEST6.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file6, "TEST7.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file7, "TEST8.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file8, "TEST9.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file9, "TEST10.TXT", FX_OPEN_FOR_WRITE); + + /* Now set the attributes for TEST.TXT. */ + status += fx_file_attributes_set(&ram_disk, "TEST.TXT", FX_READ_ONLY); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(23); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + else + { + + printf("SUCCESS!\n"); + test_control_return(0); + } +} + diff --git a/test/regression_test/filex_file_create_delete_test.c b/test/regression_test/filex_file_create_delete_test.c new file mode 100644 index 0000000..725b51f --- /dev/null +++ b/test/regression_test/filex_file_create_delete_test.c @@ -0,0 +1,1100 @@ +/* This FileX test concentrates on the file create/delete operations. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_fault_tolerant.h" +#include "fx_media.h" +#include "fx_ram_driver_test.h" +#include "fx_utility.h" +#include + +#define DEMO_STACK_SIZE 8192 +#define CACHE_SIZE 16*128 +#ifdef FX_ENABLE_FAULT_TOLERANT +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE +#else +#define FAULT_TOLERANT_SIZE 0 +#endif + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE file_1; +static FX_FILE file_2; +static FX_FILE file_3; +static FX_FILE file_4; +static FX_FILE file_5; +static FX_FILE file_6; +static FX_FILE file_7; + + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif +static UCHAR fat_buffer[128]; +static UCHAR name_buffer[FX_MAX_LONG_NAME_LEN+1]; + + +/* Define thread prototypes. */ + +void filex_file_create_delete_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_file_create_delete_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UCHAR buffer[30]; +ULONG actual; +ULONG64 actual64; +UINT i; +ULONG64 temp; +UINT temp_attr; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: File create/delete test................................"); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Attempt to invalidate the media cache before the media is opened to generate an error */ + status = fx_media_cache_invalidate(&ram_disk); + return_if_fail( status == FX_MEDIA_NOT_OPEN); + + /* Attempt to get space available information before the media is opened to generate an error */ + status = fx_media_extended_space_available(&ram_disk, &temp); + return_if_fail( status == FX_MEDIA_NOT_OPEN); + + /* Attempt to allocate space before the media is opened to generate an error */ + status = fx_file_extended_allocate(&file_1, 0); + return_if_fail( status == FX_NOT_OPEN); + + /* Attempt to allocate space before the media is opened to generate an error */ + status = fx_file_extended_best_effort_allocate(&file_1, 0, &temp); + return_if_fail( status == FX_NOT_OPEN); + + /* try to create a file before the media has been opened to generate an error */ + status = fx_file_create(&ram_disk, "asdf"); + return_if_fail( status == FX_MEDIA_NOT_OPEN); + + /* try to close a file before the file has been opened to generate an error */ + status = fx_file_close(&file_1); + return_if_fail( status == FX_NOT_OPEN); + + /* try to delete a file before the media has been opened to generate an error */ + status = fx_file_delete(&ram_disk, "asdf"); + return_if_fail( status == FX_MEDIA_NOT_OPEN); + + /* this will be caught by _fxe_file_open instead of _fx_file_open if DISABLE_ERROR_CHECKING is not defined */ + /* Attempt to open a file before the media has been opened to generate an error */ + status = fx_file_open(&ram_disk, &file_5, "rootname2", FX_OPEN_FOR_WRITE); + return_if_fail( (status == FX_MEDIA_NOT_OPEN) || (status == FX_PTR_ERROR)); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Enable fault tolerant if FX_ENABLE_FAULT_TOLERANT is defined. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); +#endif + + /* try to create a file with an illegal name to generate an error */ + status = fx_file_create(&ram_disk, ""); + return_if_fail( status == FX_INVALID_NAME); + + /* try to create a file with an illegal path to generate an error */ + status = fx_file_create(&ram_disk, "/subdir/root"); + return_if_fail( status == FX_INVALID_PATH); + + /* Simple 8.3 rename in the root directory. */ + status = fx_directory_create(&ram_disk, "subdir"); + status += fx_file_create(&ram_disk, "rootname"); + status += fx_file_create(&ram_disk, "rootname1"); + status += fx_file_create(&ram_disk, "rootname2"); + status += fx_file_create(&ram_disk, "/subdir/rootname"); + status += fx_file_create(&ram_disk, "/subdir/rootname1"); + return_if_fail( status == FX_SUCCESS); + + /* Attempt to open a file that is not a file to generate an error */ + status = fx_file_open(&ram_disk, &file_5, "subdir", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_NOT_A_FILE); + + /* try to create and delete a file when the media is write protected to generate an error */ + ram_disk.fx_media_driver_write_protect = FX_TRUE; + status = fx_file_create(&ram_disk, "asdf"); + return_if_fail( status == FX_WRITE_PROTECT); + + status = fx_file_delete(&ram_disk, "rootname"); + return_if_fail( status == FX_WRITE_PROTECT); + + /* Attempt to open a file while the media is write protected to generate an error */ + status = fx_file_open(&ram_disk, &file_5, "rootname2", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_WRITE_PROTECT); + ram_disk.fx_media_driver_write_protect = FX_FALSE; + + /* Attempt to create the same file again. This should cause an error! */ + status = fx_file_create(&ram_disk, "rootname"); + return_if_fail( status == FX_ALREADY_CREATED); + + /* attempt to open a file with an invalid open type to generate an error */ + /* This code is executing differently on local vs server. Disabled until cause is explored + status = fx_file_open(&ram_disk, &file_5, "rootname2", 3); + return_if_fail( status == FX_ACCESS_ERROR); + */ + + /* try to delete a file in a directory that is read only */ + status = fx_file_attributes_read(&ram_disk, "rootname2", &temp_attr); + status += fx_file_attributes_set(&ram_disk, "rootname2", FX_READ_ONLY); + status += fx_file_delete(&ram_disk, "rootname2"); + status += fx_file_attributes_set(&ram_disk, "rootname2", temp_attr); + return_if_fail( status == FX_WRITE_PROTECT); + + /* Open all the files. */ + status = fx_file_open(&ram_disk, &file_1, "rootname", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_2, "rootname1", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_3, "/subdir/rootname", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_4, "/subdir/rootname1", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_5, "rootname2", FX_OPEN_FOR_READ); + return_if_fail( status == FX_SUCCESS); + + /* attempt to open a file that is already open */ + status = fx_file_open(&ram_disk, &file_6, "rootname", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_ACCESS_ERROR); + + /* try to create and delete a file when the media is write protected to generate an error */ + ram_disk.fx_media_driver_write_protect = FX_TRUE; + status = fx_file_create(&ram_disk, "asdf"); + return_if_fail( status == FX_WRITE_PROTECT); + status = fx_file_delete(&ram_disk, "asdf"); + return_if_fail( status == FX_WRITE_PROTECT); + + /* Attempt to allocate space while the media is write protected to generate an error */ + status = fx_file_extended_allocate(&file_1, 1); + return_if_fail( status == FX_WRITE_PROTECT); + status = fx_file_extended_best_effort_allocate(&file_1, 1, &temp); + return_if_fail( status == FX_WRITE_PROTECT); + ram_disk.fx_media_driver_write_protect = FX_FALSE; + +/* test the error checking */ +#ifndef FX_DISABLE_ERROR_CHECKING + + /* send a null pointer to generate an error */ + status = fx_media_extended_space_available(FX_NULL, FX_NULL); + return_if_fail( status == FX_PTR_ERROR); + + /* send a null pointer to generate an error */ + status = fx_file_extended_best_effort_allocate(FX_NULL, 0, FX_NULL); + + /* send null pointer to generate an error */ + status = fx_file_create(FX_NULL, "rootname"); + return_if_fail( status == FX_PTR_ERROR); + + /* send null pointer to generate an error */ + status = fx_file_open(FX_NULL, FX_NULL, "rootname", 0); + return_if_fail( status == FX_PTR_ERROR); + + /* attempt to open an already open file to generate an error */ + status = fx_file_open(&ram_disk, &file_1, "rootname", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_PTR_ERROR); + + /* Attempt to allocate space for a file that is not open to write to generate an error */ + status = fx_file_open(&ram_disk, &file_5, "rootname2", FX_OPEN_FOR_READ); + status = fx_file_extended_allocate(&file_5, 1); + return_if_fail( status == FX_ACCESS_ERROR); + + /* Attempt to allocate space for a file that is not open to write to generate an error */ + status = fx_file_extended_best_effort_allocate(&file_5, 0, &temp); + return_if_fail( status == FX_ACCESS_ERROR); + + /* Allocate 0 space for a file */ + status = fx_file_extended_allocate(&file_1, 0); + return_if_fail( status == FX_SUCCESS); + + /* Allocate 0 space for a file */ + status = fx_file_extended_best_effort_allocate(&file_1, 0, &temp); + return_if_fail( status == FX_SUCCESS); + + /* Attempt to allocate too much space for a file */ + status = fx_file_extended_allocate(&file_1, 0xFFFFFFFFFFFFFFFF); + return_if_fail( status == FX_NO_MORE_SPACE); + + /* Corrupt the media. */ + ram_disk.fx_media_bytes_per_sector = 0; + status = fx_file_extended_allocate(&file_1, 1); + return_if_fail(status == FX_MEDIA_INVALID); + + status = fx_file_extended_best_effort_allocate(&file_1, 1, &actual64); + return_if_fail(status == FX_MEDIA_INVALID); + + ram_disk.fx_media_bytes_per_sector = 256; + +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* try to delete an open file to generate an error */ + status = fx_file_delete(&ram_disk, "rootname"); + return_if_fail( status == FX_ACCESS_ERROR); + + /* try to delete something that is not a file to generate an error */ + status = fx_file_delete(&ram_disk, "subdir"); + return_if_fail( status == FX_NOT_A_FILE); + + /* Now write a buffer to each file. */ + status = fx_file_write(&file_1, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&file_2, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&file_3, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&file_4, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + return_if_fail( status == FX_SUCCESS); + + /* Seek to the beginning of each file. */ + status += fx_file_seek(&file_1, 0); + status += fx_file_seek(&file_2, 0); + status += fx_file_seek(&file_3, 0); + status += fx_file_seek(&file_4, 0); + return_if_fail( status == FX_SUCCESS); + + /* Now read the buffer. */ + status = fx_file_read(&file_1, buffer, 30, &actual); + return_if_fail( (status == FX_SUCCESS) && (actual == 26)); + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + /* send null pointer to generate an error */ + status = fx_file_read(FX_NULL, buffer, 30, &actual); + return_if_fail(status == FX_PTR_ERROR); +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Now read the buffer. */ + status = fx_file_read(&file_2, buffer, 30, &actual); + return_if_fail( (status == FX_SUCCESS) && (actual == 26)); + + /* Now read the buffer. */ + status = fx_file_read(&file_3, buffer, 30, &actual); + return_if_fail( (status == FX_SUCCESS) && (actual == 26)); + + /* Now read the buffer. */ + status = fx_file_read(&file_4, buffer, 30, &actual); + return_if_fail( (status == FX_SUCCESS) && (actual == 26)); + + /* Close all files. */ + status = fx_file_close(&file_1); + status += fx_file_close(&file_2); + status += fx_file_close(&file_3); + status += fx_file_close(&file_4); + status += fx_file_close(&file_5); + return_if_fail( status == FX_SUCCESS); + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + /* send null pointer to generate an error */ + status = fx_file_close(FX_NULL); + return_if_fail(status == FX_PTR_ERROR); + + /* send null pointer to generate an error */ + status = fx_file_delete(FX_NULL, "rootname"); + return_if_fail(status == FX_PTR_ERROR); +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Delete all files. */ + status = fx_file_delete(&ram_disk, "rootname"); + status += fx_file_delete(&ram_disk, "rootname1"); + status += fx_file_delete(&ram_disk, "/subdir/rootname"); + status += fx_file_delete(&ram_disk, "/subdir/rootname1"); + status += fx_file_delete(&ram_disk, "rootname2"); + status += fx_directory_delete(&ram_disk, "/subdir"); + return_if_fail( status == FX_SUCCESS); + + /* Attempt to delete an already deleted file. */ + status += fx_file_delete(&ram_disk, "rootname"); + return_if_fail( status == FX_NOT_FOUND); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Test corner cases in extended best effort allocate. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 7000, // Total sectors - FAT 16 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + return_if_fail( status == FX_SUCCESS); + + /* Create a file. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Create a secondary file. */ + status += fx_file_create(&ram_disk, "TEST1.TXT"); + return_if_fail( status == FX_SUCCESS); + + /* Open the file. */ + status = fx_file_open(&ram_disk, &file_6, "TEST.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_5, "TEST1.TXT", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + /* Write one cluster of information to the TEST1.TXT file. */ + status = fx_file_write(&file_5, buffer, 128); + + /* Close the secondary file. */ + status += fx_file_close(&file_5); + + /* Loop to take up the entire ram disk by writing to this file. */ + while (ram_disk.fx_media_available_clusters) + { + /* Write a one cluster block. */ + status += fx_file_write(&file_6, buffer, 128); + } + return_if_fail( status == FX_SUCCESS); + + /* Attempt to allocate when there is nothing available. */ + status = fx_file_extended_best_effort_allocate(&file_6, 128, &actual64); + return_if_fail( status == FX_NO_MORE_SPACE); + + /* Now release the first cluster to create a hole at the front. */ + status = fx_file_delete(&ram_disk, "TEST1.TXT"); + + /* Now flush everything out. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Read the first FAT sector. */ + status += fx_media_read(&ram_disk, 1, (VOID *) fat_buffer); + + /* Add a FAT entry randomly in the FAT table. */ + fat_buffer[4] = 0x03; + fat_buffer[5] = 0x00; + + /* Write the FAT corruption out. */ + status += fx_media_write(&ram_disk, 1, (VOID *) fat_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Attempt to allocate when there is nothing available - with a lost cluster. */ + status = fx_file_extended_best_effort_allocate(&file_6, 128, &actual64); + return_if_fail( status == FX_NO_MORE_SPACE); + + /* Attempt to allocate when there is an I/O error. */ + _fx_utility_fat_entry_read_error_request = 1; + status = fx_file_extended_best_effort_allocate(&file_6, 128, &actual64); + _fx_utility_fat_entry_read_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Now flush everything out. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 1, (VOID *) fat_buffer); + + /* Make the first entry available. */ + fat_buffer[4] = 0x00; + fat_buffer[5] = 0x00; + + /* Write the FAT corruption out. */ + status += fx_media_write(&ram_disk, 1, (VOID *) fat_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Allocate with an exact fit. */ + status = fx_file_extended_best_effort_allocate(&file_6, 128, &actual64); + return_if_fail( status == FX_SUCCESS); + + /* Release all the clusters for this file. */ + status = fx_file_extended_truncate_release(&file_6, 0); + status += fx_file_write(&file_6, fat_buffer, 128); + status += fx_file_write(&file_6, fat_buffer, 128); + status += fx_file_write(&file_6, fat_buffer, 128); + status += fx_file_write(&file_6, fat_buffer, 128); + + /* Allocate 4 clusters. */ + status += fx_file_extended_best_effort_allocate(&file_6, 512, &actual64); + return_if_fail( status == FX_SUCCESS); + + /* Release all the clusters for this file. */ + status = fx_file_extended_truncate_release(&file_6, 0); + + /* Allocate 4 clusters with no clusters in the file. */ + status += fx_file_extended_best_effort_allocate(&file_6, 512, &actual64); + return_if_fail( status == FX_SUCCESS); + + /* Write 512 bytes of data out. */ + status += fx_file_write(&file_6, fat_buffer, 128); + status += fx_file_write(&file_6, fat_buffer, 128); + status += fx_file_write(&file_6, fat_buffer, 128); + status += fx_file_write(&file_6, fat_buffer, 128); + + /* Now allocate perform allocations with I/O errors on building the new FAT chain. */ + _fx_utility_fat_entry_write_error_request = 1; + status += fx_file_extended_best_effort_allocate(&file_6, 512, &actual64); + _fx_utility_fat_entry_write_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Now allocate perform allocations with I/O error on writing the EOF. */ + _fx_utility_fat_entry_write_error_request = 4; + status = fx_file_extended_best_effort_allocate(&file_6, 512, &actual64); + _fx_utility_fat_entry_write_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Now allocate perform allocations with I/O error on linking to last cluster. */ + _fx_utility_fat_entry_write_error_request = 5; + status = fx_file_extended_best_effort_allocate(&file_6, 512, &actual64); + _fx_utility_fat_entry_write_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Release all the clusters for this file. */ + status = fx_file_extended_truncate_release(&file_6, 0); + + /* Now flush everything out. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + +#ifndef FX_ENABLE_FAULT_TOLERANT + /* Now allocate perform allocations with I/O error on the directory write request. */ + _fx_utility_logical_sector_write_error_request = 1; + status = fx_file_extended_best_effort_allocate(&file_6, 128, &actual64); + _fx_utility_logical_sector_write_error_request = 0; + return_if_fail( status == FX_IO_ERROR); +#endif + + /* Close the file and the media. */ + status = fx_file_close(&file_6); + status += fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 21000, // Total sectors - FAT 16 + 128, // Sector size + 3, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + return_if_fail( status == FX_SUCCESS); + + /* Create a file. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + return_if_fail( status == FX_SUCCESS); + + /* Open the file. */ + status = fx_file_open(&ram_disk, &file_6, "TEST.TXT", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + /* Write data to the TEST.TXT file. */ + status = fx_file_write(&file_6, buffer, 128); + status += fx_file_write(&file_6, buffer, 128); + status += fx_file_write(&file_6, buffer, 64); + + /* Allocate more clusters but with the offset on a sector boundary rather than a cluster boundary. */ + status = fx_file_extended_best_effort_allocate(&file_6, 512, &actual64); + return_if_fail( status == FX_SUCCESS); + + /* Truncate the file. */ + status = fx_file_extended_truncate_release(&file_6, 0); + + /* Write data to the TEST.TXT file. */ + status += fx_file_write(&file_6, buffer, 128); + + /* Allocate more clusters but with the offset on a sector boundary rather than a cluster boundary. */ + status += fx_file_extended_best_effort_allocate(&file_6, 512, &actual64); + return_if_fail( status == FX_SUCCESS); + + /* Close the file and the media. */ + status = fx_file_close(&file_6); + status += fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Test corner cases in extended allocate. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 7000, // Total sectors - FAT 16 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + return_if_fail( status == FX_SUCCESS); + + /* Create a file. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Create a secondary file. */ + status += fx_file_create(&ram_disk, "TEST1.TXT"); + return_if_fail( status == FX_SUCCESS); + + /* Open the file. */ + status = fx_file_open(&ram_disk, &file_6, "TEST.TXT", FX_OPEN_FOR_WRITE); + status = fx_file_open(&ram_disk, &file_5, "TEST1.TXT", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + /* Write one cluster of information to the TEST1.TXT file. */ + status = fx_file_write(&file_5, buffer, 128); + + /* Close the secondary file. */ + status += fx_file_close(&file_5); + + /* Loop to take up the entire ram disk by writing to this file. */ + while (ram_disk.fx_media_available_clusters) + { + + /* Write a one cluster block. */ + status += fx_file_write(&file_6, buffer, 128); + } + return_if_fail( status == FX_SUCCESS); + + /* Attempt to allocate when there is nothing available. */ + status = fx_file_extended_allocate(&file_6, 128); + return_if_fail( status == FX_NO_MORE_SPACE); + + /* Now release the first cluster to create a hole at the front. */ + status = fx_file_delete(&ram_disk, "TEST1.TXT"); + + /* Now flush everything out. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Read the first FAT sector. */ + status += fx_media_read(&ram_disk, 1, (VOID *) fat_buffer); + + /* Add a FAT entry randomly in the FAT table. */ + fat_buffer[4] = 0x03; + fat_buffer[5] = 0x00; + + /* Write the FAT corruption out. */ + status += fx_media_write(&ram_disk, 1, (VOID *) fat_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Attempt to allocate when there is nothing available - with a lost cluster. */ + status = fx_file_extended_allocate(&file_6, 128); + return_if_fail( status == FX_NO_MORE_SPACE); + + /* Attempt to allocate when there is an I/O error. */ + _fx_utility_fat_entry_read_error_request = 1; + status = fx_file_extended_allocate(&file_6, 128); + _fx_utility_fat_entry_read_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Now flush everything out. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 1, (VOID *) fat_buffer); + + /* Make the first entry available. */ + fat_buffer[4] = 0x00; + fat_buffer[5] = 0x00; + + /* Write the FAT corruption out. */ + status += fx_media_write(&ram_disk, 1, (VOID *) fat_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Allocate with an exact fit. */ + status = fx_file_extended_allocate(&file_6, 128); + return_if_fail( status == FX_SUCCESS); + + /* Release all the clusters for this file. */ + status = fx_file_extended_truncate_release(&file_6, 0); + status += fx_file_write(&file_6, fat_buffer, 128); + status += fx_file_write(&file_6, fat_buffer, 128); + status += fx_file_write(&file_6, fat_buffer, 128); + status += fx_file_write(&file_6, fat_buffer, 128); + + /* Allocate 4 clusters. */ + status += fx_file_extended_allocate(&file_6, 512); + return_if_fail( status == FX_SUCCESS); + + /* Release all the clusters for this file. */ + status = fx_file_extended_truncate_release(&file_6, 0); + + /* Allocate 4 clusters with no clusters in the file. */ + status += fx_file_extended_allocate(&file_6, 512); + return_if_fail( status == FX_SUCCESS); + + /* Write 512 bytes of data out. */ + status += fx_file_write(&file_6, fat_buffer, 128); + status += fx_file_write(&file_6, fat_buffer, 128); + status += fx_file_write(&file_6, fat_buffer, 128); + status += fx_file_write(&file_6, fat_buffer, 128); + + /* Now allocate perform allocations with I/O errors on building the new FAT chain. */ + _fx_utility_fat_entry_write_error_request = 1; + status += fx_file_extended_allocate(&file_6, 512); + _fx_utility_fat_entry_write_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Now allocate perform allocations with I/O error on writing the EOF. */ + _fx_utility_fat_entry_write_error_request = 4; + status = fx_file_extended_allocate(&file_6, 512); + _fx_utility_fat_entry_write_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Now allocate perform allocations with I/O error on linking to last cluster. */ + _fx_utility_fat_entry_write_error_request = 5; + status = fx_file_extended_allocate(&file_6, 512); + _fx_utility_fat_entry_write_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Release all the clusters for this file. */ + status = fx_file_extended_truncate_release(&file_6, 0); + + /* Now flush everything out. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + +#ifndef FX_ENABLE_FAULT_TOLERANT + + /* Now allocate perform allocations with I/O error on the directory write request. */ + _fx_utility_logical_sector_write_error_request = 1; + status = fx_file_extended_allocate(&file_6, 128); + _fx_utility_logical_sector_write_error_request = 0; + return_if_fail( status == FX_IO_ERROR); +#endif + + /* Close the file and the media. */ + status = fx_file_close(&file_6); + status += fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 21000, // Total sectors - FAT 16 + 128, // Sector size + 3, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + return_if_fail( status == FX_SUCCESS); + + /* Create a file. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + return_if_fail( status == FX_SUCCESS); + + /* Open the file. */ + status = fx_file_open(&ram_disk, &file_6, "TEST.TXT", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + /* Write one cluster of information to the TEST1.TXT file. */ + status = fx_file_write(&file_6, buffer, 128); + status += fx_file_write(&file_6, buffer, 128); + status += fx_file_write(&file_6, buffer, 64); + + /* Allocate more clusters but with the offset on a sector boundary rather than a cluster boundary. */ + status = fx_file_extended_allocate(&file_6, 512); + return_if_fail( status == FX_SUCCESS); + + /* Truncate the file. */ + status = fx_file_extended_truncate_release(&file_6, 0); + + /* Write data to the TEST.TXT file. */ + status += fx_file_write(&file_6, buffer, 128); + + /* Allocate more clusters but with the offset on a sector boundary rather than a cluster boundary. */ + status += fx_file_extended_allocate(&file_6, 512); + return_if_fail( status == FX_SUCCESS); + + /* Now test the maximum allocate size. */ + actual64 = file_6.fx_file_current_available_size; + file_6.fx_file_current_available_size = 0xFFFFFFF8; + + /* Allocate more clusters but with the file near maximum size to test the maximum size logic. */ + status = fx_file_extended_allocate(&file_6, 512); + file_6.fx_file_current_available_size = actual64; + return_if_fail( status == FX_NO_MORE_SPACE); + + /* Now test the maximum allocate size. */ + actual64 = file_6.fx_file_current_available_size; + file_6.fx_file_current_available_size = 0xFFFFFFFFFFFFFFF8; + + /* Allocate more clusters but with the file near maximum size to test the maximum size logic. */ + status = fx_file_extended_allocate(&file_6, 512); + file_6.fx_file_current_available_size = actual64; + return_if_fail( status == FX_NO_MORE_SPACE); + + /* Now test the maximum allocate size. */ + actual64 = file_6.fx_file_current_available_size; + file_6.fx_file_current_available_size = 0xFFFFFFF8; + + /* Allocate more clusters but with the file near maximum size to test the maximum size logic. */ + status = fx_file_extended_best_effort_allocate(&file_6, 512, &actual64); + file_6.fx_file_current_available_size = actual64; + return_if_fail( status == FX_NO_MORE_SPACE); + + /* Now test the maximum allocate size. */ + actual64 = file_6.fx_file_current_available_size; + file_6.fx_file_current_available_size = 0xFFFFFFFFFFFFFFF8; + + /* Allocate more clusters but with the file near maximum size to test the maximum size logic. */ + status = fx_file_extended_best_effort_allocate(&file_6, 512, &actual64); + file_6.fx_file_current_available_size = actual64; + return_if_fail( status == FX_NO_MORE_SPACE); + + /* Now test a maximium size allocation. */ + status = fx_file_extended_best_effort_allocate(&file_6, 0xFFFFFFFFFFFFFFF8ULL, &actual64); + return_if_fail( status == FX_NO_MORE_SPACE); + + /* Close the file and the media. */ + status = fx_file_close(&file_6); + status += fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Check the corner cases of file delete. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 7000, // Total sectors - FAT 16 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + return_if_fail( status == FX_SUCCESS); + + /* Create a set of files. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_create(&ram_disk, "TEST1.TXT"); + status += fx_file_create(&ram_disk, "TEST2.TXT"); + status += fx_file_create(&ram_disk, "TEST3.TXT"); + status += fx_file_create(&ram_disk, "TEST4.TXT"); + status += fx_file_create(&ram_disk, "TEST5.TXT"); + status += fx_file_create(&ram_disk, "TEST6.TXT"); + status += fx_file_create(&ram_disk, "TEST7.TXT"); + status += fx_file_create(&ram_disk, "TEST8.TXT"); + status += fx_file_create(&ram_disk, "TEST9.TXT"); + status += fx_file_create(&ram_disk, "TEST10.TXT"); + status += fx_file_create(&ram_disk, "TEST11.TXT"); + status += fx_file_create(&ram_disk, "TEST12.TXT"); + status += fx_file_create(&ram_disk, "TEST13.TXT"); + status += fx_file_create(&ram_disk, "TEST14.TXT"); + status += fx_file_create(&ram_disk, "TEST15.TXT"); + status += fx_file_create(&ram_disk, "TEST16.TXT"); + status += fx_file_create(&ram_disk, "TEST17.TXT"); + status += fx_file_create(&ram_disk, "TEST18.TXT"); + status += fx_file_create(&ram_disk, "TEST19.TXT"); + status += fx_file_create(&ram_disk, "TEST20.TXT"); + status += fx_file_create(&ram_disk, "TEST21.TXT"); + status += fx_file_create(&ram_disk, "TEST22.TXT"); + status += fx_file_create(&ram_disk, "TEST23.TXT"); + status += fx_file_create(&ram_disk, "TEST24.TXT"); + status += fx_file_create(&ram_disk, "TEST25.TXT"); + status += fx_file_create(&ram_disk, "TEST26.TXT"); + status += fx_file_create(&ram_disk, "TEST27.TXT"); + status += fx_file_create(&ram_disk, "TEST28.TXT"); + status += fx_file_create(&ram_disk, "TEST29.TXT"); + status += fx_file_create(&ram_disk, "TEST30.TXT"); + return_if_fail( status == FX_SUCCESS); + + /* Open a couple files.... */ + status = fx_file_open(&ram_disk, &file_4, "TEST.TXT", FX_OPEN_FOR_WRITE); + status = fx_file_open(&ram_disk, &file_5, "TEST1.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_6, "TEST30.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_7, "TEST29.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&file_4, fat_buffer, 128); + status += fx_file_write(&file_4, fat_buffer, 128); + status += fx_file_write(&file_4, fat_buffer, 128); + status += fx_file_write(&file_4, fat_buffer, 128); + status += fx_file_write(&file_5, fat_buffer, 128); + status += fx_file_write(&file_5, fat_buffer, 128); + status += fx_file_write(&file_5, fat_buffer, 128); + status += fx_file_write(&file_5, fat_buffer, 128); + status += fx_file_write(&file_6, fat_buffer, 128); + status += fx_file_write(&file_6, fat_buffer, 128); + status += fx_file_write(&file_6, fat_buffer, 128); + status += fx_file_write(&file_6, fat_buffer, 128); + status += fx_file_write(&file_7, fat_buffer, 128); + status += fx_file_write(&file_7, fat_buffer, 128); + status += fx_file_write(&file_7, fat_buffer, 128); + status += fx_file_write(&file_7, fat_buffer, 128); + return_if_fail( status == FX_SUCCESS); + + /* Now flush everything out. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Close TEST.TXT. */ + status = fx_file_close(&file_4); + + /* Attempt to delete the file, but with open files on a different offset/sector and a write I/O error. */ + _fx_utility_logical_sector_read_error_request = 2; + status += fx_file_delete(&ram_disk, "TEST.TXT"); + _fx_utility_logical_sector_read_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Close all the open files. */ + status = fx_file_close(&file_5); + status += fx_file_close(&file_6); + status += fx_file_close(&file_7); + + /* Now flush everything out. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Attempt to delete the file, but with a FAT read error. */ + _fx_utility_fat_entry_read_error_request = 1; + status += fx_file_delete(&ram_disk, "TEST1.TXT"); + _fx_utility_fat_entry_read_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Now flush everything out. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Attempt to delete the file, but with a FAT write error. */ + _fx_utility_fat_entry_write_error_request = 1; + status = fx_file_delete(&ram_disk, "TEST30.TXT"); + _fx_utility_fat_entry_write_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Now flush everything out. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 1, (VOID *) fat_buffer); + + /* Make the first entry available. */ + fat_buffer[4] = 0x02; + fat_buffer[5] = 0x00; + + /* Write the FAT corruption out. */ + status += fx_media_write(&ram_disk, 1, (VOID *) fat_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Attempt to delete the file, but with a bad FAT chain. */ + status = fx_file_delete(&ram_disk, "TEST.TXT"); + return_if_fail( status == FX_FAT_READ_ERROR); + + /* Attempt to delete the file that exceeds the cluster clount (that we modify to be small). */ + actual = ram_disk.fx_media_total_clusters; + ram_disk.fx_media_total_clusters = 1; + status = fx_file_delete(&ram_disk, "TEST29.TXT"); + ram_disk.fx_media_total_clusters = actual; + return_if_fail( status == FX_FAT_READ_ERROR); + + /* Build a long file name, exceeding FX_MAX_LONG_NAME_LEN. */ + for (i = 0; i < (FX_MAX_LONG_NAME_LEN+1); i++) + { + name_buffer[i] = 'a'; + } + name_buffer[FX_MAX_LONG_NAME_LEN] = 0; + + /* Attempt to create a file with this extra long name... this is expected to fail. */ + status = fx_file_create(&ram_disk, (CHAR *)name_buffer); + return_if_fail( status == FX_INVALID_NAME); + + /* Now open and write to a file so we can test file close I/O error condition. */ + status = fx_file_open(&ram_disk, &file_7, "TEST20.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&file_7, fat_buffer, 128); + + /* Close the file with an I/O error. */ + _fx_utility_logical_sector_read_error_request = 1; + status += fx_file_close(&file_7); + _fx_utility_logical_sector_read_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Now call the best effort allocate with a closed file handle. */ + status = fx_file_best_effort_allocate(&file_7, 1, &actual); + return_if_fail( status == FX_NOT_OPEN); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + printf("SUCCESS!\n"); + test_control_return(0); +} diff --git a/test/regression_test/filex_file_date_time_set_test.c b/test/regression_test/filex_file_date_time_set_test.c new file mode 100644 index 0000000..df3ecce --- /dev/null +++ b/test/regression_test/filex_file_date_time_set_test.c @@ -0,0 +1,646 @@ +/* This FileX test concentrates on the file date/time set operation. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include +#include "fx_ram_driver_test.h" + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 128*128 + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE file_0; +static FX_FILE file_1; +static FX_FILE file_2; +static FX_FILE file_3; +static FX_FILE file_4; +static FX_FILE file_5; +static FX_FILE file_6; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + + +/* Define thread prototypes. */ + +void filex_file_date_time_set_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_file_date_time_set_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT attributes; +ULONG size; +UINT year, month, day, hour, minute, second; +#ifndef FX_DISABLE_ERROR_CHECKING +UINT i; +#endif /* FX_DISABLE_ERROR_CHECKING */ +FX_FILE my_file; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: File date/time set test................................"); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* try to retrieve directory information before the media has been opened to generate an error */ + status = fx_directory_information_get(&ram_disk, "DOES_NOT_EXIST", &attributes, &size, + &year, &month, &day, &hour, &minute, &second); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(2); + } + + /* Attempt to set the date and time for a file before the media is opened to generate an error */ + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", 2000, 1, 1, 1, 1, 1); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(3); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(4); + } + + /* Create a few files file in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_create(&ram_disk, "OPENED_FOR_WRITE.TXT"); + status += fx_file_create(&ram_disk, "NOT_OPENED.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + +/* test error checking */ +#ifndef FX_DISABLE_ERROR_CHECKING + + /* send a null pointer to generate an error */ + status = fx_directory_information_get(&ram_disk, "DOES_NOT_EXIST", FX_NULL, FX_NULL, + FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL, FX_NULL); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(6); + } + + /* send null pointer to generate an error */ + status = fx_file_date_time_set(FX_NULL, "TEST.TXT", 0, 0, 0, 0, 0, 0); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(7); + } + + /* send an invalid year to generate an error */ + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", 0, 0, 0, 0, 0, 0); + if (status != FX_INVALID_YEAR) + { + printf("ERROR!\n"); + test_control_return(8); + } + + /* send an invalid month to generate an error */ + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", 2015, 0, 1, 0, 0, 0); + if (status != FX_INVALID_MONTH) + { + printf("ERROR!\n"); + test_control_return(9); + } + + /* send a valid and invalid day for each month to generate an error */ + for (i = 1; i <= 12; i++) + { + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", 2015, i, 0, 0, 0, 0); + if (status != FX_INVALID_DAY) + { + printf("ERROR!\n"); + test_control_return(10); + } + + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", 2015, i, 1, 1, 1, 1); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(11); + } + } + + /* send an invalid day for feb of a leap year */ + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", 2016, 2, 0, 0, 0, 0); + if (status != FX_INVALID_DAY) + { + printf("ERROR!\n"); + test_control_return(12); + } + + /* send an invalid hour to generate an error */ + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", 2000, 1, 1, 99, 0, 0); + if (status != FX_INVALID_HOUR) + { + printf("ERROR!\n"); + test_control_return(13); + } + + /* send an invalid minute to generate an error */ + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", 2000, 1, 1, 1, 99, 0); + if (status != FX_INVALID_MINUTE) + { + printf("ERROR!\n"); + test_control_return(14); + } + + /* send an invalid second to generate an error */ + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", 2000, 1, 1, 1, 1, 99); + if (status != FX_INVALID_SECOND) + { + printf("ERROR!\n"); + test_control_return(15); + } + +#endif + + /* Attempt to set the date and time for a file that does not exist to generate an error */ + status = fx_file_date_time_set(&ram_disk, "DOES_NOT_EXIST", 2000, 1, 1, 1, 1, 1); + if (status == FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(16); + } + + /* Invalidate the media cache. */ + fx_media_cache_invalidate(&ram_disk); + + /* Set the date and time for the file. */ + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", + 1999, 12, 31, 23, 59, 58); + + /* Check the date/time set status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* try to retrieve directory information for something that doesnt exist to generate an error */ + status = fx_directory_information_get(&ram_disk, "DOES_NOT_EXIST", &attributes, &size, + &year, &month, &day, &hour, &minute, &second); + if (status == FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(18); + } + + /* Invalidate the media cache. */ + fx_media_cache_invalidate(&ram_disk); + + /* Now pickup the date/time for the file. */ + status = fx_directory_information_get(&ram_disk, "TEST.TXT", &attributes, &size, + &year, &month, &day, &hour, &minute, &second); + + /* Check the date/time status. */ + if ((status != FX_SUCCESS) || (attributes != FX_ARCHIVE) || (size != 0) || + (year != 1999) || (month != 12) || (day != 31) || (hour != 23) || (minute != 59) || (second != 58)) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* get the date/time for the other files while some are opened to get better code coverage */ + status = fx_file_open(&ram_disk, &my_file, "OPENED_FOR_WRITE.TXT", FX_OPEN_FOR_WRITE); + status += fx_directory_information_get(&ram_disk, "OPENED_FOR_WRITE.TXT", &attributes, &size, + &year, &month, &day, &hour, &minute, &second); + status += fx_directory_information_get(&ram_disk, "NOT_OPENED.TXT", &attributes, &size, + &year, &month, &day, &hour, &minute, &second); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } +#ifndef FX_DISABLE_ERROR_CHECKING + /* Set the date and time for with an invalid file name. */ + status = fx_file_date_time_set(&ram_disk, NULL, + 1999, 12, 31, 23, 59, 58); + + /* Check the date/time set status. */ + if (status != FX_PTR_ERROR) + { + + printf("ERROR!\n"); + test_control_return(22); + } + + /* Set the date and time for with an invalid year. */ + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", + FX_MAXIMUM_YEAR+1, 12, 31, 23, 59, 58); + + /* Check the date/time set status. */ + if (status != FX_INVALID_YEAR) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Set the date and time for with an invalid day for January. */ + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", + 2016, 1, 32, 23, 59, 58); + + /* Check the date/time set status. */ + if (status != FX_INVALID_DAY) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Set the date and time for with an invalid day for Febuary leap year. */ + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", + 2016, 2, 30, 23, 59, 58); + + /* Check the date/time set status. */ + if (status != FX_INVALID_DAY) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Set the date and time for with a valid day for Febuary leap year. */ + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", + 2016, 2, 29, 24, 59, 58); + + /* Check the date/time set status. */ + if (status != FX_INVALID_HOUR) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Set the date and time for with an invalid day for Febuary. */ + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", + 2017, 2, 29, 23, 59, 58); + + /* Check the date/time set status. */ + if (status != FX_INVALID_DAY) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Set the date and time for with an invalid day for March. */ + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", + 2017, 3, 32, 23, 59, 58); + + /* Check the date/time set status. */ + if (status != FX_INVALID_DAY) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Set the date and time for with an invalid day for April. */ + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", + 2017, 4, 31, 23, 59, 58); + + /* Check the date/time set status. */ + if (status != FX_INVALID_DAY) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Set the date and time for with an invalid day for May. */ + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", + 2017, 5, 32, 23, 59, 58); + + /* Check the date/time set status. */ + if (status != FX_INVALID_DAY) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Set the date and time for with an invalid day for June. */ + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", + 2017, 6, 31, 23, 59, 58); + + /* Check the date/time set status. */ + if (status != FX_INVALID_DAY) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Set the date and time for with an invalid day for July. */ + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", + 2017, 7, 32, 23, 59, 58); + + /* Check the date/time set status. */ + if (status != FX_INVALID_DAY) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Set the date and time for with an invalid day for August. */ + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", + 2017, 8, 32, 23, 59, 58); + + /* Check the date/time set status. */ + if (status != FX_INVALID_DAY) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Set the date and time for with an invalid day for September. */ + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", + 2017, 9, 31, 23, 59, 58); + + /* Check the date/time set status. */ + if (status != FX_INVALID_DAY) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Set the date and time for with an invalid day for October. */ + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", + 2017, 10, 32, 23, 59, 58); + + /* Check the date/time set status. */ + if (status != FX_INVALID_DAY) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Set the date and time for with an invalid day for November. */ + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", + 2017, 11, 31, 23, 59, 58); + + /* Check the date/time set status. */ + if (status != FX_INVALID_DAY) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Set the date and time for with an invalid day for December. */ + status = fx_file_date_time_set(&ram_disk, "TEST.TXT", + 2017, 12, 32, 23, 59, 58); + + /* Check the date/time set status. */ + if (status != FX_INVALID_DAY) + { + + printf("ERROR!\n"); + test_control_return(37); + } +#endif + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 64, // Directory Entries + 0, // Hidden sectors + 4096, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(38); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(39); + } + + /* Create a bunch of files. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_create(&ram_disk, "TEST1.TXT"); + status += fx_file_create(&ram_disk, "TEST2.TXT"); + status += fx_file_create(&ram_disk, "TEST3.TXT"); + status += fx_file_create(&ram_disk, "TEST4.TXT"); + status += fx_file_create(&ram_disk, "TEST5.TXT"); + status += fx_file_create(&ram_disk, "TEST6.TXT"); + status += fx_file_create(&ram_disk, "TEST7.TXT"); + status += fx_file_create(&ram_disk, "TEST8.TXT"); + status += fx_file_create(&ram_disk, "TEST9.TXT"); + status += fx_file_create(&ram_disk, "TEST10.TXT"); + status += fx_file_create(&ram_disk, "TEST11.TXT"); + status += fx_file_create(&ram_disk, "TEST12.TXT"); + status += fx_file_create(&ram_disk, "TEST13.TXT"); + status += fx_file_create(&ram_disk, "TEST14.TXT"); + status += fx_file_create(&ram_disk, "TEST15.TXT"); + status += fx_file_create(&ram_disk, "TEST16.TXT"); + status += fx_file_create(&ram_disk, "TEST17.TXT"); + status += fx_file_create(&ram_disk, "TEST18.TXT"); + status += fx_file_create(&ram_disk, "TEST19.TXT"); + status += fx_file_create(&ram_disk, "TEST20.TXT"); + status += fx_file_create(&ram_disk, "TEST21.TXT"); + status += fx_file_create(&ram_disk, "TEST22.TXT"); + status += fx_file_create(&ram_disk, "TEST23.TXT"); + status += fx_file_create(&ram_disk, "TEST24.TXT"); + status += fx_file_create(&ram_disk, "TEST25.TXT"); + status += fx_file_create(&ram_disk, "TEST26.TXT"); + status += fx_file_create(&ram_disk, "TEST27.TXT"); + status += fx_file_create(&ram_disk, "TEST28.TXT"); + status += fx_file_create(&ram_disk, "TEST29.TXT"); + status += fx_file_create(&ram_disk, "TEST30.TXT"); + status += fx_file_create(&ram_disk, "TEST31.TXT"); + + /* Now open a set of files so we get all combinations through the open count check in fx_directory_information_get.c. */ + status += fx_file_open(&ram_disk, &file_0, "TEST.TXT", FX_OPEN_FOR_READ); + status += fx_file_open(&ram_disk, &file_1, "TEST1.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_2, "TEST2.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_3, "TEST9.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_4, "TEST11.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_5, "TEST19.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &file_6, "TEST30.TXT", FX_OPEN_FOR_WRITE); + + /* Now get information on the first file... requesting on the "second" parameter. */ + status += fx_directory_information_get(&ram_disk, "TEST.TXT", FX_NULL, FX_NULL, + FX_NULL, FX_NULL, FX_NULL, + FX_NULL, FX_NULL, &second); + + /* Determine if there was an error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(40); + } + + /* Now get information on the first file... requesting on the "minute" parameter. */ + status += fx_directory_information_get(&ram_disk, "TEST.TXT", FX_NULL, FX_NULL, + FX_NULL, FX_NULL, FX_NULL, + FX_NULL, &minute, FX_NULL); + + /* Determine if there was an error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(41); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if there was an error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(42); + } + else + { + + printf("SUCCESS!\n"); + test_control_return(0); + } +} + diff --git a/test/regression_test/filex_file_name_test.c b/test/regression_test/filex_file_name_test.c new file mode 100644 index 0000000..e9ba1aa --- /dev/null +++ b/test/regression_test/filex_file_name_test.c @@ -0,0 +1,213 @@ +/* This FileX test concentrates on the lowercase is not allowed in 8.3 name. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include +#include "fx_ram_driver_test.h" +#include "fx_fault_tolerant.h" + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 128*128 +#ifdef FX_ENABLE_FAULT_TOLERANT +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE +#else +#define FAULT_TOLERANT_SIZE 0 +#endif + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif +static UCHAR write_data[65535]; +static UCHAR read_data[65535]; +#ifdef FX_ENABLE_FAULT_TOLERANT +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/* Define thread prototypes. */ + +void filex_file_name_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_file_name_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +ULONG i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: File name test........................................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 512, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Initialize the data for write. */ + for (i = 0; i < sizeof(write_data); i++) + { + write_data[i] = (UCHAR)i; + read_data[i] = 0; + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + return_if_fail( status == FX_SUCCESS); + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + /* Set BIT3 to modify the filename to lowercase. */ + my_file.fx_file_dir_entry.fx_dir_entry_reserved = 0x08; + + /* Write to the file. */ + status = fx_file_write(&my_file, write_data, sizeof(write_data)); + return_if_fail( status == FX_SUCCESS); + + /* Close the file. */ + status = fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + /* Close the media. */ + fx_media_close(&ram_disk); + + /* Verify the filename is upper-case. */ + return_if_fail( memcmp(ram_disk_memory + 1024, "TEST TXT", 4) == 0); + + /* Open the media. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Open the file as lowercase name. */ + status = fx_file_open(&ram_disk, &my_file, "test.txt", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + /* Write to the file. */ + status = fx_file_write(&my_file, write_data, sizeof(write_data)); + return_if_fail( status == FX_SUCCESS); + + /* Close the file. */ + status = fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + /* Reopen the media. */ + fx_media_close(&ram_disk); + + /* Verify the filename is upper-case. */ + return_if_fail( memcmp(ram_disk_memory + 1024, "TEST TXT", 4) == 0); + + /* Open the media. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Open the file as lowercase name. */ + status = fx_file_open(&ram_disk, &my_file, "test.txt", FX_OPEN_FOR_READ); + return_if_fail( status == FX_SUCCESS); + + /* Read from the file. */ + status = fx_file_read(&my_file, (void *) read_data, sizeof(read_data), &actual); + return_if_fail( status == FX_SUCCESS); + return_if_fail( actual == sizeof(write_data)); + return_if_fail( memcmp(read_data, write_data, actual) == 0); + + /* Close the file. */ + status = fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + /* Reopen the media. */ + fx_media_close(&ram_disk); + + printf("SUCCESS!\n"); + test_control_return(0); +} + diff --git a/test/regression_test/filex_file_naming_test.c b/test/regression_test/filex_file_naming_test.c new file mode 100644 index 0000000..beffd14 --- /dev/null +++ b/test/regression_test/filex_file_naming_test.c @@ -0,0 +1,477 @@ +/* This FileX test concentrates on the basic file naming operations. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include +#include "fx_ram_driver_test.h" + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 16*128 + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + + +/* Define thread prototypes. */ + +void filex_file_naming_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_file_naming_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +CHAR *path_ptr; +CHAR special_name[10]; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: File naming test......................................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(2); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(3); + } + + /* Get the current path. This should be / or NULL. */ + status = fx_directory_default_get(&ram_disk, &path_ptr); + + /* Check the status. */ + if ((status != FX_SUCCESS) || (path_ptr[0])) + { + + /* Error getting the path. Return to caller. */ + printf("ERROR!\n"); + test_control_return(4); + } + + /* Create a series of directories... */ + status = fx_directory_create(&ram_disk, "A0"); + status += fx_directory_create(&ram_disk, ".b0"); + status += fx_directory_create(&ram_disk, "C0"); + status += fx_directory_create(&ram_disk, ".d0.dir"); + status += fx_file_create(&ram_disk, ".FOO.BAR"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(4); + } + + /* Attempt to create the same directory again. */ + status = fx_directory_create(&ram_disk, "/a0"); + + /* Check for errors... */ + if (status != FX_ALREADY_CREATED) + { + + /* Error creating same directory twice. Return to caller. */ + printf("ERROR!\n"); + test_control_return(5); + } + + /* Create files with the E5 character. */ + special_name[0] = (CHAR)0xE5; + special_name[1] = 0x2E; + special_name[2] = 0x54; + special_name[3] = 0x58; + special_name[4] = 0x54; + special_name[5] = 0; + status = fx_file_create(&ram_disk, special_name); + status += fx_media_flush(&ram_disk); + status += fx_file_delete(&ram_disk, special_name); + + /* Check for errors. */ + if (status != FX_SUCCESS) + { + + /* Error creating special name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(5); + } + + /* Create another special file name. */ + special_name[0] = (CHAR)0xE5; + special_name[1] = 0x31; + special_name[2] = 0x2E; + special_name[3] = 0x54; + special_name[4] = 0x58; + special_name[5] = 0x54; + special_name[6] = 0; + status = fx_file_create(&ram_disk, special_name); + status += fx_media_flush(&ram_disk); + status += fx_file_delete(&ram_disk, special_name); + + /* Check for errors. */ + if (status != FX_SUCCESS) + { + + /* Error creating special name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(6); + } + + /* Create the next level of sub-directories.... with the interesting names... */ + status = fx_directory_default_set(&ram_disk, "/A0"); + + status = fx_file_create(&ram_disk, ".a.a.a."); + status += fx_file_create(&ram_disk, "this...ismytestfile"); + status += fx_file_create(&ram_disk, "this ... is my test file"); + status += fx_file_create(&ram_disk, "ThisIsTheFirstVeryLongFileNameForThisTestABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZfirstfile11111111111111110000000000000000000000000000000000000000000000000000000"); + status += fx_directory_create(&ram_disk, " c"); + status += fx_directory_create(&ram_disk, " c/ e"); + status += fx_directory_create(&ram_disk, " c/ e/ f"); + status += fx_file_create(&ram_disk, "/A0 / c/ e/ f/test.txt"); + status += fx_file_create(&ram_disk, "/.d0.dir/test.txt"); + status += fx_file_create(&ram_disk, "/.b0/TEST.TXT"); + status += fx_media_flush(&ram_disk); + + /* Check for errors. */ + if (status != FX_SUCCESS) + { + + /* Error creating interesting file names. Return to caller. */ + printf("ERROR!\n"); + test_control_return(7); + } + + /* Now attempt to delete the files in each of these sub-directories. */ + status = fx_file_delete(&ram_disk, "/A0/.a.a.a."); + status += fx_file_delete(&ram_disk, "/A0/this...ismytestfile"); + status += fx_file_delete(&ram_disk, "/A0/this ... is my test file"); + status += fx_file_delete(&ram_disk, "/A0/ThisIsTheFirstVeryLongFileNameForThisTestABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZfirstfile11111111111111110000000000000000000000000000000000000000000000000000000"); + status += fx_file_delete(&ram_disk, "/A0 / c/ e/ f/test.txt"); + status += fx_directory_delete(&ram_disk, "/A0 /c /e/ f"); + status += fx_directory_delete(&ram_disk, "/A0 /c /e"); + status += fx_directory_delete(&ram_disk, "/A0 /c "); + status += fx_file_delete(&ram_disk, "/.d0.dir/test.txt"); + status += fx_file_delete(&ram_disk, "/.b0/TEST.TXT"); + status += fx_directory_delete(&ram_disk, "/A0"); + status += fx_directory_delete(&ram_disk, "/ .b0"); + status += fx_directory_delete(&ram_disk, "/C0 "); + status += fx_directory_delete(&ram_disk, "/.d0.dir"); + status += fx_file_delete(&ram_disk, "/.FOO.BAR"); + status += fx_media_flush(&ram_disk); + + /* Check for errors. */ + if (status != FX_SUCCESS) + { + + /* Error deleting test files in the interesting sub-directories. Return to caller. */ + printf("ERROR!\n"); + test_control_return(8); + } + + /* Set default to root directory. */ + status = fx_directory_default_set(&ram_disk, "/"); + + /* Flush the media... should be empty at this point. */ + status += fx_media_flush(&ram_disk); + + /* Now fill the media's root directory until we get an error... We will use 8.3 names + to attempt to create 32 names. */ + status += fx_file_create(&ram_disk, "A01"); + status += fx_file_create(&ram_disk, "A02"); + status += fx_file_create(&ram_disk, "A03"); + status += fx_file_create(&ram_disk, "A04"); + status += fx_file_create(&ram_disk, "A05"); + status += fx_file_create(&ram_disk, "A06"); + status += fx_file_create(&ram_disk, "A07"); + status += fx_file_create(&ram_disk, "A08"); + status += fx_file_create(&ram_disk, "A09"); + status += fx_file_create(&ram_disk, "A10"); + status += fx_file_create(&ram_disk, "A11"); + status += fx_file_create(&ram_disk, "A12"); + status += fx_file_create(&ram_disk, "A13"); + status += fx_file_create(&ram_disk, "A14"); + status += fx_file_create(&ram_disk, "A15"); + status += fx_file_create(&ram_disk, "A16"); + status += fx_file_create(&ram_disk, "A17"); + status += fx_file_create(&ram_disk, "A18"); + status += fx_file_create(&ram_disk, "A19"); + status += fx_file_create(&ram_disk, "A20"); + status += fx_file_create(&ram_disk, "A21"); + status += fx_file_create(&ram_disk, "A22"); + status += fx_file_create(&ram_disk, "A23"); + status += fx_file_create(&ram_disk, "A24"); + status += fx_file_create(&ram_disk, "A25"); + status += fx_file_create(&ram_disk, "A26"); + status += fx_file_create(&ram_disk, "A27"); + status += fx_file_create(&ram_disk, "A28"); + status += fx_file_create(&ram_disk, "A29"); + status += fx_file_create(&ram_disk, "A30"); + status += fx_file_create(&ram_disk, "A31"); + status += fx_file_create(&ram_disk, "A32"); + + fx_media_flush(&ram_disk); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating files. Return to caller. */ + printf("ERROR!\n"); + test_control_return(9); + } + + /* Attempt to create 33rd name... this should fail. */ + status = fx_file_create(&ram_disk, "A33"); + + /* Check for errors... */ + if (status != FX_NO_MORE_SPACE) + { + + /* Error creating files. Return to caller. */ + printf("ERROR!\n"); + test_control_return(10); + } + + /* Flush the media. */ + status = fx_media_flush(&ram_disk); + + /* Now delete all the names. */ + status += fx_file_delete(&ram_disk, "A01"); + status += fx_file_delete(&ram_disk, "A02"); + status += fx_file_delete(&ram_disk, "A03"); + status += fx_file_delete(&ram_disk, "A04"); + status += fx_file_delete(&ram_disk, "A05"); + status += fx_file_delete(&ram_disk, "A06"); + status += fx_file_delete(&ram_disk, "A07"); + status += fx_file_delete(&ram_disk, "A08"); + status += fx_file_delete(&ram_disk, "A09"); + status += fx_file_delete(&ram_disk, "A10"); + status += fx_file_delete(&ram_disk, "A11"); + status += fx_file_delete(&ram_disk, "A12"); + status += fx_file_delete(&ram_disk, "A13"); + status += fx_file_delete(&ram_disk, "A14"); + status += fx_file_delete(&ram_disk, "A15"); + status += fx_file_delete(&ram_disk, "A16"); + status += fx_file_delete(&ram_disk, "A17"); + status += fx_file_delete(&ram_disk, "A18"); + status += fx_file_delete(&ram_disk, "A19"); + status += fx_file_delete(&ram_disk, "A20"); + status += fx_file_delete(&ram_disk, "A21"); + status += fx_file_delete(&ram_disk, "A22"); + status += fx_file_delete(&ram_disk, "A23"); + status += fx_file_delete(&ram_disk, "A24"); + status += fx_file_delete(&ram_disk, "A25"); + status += fx_file_delete(&ram_disk, "A26"); + status += fx_file_delete(&ram_disk, "A27"); + status += fx_file_delete(&ram_disk, "A28"); + status += fx_file_delete(&ram_disk, "A29"); + status += fx_file_delete(&ram_disk, "A30"); + status += fx_file_delete(&ram_disk, "A31"); + status += fx_file_delete(&ram_disk, "A32"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error deleting files. Return to caller. */ + printf("ERROR!\n"); + test_control_return(11); + } + + /* Flush the media. */ + status = fx_media_flush(&ram_disk); + + /* Now do the same thing, except with 2 entry long names. */ + status += fx_file_create(&ram_disk, "b01"); + status += fx_file_create(&ram_disk, "b02"); + status += fx_file_create(&ram_disk, "b03"); + status += fx_file_create(&ram_disk, "b04"); + status += fx_file_create(&ram_disk, "b05"); + status += fx_file_create(&ram_disk, "b06"); + status += fx_file_create(&ram_disk, "b07"); + status += fx_file_create(&ram_disk, "b08"); + status += fx_file_create(&ram_disk, "b09"); + status += fx_file_create(&ram_disk, "b10"); + status += fx_file_create(&ram_disk, "b11"); + status += fx_file_create(&ram_disk, "b12"); + status += fx_file_create(&ram_disk, "b13"); + status += fx_file_create(&ram_disk, "b14"); + status += fx_file_create(&ram_disk, "b15"); + status += fx_file_create(&ram_disk, "b16"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error creating files. Return to caller. */ + printf("ERROR!\n"); + test_control_return(12); + } + + /* Attempt to create 17th long name... this should fail. */ + status = fx_file_create(&ram_disk, "b17"); + + /* Check for errors... */ + if (status != FX_NO_MORE_SPACE) + { + + /* Error creating files. Return to caller. */ + printf("ERROR!\n"); + test_control_return(13); + } + + /* Flush the media. */ + status = fx_media_flush(&ram_disk); + + /* Now delete all the names. */ + status += fx_file_delete(&ram_disk, "b01"); + status += fx_file_delete(&ram_disk, "b02"); + status += fx_file_delete(&ram_disk, "b03"); + status += fx_file_delete(&ram_disk, "b04"); + status += fx_file_delete(&ram_disk, "b05"); + status += fx_file_delete(&ram_disk, "b06"); + status += fx_file_delete(&ram_disk, "b07"); + status += fx_file_delete(&ram_disk, "b08"); + status += fx_file_delete(&ram_disk, "b09"); + status += fx_file_delete(&ram_disk, "b10"); + status += fx_file_delete(&ram_disk, "b11"); + status += fx_file_delete(&ram_disk, "b12"); + status += fx_file_delete(&ram_disk, "b13"); + status += fx_file_delete(&ram_disk, "b14"); + status += fx_file_delete(&ram_disk, "b15"); + status += fx_file_delete(&ram_disk, "b16"); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error deleting files. Return to caller. */ + printf("ERROR!\n"); + test_control_return(14); + } + + /* Create a file with non alphabet character and space. */ + status = fx_file_create(&ram_disk, "TEST.T~ "); + + /* Now delete this file. */ + status += fx_file_delete(&ram_disk, "TEST.T~ "); + + /* Check for errors... */ + if (status != FX_SUCCESS) + { + + /* Error deleting files. Return to caller. */ + printf("ERROR!\n"); + test_control_return(14); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + else + { + + printf("SUCCESS!\n"); + test_control_return(0); + } +} + diff --git a/test/regression_test/filex_file_read_write_test.c b/test/regression_test/filex_file_read_write_test.c new file mode 100644 index 0000000..7e69263 --- /dev/null +++ b/test/regression_test/filex_file_read_write_test.c @@ -0,0 +1,2586 @@ +/* This FileX test concentrates on the file read/write operation. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_ram_driver_test.h" +#include + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 128*128 + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static FX_FILE my_file1; +static FX_FILE my_file2; +static FX_FILE my_file3; +static FX_FILE my_file4; +static FX_FILE read_only; +static ULONG my_buffer[128]; +static UCHAR fat_buffer[128]; +static UCHAR buffer[128*3]; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + + +/* Define thread prototypes. */ + +void filex_file_read_write_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_file_read_write_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +ULONG actual1; +ULONG actual2; +ULONG temp; +ULONG temp1; +ULONG read_value; +ULONG write_value; +ULONG available_bytes; +ULONG i, j; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: File read/write test..................................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 511, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(2); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_create(&ram_disk, "READ_ONLY.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* try to write to a file before it has been opened */ + status = fx_file_write(&my_file, (void *) &write_value, sizeof(ULONG)); + if (status != FX_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &read_only, "READ_ONLY.TXT", FX_OPEN_FOR_READ); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* try to write to a file while it is write protected */ + ram_disk.fx_media_driver_write_protect = FX_TRUE; + status = fx_file_write(&my_file, (void *) &write_value, sizeof(ULONG)); + if (status != FX_WRITE_PROTECT) + { + printf("ERROR!\n"); + test_control_return(23); + } + ram_disk.fx_media_driver_write_protect = FX_FALSE; + + /* try to write to a file that is not opened for writing */ + status = fx_file_write(&read_only, (void *) &write_value, sizeof(ULONG)); + if (status != FX_ACCESS_ERROR) + { + printf("ERROR!\n"); + test_control_return(23); + } + + ram_disk.fx_media_bytes_per_sector = 0; + + /* Try to write to a file when media is corrupted. */ + status = fx_file_write(&my_file, (void*)& write_value, sizeof(ULONG)); + if (status != FX_MEDIA_INVALID) + { + printf("ERROR!\n"); + test_control_return(23); + } + + ram_disk.fx_media_bytes_per_sector = 128; + + /* Pickup the available bytes in the media. */ + status = fx_media_space_available(&ram_disk, &available_bytes); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (available_bytes < sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(5); + } + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + /* send null pointer to generate an error */ + status = fx_file_write(FX_NULL, (void *) &write_value, 0); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(11); + } +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Loop to write successive bytes out to the file.... to fill the media! */ + i = 0; + write_value = 0; + while (i < available_bytes) + { + + /* Write 4 bytes to the file. */ + status = fx_file_write(&my_file, (void *) &write_value, sizeof(ULONG)); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Increment byte count. */ + i = i + sizeof(ULONG); + + /* Increment write value. */ + write_value++; + } + + /* Pickup the available bytes in the media again. */ + status = fx_media_space_available(&ram_disk, &i); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (i != 0)) + { + + printf("ERROR!\n"); + test_control_return(7); + } + +#ifndef FX_DISABLE_CACHE + /* At this point, we should invalidate the (which also flushes the cache) media to ensure that all + dirty sectors are written. */ + status = fx_media_cache_invalidate(&ram_disk); + + /* Check for flush errors. */ + if ((status != FX_SUCCESS) || (ram_disk.fx_media_sector_cache_dirty_count)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* See if any sectors are still valid in the cache. */ + for (i = 0; i < FX_MAX_SECTOR_CACHE; i++) + { + + /* Determine if this cache entry is still valid. */ + if (ram_disk.fx_media_sector_cache[i].fx_cached_sector_valid) + { + + printf("ERROR!\n"); + test_control_return(81); + } + } +#endif + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + /* send null pointer to generate an error */ + status = fx_file_seek(FX_NULL, 0); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(11); + } +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Now read in all the bytes again to make sure the file contents are really there. */ + i = 0; + read_value = 0; + while (i < available_bytes) + { + + /* Read 4 bytes from the file. */ + status = fx_file_read(&my_file, (void *) &read_value, sizeof(ULONG), &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 4) || (read_value != i/4)) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Increment byte count. */ + i = i + sizeof(ULONG); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Check the media close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Reformat the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 511, // Total sectors (ensure clusters divisible by 4) + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Open the ram_disk, but do so to ensure non-hashed algorithm is used by supplying CACHE_SIZE-1. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE-1); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(14); + } + + /* Read 4 bytes from the file. */ + status = fx_file_read(&my_file, (void *) &read_value, sizeof(ULONG), &actual); + if (status != FX_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(23); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Pickup the available bytes in the media. */ + status = fx_media_space_available(&ram_disk, &available_bytes); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (available_bytes < sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Loop to write successive bytes out to the file.... to fill the media! */ + i = 0; + write_value = 0; + while (i < available_bytes) + { + + /* Write 4 bytes to the file. */ + status = fx_file_write(&my_file, (void *) &write_value, sizeof(ULONG)); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Increment byte count. */ + i = i + sizeof(ULONG); + + /* Increment write value. */ + write_value++; + } + + /* Pickup the available bytes in the media again. */ + status = fx_media_space_available(&ram_disk, &i); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (i != 0)) + { + + printf("ERROR!\n"); + test_control_return(19); + } + +#ifndef FX_DISABLE_CACHE + /* At this point, we should invalidate the media to ensure that all + dirty sectors are written. */ + status = fx_media_cache_invalidate(&ram_disk); + + /* Check for flush errors. */ + if ((status != FX_SUCCESS) || (ram_disk.fx_media_sector_cache_dirty_count)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* See if any sectors are still valid in the cache. */ + for (i = 0; i < ram_disk.fx_media_sector_cache_size; i++) + { + + /* Determine if this cache entry is still valid. */ + if (ram_disk.fx_media_sector_cache[i].fx_cached_sector_valid) + { + + printf("ERROR!\n"); + test_control_return(81); + } + } +#endif + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(22); + } + + /* Now read in all the bytes again to make sure the file contents are really there. */ + i = 0; + read_value = 0; + while (i < available_bytes) + { + + /* Read 4 bytes from the file. */ + status = fx_file_read(&my_file, (void *) &read_value, sizeof(ULONG), &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 4) || (read_value != i/4)) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Increment byte count. */ + i = i + sizeof(ULONG); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(25); + } + + /* Open the file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_READ); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + /* Error opening file. Return to caller. */ + printf("ERROR!\n"); + test_control_return(26); + } + + /* Now read in all the bytes again to make sure the file contents are really there. */ + i = 0; + read_value = 0; + while (i < available_bytes) + { + + /* Read as much as 4 sectors full of bytes from the file. */ + status = fx_file_read(&my_file, (void *) my_buffer, sizeof(my_buffer), &actual); + + /* Check the file read status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Determine if the contents are what is expected. */ + for (j = 0; j < actual/sizeof(ULONG); j++) + { + + /* Determine if the buffer is correct. */ + if (read_value != my_buffer[j]) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + read_value++; + } + + /* Increment byte count. */ + i = i + actual; + } + + /* Close the file. */ + status = fx_file_close(&my_file); + + /* Open the file again but with the fast option. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_READ_FAST); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + /* Error opening file. Return to caller. */ + printf("ERROR!\n"); + test_control_return(29); + } + + /* Now read in all the bytes again to make sure the file contents are really there. */ + i = 0; + read_value = 0; + while (i < available_bytes) + { + + /* Read as much as 4 sectors full of bytes from the file. */ + status = fx_file_read(&my_file, (void *) my_buffer, sizeof(my_buffer), &actual); + + /* Check the file read status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Determine if the contents are what is expected. */ + for (j = 0; j < actual/sizeof(ULONG); j++) + { + + /* Determine if the buffer is correct. */ + if (read_value != my_buffer[j]) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + read_value++; + } + + /* Increment byte count. */ + i = i + actual; + } + + /* Close the file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Delete the file. */ + status = fx_file_delete(&ram_disk, "TEST.TXT"); + + /* Check the file delete status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Write the file in blocks and then read ulong at a time. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Now write the big buffer at the same time. */ + /* Now read in all the bytes again to make sure the file contents are really there. */ + i = 0; + read_value = 0; + while (i < available_bytes) + { + + /* Build the buffer. */ + for (j = 0; j < actual/sizeof(ULONG); j++) + { + + /* Build the buffer entry. */ + my_buffer[j] = read_value++; + } + + + /* Write 4 sectors at a time. */ + status = fx_file_write(&my_file, (void *) my_buffer, sizeof(my_buffer)); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Increment byte count. */ + i = i + sizeof(my_buffer); + } + + /* At this point, seek to the beginning of the file and read every 4 bytes. */ + status = fx_file_seek(&my_file, 0); + + i = 0; + read_value = 0; + while (i < available_bytes) + { + + /* Read 4 bytes from the file. */ + status = fx_file_read(&my_file, (void *) &read_value, sizeof(ULONG), &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 4) || (read_value != i/4)) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Increment byte count. */ + i = i + sizeof(ULONG); + } + + /* Close the file. */ + status += fx_file_close(&my_file); + + /* Close the media. */ + status += fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(37); + } + + /* Test the update of files open for reading while the write is happening. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 6000, // Total sectors - FAT16 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(38); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(39); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_create(&ram_disk, "TEST1.TXT"); + status += fx_file_create(&ram_disk, "TEST2.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_READ); + status += fx_file_open(&ram_disk, &my_file1, "TEST.TXT", FX_OPEN_FOR_READ); + status += fx_file_open(&ram_disk, &my_file2, "TEST.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file3, "TEST1.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file4, "TEST2.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file4, my_buffer, 128); + + /* Now loop through the maximum of clusters to fill and read the file. */ + i = 0; + while (ram_disk.fx_media_available_clusters) + { + + if (i == 4) + { + status += fx_file_close(&my_file4); + status += fx_file_delete(&ram_disk, "TEST2.TXT"); + } + + /* Write to the writable file. */ + status += fx_file_write(&my_file2, my_buffer, 128); + + /* Read the data in the file from the other 2 file handles. */ + status += fx_file_read(&my_file1, my_buffer, 128, &actual1); + status += fx_file_read(&my_file, my_buffer, 128, &actual2); + + /* Check the status. */ + if (status) + break; + i++; + } + + /* Close the files and the media. */ + status += fx_file_close(&my_file); + status += fx_file_close(&my_file1); + status += fx_file_close(&my_file2); + status += fx_media_close(&ram_disk); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(40); + } + + /* Test the update of files open for reading while the write is happening with multiple sectors per cluster. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 6000, // Total sectors - FAT16 + 128, // Sector size + 2, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(41); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(42); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_create(&ram_disk, "TEST1.TXT"); + status += fx_file_create(&ram_disk, "TEST2.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_READ); + status += fx_file_open(&ram_disk, &my_file1, "TEST.TXT", FX_OPEN_FOR_READ); + status += fx_file_open(&ram_disk, &my_file2, "TEST.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file3, "TEST1.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file4, "TEST2.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file4, my_buffer, 128); + + /* Now loop through the maximum of clusters to fill and read the file. */ + i = 0; + while (ram_disk.fx_media_available_clusters) + { + + if (i == 4) + { + status += fx_file_close(&my_file4); + status += fx_file_delete(&ram_disk, "TEST2.TXT"); + } + + /* Write to the writable file. */ + status += fx_file_write(&my_file2, my_buffer, 128); + status += fx_file_write(&my_file2, my_buffer, 128); + + /* Read the data in the file from the other 2 file handles. */ + status += fx_file_read(&my_file1, my_buffer, 128, &actual1); + status += fx_file_read(&my_file, my_buffer, 128, &actual2); + status += fx_file_read(&my_file1, my_buffer, 128, &actual1); + status += fx_file_read(&my_file, my_buffer, 128, &actual2); + + /* Check the status. */ + if (status) + break; + i++; + } + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(43); + } + + /* Now force a wrap of the FAT search. */ + ram_disk.fx_media_available_clusters++; + + status = fx_file_write(&my_file2, my_buffer, 128); + + /* Did we get an error? */ + if (status != FX_NO_MORE_SPACE) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(44); + } + + /* Close the files and the media. */ + status = fx_file_close(&my_file); + status += fx_file_close(&my_file1); + status += fx_file_close(&my_file2); + status += fx_media_close(&ram_disk); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(43); + } + + /* Test the update of files open for reading while the write is happening - with random errors! */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 6000, // Total sectors - FAT16 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(44); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(45); + } + + /* Create a file called TEST.TXT in the root directory. */ + fx_file_create(&ram_disk, "TEST.TXT"); + fx_file_create(&ram_disk, "TEST1.TXT"); + fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_READ); + fx_file_open(&ram_disk, &my_file1, "TEST.TXT", FX_OPEN_FOR_READ); + fx_file_open(&ram_disk, &my_file2, "TEST.TXT", FX_OPEN_FOR_WRITE); + fx_file_open(&ram_disk, &my_file3, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Now loop through the maximum of clusters to fill and read the file. */ + i = 0; + while (ram_disk.fx_media_available_clusters) + { + + /* Flush the media. */ + fx_media_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + + /* Setup the random I/O Error. */ + _fx_ram_driver_io_error_request = (rand() & 4); + if (_fx_ram_driver_io_error_request == 0) + _fx_ram_driver_io_error_request = 1; + _fx_utility_fat_entry_write_error_request = _fx_ram_driver_io_error_request; + _fx_utility_fat_entry_read_error_request = _fx_ram_driver_io_error_request; + _fx_utility_logical_sector_write_error_request = _fx_ram_driver_io_error_request; + _fx_utility_logical_sector_read_error_request = _fx_ram_driver_io_error_request; + + /* Write to the writable file. */ + if (i & 1) + { + fx_file_write(&my_file2, my_buffer, 128); + } + else + { + fx_file_write(&my_file2, my_buffer, 32); + fx_file_write(&my_file2, my_buffer, 32); + fx_file_write(&my_file2, my_buffer, 32); + fx_file_write(&my_file2, my_buffer, 32); + } + + /* Setup the random I/O Error. */ + _fx_ram_driver_io_error_request = (rand() & 4); + if (_fx_ram_driver_io_error_request == 0) + _fx_ram_driver_io_error_request = 1; + _fx_utility_fat_entry_write_error_request = _fx_ram_driver_io_error_request; + _fx_utility_fat_entry_read_error_request = _fx_ram_driver_io_error_request; + _fx_utility_logical_sector_write_error_request = _fx_ram_driver_io_error_request; + _fx_utility_logical_sector_read_error_request = _fx_ram_driver_io_error_request; + + /* Read the data in the file from the other 2 file handles. */ + if (i & 1) + { + fx_file_read(&my_file1, my_buffer, 128, &actual1); + } + else + { + fx_file_read(&my_file1, my_buffer, 32, &actual1); + fx_file_read(&my_file1, my_buffer, 32, &actual1); + fx_file_read(&my_file1, my_buffer, 32, &actual1); + fx_file_read(&my_file1, my_buffer, 32, &actual1); + } + + /* Setup the random I/O Error. */ + _fx_ram_driver_io_error_request = (rand() & 4); + if (_fx_ram_driver_io_error_request == 0) + _fx_ram_driver_io_error_request = 1; + _fx_utility_fat_entry_write_error_request = _fx_ram_driver_io_error_request; + _fx_utility_fat_entry_read_error_request = _fx_ram_driver_io_error_request; + _fx_utility_logical_sector_write_error_request = _fx_ram_driver_io_error_request; + _fx_utility_logical_sector_read_error_request = _fx_ram_driver_io_error_request; + + fx_file_read(&my_file, my_buffer, 128, &actual2); + + _fx_ram_driver_io_error_request = 0; + _fx_utility_fat_entry_write_error_request = _fx_ram_driver_io_error_request; + _fx_utility_fat_entry_read_error_request = _fx_ram_driver_io_error_request; + _fx_utility_logical_sector_write_error_request = _fx_ram_driver_io_error_request; + _fx_utility_logical_sector_read_error_request = _fx_ram_driver_io_error_request; + + i++; + } + + /* Close the files and the media. */ + fx_file_close(&my_file); + fx_file_close(&my_file1); + fx_file_close(&my_file2); + fx_media_close(&ram_disk); + + + /* Test the write of partial cluster and direct I/O over non-contigous clusters. */ + + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 6000, // Total sectors - FAT16 + 128, // Sector size + 3, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(46); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(47); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_create(&ram_disk, "TEST1.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file4,"TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Write a small amount of data first to cause an unalignment and partial sector write. */ + status += fx_file_write(&my_file, my_buffer, 32); + status += fx_file_write(&my_file4, my_buffer, 32); + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_write(&my_file, my_buffer, 128*4); + + /* Now loop through the maximum of clusters to fill up the rest of the FAT table. */ + i = 0; + while (ram_disk.fx_media_available_clusters) + { + + + /* Write to the writable file. */ + status += fx_file_write(&my_file, my_buffer, 32); + status += fx_file_write(&my_file4, my_buffer, 32); + i++; + } + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(48); + } + + /* Now read the file in as big of chunks as possible to excersise the partial read paths. */ + status = fx_file_seek(&my_file, 0); + do + { + /* Read chunks of the file. */ + status = fx_file_read(&my_file, my_buffer, 128*4, &actual); + + } while (status != FX_END_OF_FILE); + + + /* Now delete the second file to leave holes in the FAT table. */ + status += fx_file_close(&my_file4); + status += fx_file_delete(&ram_disk, "TEST1.TXT"); + + /* Finally, perform a direct write with that can't be done with contigous clusters. */ + status = fx_file_write(&my_file, my_buffer, 128*4); + + /* Did we get an error? */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(49); + } + + /* Close the files and the media. */ + status = fx_file_close(&my_file); + status += fx_media_close(&ram_disk); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(50); + } + + + /* Test the write of partial cluster and direct I/O when the FAT chain is broken. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 14000, // Total sectors - FAT16 + 128, // Sector size + 2, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(51); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(52); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Write a data first to build a FAT chain. */ + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_seek(&my_file, 0); + status += fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Read the first FAT sector. */ + status += fx_media_read(&ram_disk, 1, (VOID *) fat_buffer); + + /* Add a FAT entry randomly in the FAT table. */ + fat_buffer[6] = 1; + fat_buffer[7] = 0; + fat_buffer[8] = 1; + fat_buffer[9] = 0; + + /* Write the FAT corruption out. */ + status += fx_media_write(&ram_disk, 1, (VOID *) fat_buffer); + + /* See if we are okay. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(53); + } + + /* Now attempt to write a partial sector. */ + status = fx_file_write(&my_file, my_buffer, 128*4); + + /* See if we get the file corrupt error. */ + if (status != FX_FILE_CORRUPT) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(54); + } + + status = fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_write(&my_file, my_buffer, 128*4); + + /* See if we get the file corrupt error. */ + if (status != FX_FILE_CORRUPT) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(55); + } + + + /* Close the files and the media. */ + status = fx_file_close(&my_file); + status += fx_media_close(&ram_disk); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(56); + } + + /* Test the maximum write size. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 1000, // Total sectors - FAT12 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(57); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(58); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Write a data first to build a FAT chain. */ + status += fx_file_write(&my_file, my_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(59); + } + + /* Now manually setup the file offset to force an error. */ + temp = (ULONG)my_file.fx_file_current_file_offset; + my_file.fx_file_current_file_offset = 0xFFFFFFF0; + + /* Write a data first to force the overflow. */ + status = fx_file_write(&my_file, my_buffer, 32); + + /* Check the status. */ + if (status != FX_NO_MORE_SPACE) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(60); + } + + /* Also save, adjust the current available so that one new cluster will be written. */ + my_file.fx_file_current_available_size = 0xFFFFFFF0; + temp1 = (ULONG)my_file.fx_file_current_available_size; + + /* Now attempt to write a massive file to exercise the maximum available file size logic. */ + status = fx_file_write(&my_file, my_buffer, 1); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(61); + } + + /* Now restore the original offset. */ + my_file.fx_file_current_file_offset = temp; + my_file.fx_file_current_available_size = temp1; + + /* Close the file. */ + status = fx_file_close(&my_file); + status += fx_media_close(&ram_disk); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(62); + } + + + /* Test the remaining I/O error paths in file write. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 1000, // Total sectors - FAT12 + 128, // Sector size + 3, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(63); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(64); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(65); + } + + /* Write a data first to build a FAT chain, with an I/O error on liking the FAT chain. */ + _fx_utility_fat_entry_write_error_request = 1; + status = fx_file_write(&my_file, my_buffer, 128*4); + _fx_utility_fat_entry_write_error_request = 0; + + /* Check the status. */ + if (status != FX_IO_ERROR) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(66); + } + + /* Close the file. */ + status = fx_file_close(&my_file); + status += fx_media_close(&ram_disk); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(67); + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 1000, // Total sectors - FAT12 + 128, // Sector size + 3, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(68); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(69); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(70); + } + + /* Write a data first to build a FAT chain, with an I/O error on writing EOF at the end of the FAT chain. */ + _fx_utility_fat_entry_write_error_request = 2; + status = fx_file_write(&my_file, my_buffer, 128*4); + _fx_utility_fat_entry_write_error_request = 0; + + /* Check the status. */ + if (status != FX_IO_ERROR) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(71); + } + + /* Close the file. */ + status = fx_file_close(&my_file); + status += fx_media_close(&ram_disk); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(72); + } + + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 1000, // Total sectors - FAT12 + 128, // Sector size + 3, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(73); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(74); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, my_buffer, 128*4); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(75); + } + + /* Write a data first to build a FAT chain, with an I/O error on linking the new FAT chain to existing FAT chain. */ + _fx_utility_fat_entry_write_error_request = 2; + status = fx_file_write(&my_file, my_buffer, 128*4); + _fx_utility_fat_entry_write_error_request = 0; + + /* Check the status. */ + if (status != FX_IO_ERROR) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(76); + } + + /* Close the file. */ + status = fx_file_close(&my_file); + status += fx_media_close(&ram_disk); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(77); + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 1000, // Total sectors - FAT12 + 128, // Sector size + 3, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(78); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(79); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, my_buffer, 128+64); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(80); + } + + /* Write data that will require another cluster, even though we have only written one sector of the first allocated cluster. */ + status = fx_file_write(&my_file, my_buffer, 128*4); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(81); + } + + /* Close the file. */ + status = fx_file_close(&my_file); + status += fx_media_close(&ram_disk); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(82); + } + + /* Test the read of partial cluster and direct I/O when the FAT chain is broken. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 14000, // Total sectors - FAT16 + 128, // Sector size + 2, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(83); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(84); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Write a data first to build a FAT chain. */ + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_seek(&my_file, 0); + status += fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Read the first FAT sector. */ + status += fx_media_read(&ram_disk, 1, (VOID *) fat_buffer); + + /* Add a FAT entry randomly in the FAT table. */ + fat_buffer[6] = 1; + fat_buffer[7] = 0; + fat_buffer[8] = 1; + fat_buffer[9] = 0; + + /* Write the FAT corruption out. */ + status += fx_media_write(&ram_disk, 1, (VOID *) fat_buffer); + + /* See if we are okay. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(85); + } + + /* Read the file to get the file corruption error. */ + status = fx_file_read(&my_file, my_buffer, 128*4, &actual); + + /* See if the file is corrupted - it should be. */ + if (status != FX_FILE_CORRUPT) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(86); + } + + /* Close everything down. */ + fx_file_close(&my_file); + fx_media_abort(&ram_disk); + + /* Test the read of partial cluster and direct I/O when the FAT chain is broken - at the beginning of the FAT chain! */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 14000, // Total sectors - FAT16 + 128, // Sector size + 2, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(87); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(88); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Write a data first to build a FAT chain. */ + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_seek(&my_file, 0); + status += fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Read the first FAT sector. */ + status += fx_media_read(&ram_disk, 1, (VOID *) fat_buffer); + + /* Add a FAT entry randomly in the FAT table. */ + fat_buffer[4] = 1; + fat_buffer[5] = 0; + fat_buffer[6] = 1; + fat_buffer[7] = 0; + fat_buffer[8] = 1; + fat_buffer[9] = 0; + + /* Write the FAT corruption out. */ + status += fx_media_write(&ram_disk, 1, (VOID *) fat_buffer); + + /* See if we are okay. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(89); + } + + /* Read the file to get the file corruption error. */ + status = fx_file_read(&my_file, my_buffer, 128*4, &actual); + + /* See if the file is corrupted - it should be. */ + if (status != FX_FILE_CORRUPT) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(90); + } + + /* Close everything down. */ + fx_file_close(&my_file); + fx_media_abort(&ram_disk); + + + /* Test the read of partial cluster and direct I/O when the FAT chain is broken - at the beginning of the FAT chain and with FFs instead of 1! */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 14000, // Total sectors - FAT16 + 128, // Sector size + 2, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(91); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(92); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Write a data first to build a FAT chain. */ + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_seek(&my_file, 0); + status += fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Read the first FAT sector. */ + status += fx_media_read(&ram_disk, 1, (VOID *) fat_buffer); + + /* Add a FAT entry randomly in the FAT table. */ + fat_buffer[4] = 0xFF; + fat_buffer[5] = 0xFF; + fat_buffer[6] = 0xFF; + fat_buffer[7] = 0xFF; + fat_buffer[8] = 0xFF; + fat_buffer[9] = 0xFF; + + /* Write the FAT corruption out. */ + status += fx_media_write(&ram_disk, 1, (VOID *) fat_buffer); + + /* See if we are okay. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(93); + } + + /* Read the file to get the file corruption error. */ + status = fx_file_read(&my_file, my_buffer, 128*4, &actual); + + /* See if the file is corrupted - it should be. */ + if (status != FX_FILE_CORRUPT) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(94); + } + + /* Close everything down. */ + fx_file_close(&my_file); + fx_media_abort(&ram_disk); + + + /* Test the read with I/O FAT read error. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 14000, // Total sectors - FAT16 + 128, // Sector size + 2, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(95); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(96); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Write a data first to build a FAT chain. */ + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_seek(&my_file, 0); + status += fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Read the file to get the file IO error. */ + _fx_utility_fat_entry_read_error_request = 1; + status = fx_file_read(&my_file, my_buffer, 128*4, &actual); + _fx_utility_fat_entry_read_error_request = 0; + + /* See if the file is corrupted - it should be. */ + if (status != FX_IO_ERROR) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(97); + } + + /* Close everything down. */ + fx_file_close(&my_file); + fx_media_abort(&ram_disk); + + + /* Test the read with I/O logical sector read error. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 14000, // Total sectors - FAT16 + 128, // Sector size + 2, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(95); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(96); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Write a data first to build a FAT chain. */ + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_seek(&my_file, 0); + status += fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Read the file to get the file IO error. */ + _fx_ram_driver_io_error_request = 2; + status = fx_file_read(&my_file, my_buffer, 128*4, &actual); + _fx_ram_driver_io_error_request = 0; + + /* See if the file is corrupted - it should be. */ + if (status != FX_IO_ERROR) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(97); + } + + /* Close everything down. */ + fx_file_close(&my_file); + fx_media_abort(&ram_disk); + + + /* Test the file open with I/O read errors when walking the FAT chain. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 7000, // Total sectors - FAT16 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(98); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(99); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_create(&ram_disk, "TEST1.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file1, "TEST1.TXT", FX_OPEN_FOR_WRITE); + + /* Write a data first to build a FAT chain. */ + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_write(&my_file, my_buffer, 128*4); + status += fx_file_write(&my_file1, my_buffer, 128*4); + status += fx_file_write(&my_file, my_buffer, 128*4); + + /* Close the file. */ + status += fx_file_close(&my_file); + status += fx_file_close(&my_file1); + + /* Check status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(100); + } + + /* Now flush everything out. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + + /* Now open the file with an I/O error on the FAT entry read when walking the FAT chain. */ + _fx_utility_fat_entry_read_error_request = 1; + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + _fx_utility_fat_entry_read_error_request = 0; + + /* See if we got the I/O Error. */ + if (status != FX_IO_ERROR) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(101); + } + + /* Now break the FAT chain and try to open the file. */ + + /* Now flush everything out. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 1, (VOID *) fat_buffer); + + /* Add a FAT entry randomly in the FAT table. */ + fat_buffer[4] = 2; + + /* Write the FAT corruption out. */ + status += fx_media_write(&ram_disk, 1, (VOID *) fat_buffer); + + /* See if we are okay. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(102); + } + + /* Now open the file with a corrupted FAT entry which will cause an error when walking the FAT chain. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* See if we got the FAT chain. */ + if (status != FX_FAT_READ_ERROR) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(103); + } + + /* Now flush everything out. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 1, (VOID *) fat_buffer); + + /* Add a FAT entry randomly in the FAT table. */ + fat_buffer[4] = 0xF0; + fat_buffer[5] = 0xFF; + + /* Write the FAT corruption out. */ + status += fx_media_write(&ram_disk, 1, (VOID *) fat_buffer); + + /* See if we are okay. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(104); + } + + /* Now open the file with a corrupted FAT entry which will cause an error when walking the FAT chain. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* See if we got the file corrupt error. */ + if (status != FX_FILE_CORRUPT) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(105); + } + + /* Now flush everything out. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 1, (VOID *) fat_buffer); + + /* Fix the FAT chain. */ + fat_buffer[4] = 3; + fat_buffer[5] = 0; + + /* Write the FAT corruption out. */ + status += fx_media_write(&ram_disk, 1, (VOID *) fat_buffer); + + /* See if we are okay. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(106); + } + + /* Now test the total clusters check when traversing the FAT chain. */ + temp = ram_disk.fx_media_total_clusters; + ram_disk.fx_media_total_clusters = 4; + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + ram_disk.fx_media_total_clusters = temp; + + /* Check status. */ + if (status != FX_FAT_READ_ERROR) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(107); + } + + /* Now open the file with a good FAT chain. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(108); + } + + /* Now allocate another cluster to the end of the file. */ + status = fx_file_allocate(&my_file, 256); + status += fx_file_close(&my_file); + + /* Check status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(109); + } + + /* Now open the file again with an extra cluster at the end of the file... */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(110); + } + + /* Close the file again. */ + fx_file_close(&my_file); + + /* Now flush everything out. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 1, (VOID *) fat_buffer); + + /* Fix the FAT chain. */ + fat_buffer[4] = 0; + fat_buffer[5] = 0; + + /* Write the FAT corruption out. */ + status += fx_media_write(&ram_disk, 1, (VOID *) fat_buffer); + + /* See if we are okay. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(111); + } + + /* Now open the file again with an invalid last cluster at the end of the file... */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check status. */ + if (status != FX_FILE_CORRUPT) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(112); + } + + /* Now flush everything out. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 1, (VOID *) fat_buffer); + + /* Fix the FAT chain. */ + fat_buffer[4] = 3; + fat_buffer[5] = 0; + fat_buffer[0x32] = 0; + fat_buffer[0x33] = 0; + + /* Write the FAT corruption out. */ + fx_media_write(&ram_disk, 1, (VOID *) fat_buffer); + + /* Now open the file again with an invalid last cluster at the end of the file... */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check status. */ + if (status != FX_FILE_CORRUPT) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(113); + } + + /* Now flush everything out. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 1, (VOID *) fat_buffer); + + /* Fix the FAT chain. */ + fat_buffer[0x32] = 0xff; + fat_buffer[0x33] = 0xff; + + /* Write the FAT corruption out. */ + fx_media_write(&ram_disk, 1, (VOID *) fat_buffer); + + /* Open the file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, buffer, 128*3); + status += fx_file_seek(&my_file, 0); + + /* Set the flag to cause the cache update to bypass. */ + _fx_utility_logical_sector_read_1_error_request = 1; + status += fx_file_read(&my_file, buffer, 128*3, &actual); + _fx_utility_logical_sector_read_1_error_request = 0; + + /* Set the flag to cause the cache update to bypass - this time with a larger error value to make sure that path is taken. */ + _fx_utility_logical_sector_read_1_error_request = 1000000; + status += fx_file_read(&my_file, buffer, 128*3, &actual); + _fx_utility_logical_sector_read_1_error_request = 0; + +#ifndef FX_ENABLE_FAULT_TOLERANT + /* Call the logical sector flush with a large error value to get that path taken as well. */ + _fx_utility_logical_sector_flush_error_request = 1000000; + status += _fx_utility_logical_sector_flush(&ram_disk, 0, 60000, FX_FALSE); + _fx_utility_logical_sector_flush_error_request = 0; + + /* Check status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(116); + } +#endif + + /* Close everything down. */ + fx_file_close(&my_file); + fx_media_abort(&ram_disk); + + /* FAT32. */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors - FAT16 + 512, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_file_create(&ram_disk, "TEST.TXT"); + return_if_fail(FX_SUCCESS == status); + + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_allocate(&my_file, 512); + status += fx_file_close(&my_file); + return_if_fail(FX_SUCCESS == status); + + /* Open the file with a bad FAT chain. */ + _fx_utility_FAT_entry_write(&ram_disk, 3, 0); + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + return_if_fail(FX_FILE_CORRUPT == status); + + fx_file_close(&my_file); + fx_media_close(&ram_disk); + + printf("SUCCESS!\n"); + test_control_return(0); +} + diff --git a/test/regression_test/filex_file_rename_test.c b/test/regression_test/filex_file_rename_test.c new file mode 100644 index 0000000..8d15922 --- /dev/null +++ b/test/regression_test/filex_file_rename_test.c @@ -0,0 +1,353 @@ +/* This FileX test concentrates on the file rename operations. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_ram_driver_test.h" +#include "fx_fault_tolerant.h" +#include "fx_utility.h" +#include + +#define DEMO_STACK_SIZE 8192 +#define CACHE_SIZE 16*128 +#ifdef FX_ENABLE_FAULT_TOLERANT +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE +#else +#define FAULT_TOLERANT_SIZE 0 +#endif + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +static UCHAR *fault_tolerant_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif + + +/* Define thread prototypes. */ + +void filex_file_rename_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_file_rename_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + fault_tolerant_buffer = pointer; + pointer += FAULT_TOLERANT_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +FX_FILE open_file; +CHAR long_name1[FX_MAX_LONG_NAME_LEN+1]; +CHAR long_name2[FX_MAX_LONG_NAME_LEN+1]; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: File rename test......................................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors +/* Allocated a larger disk to enable fault tolerant feature. */ +#ifdef FX_ENABLE_FAULT_TOLERANT + 512 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster +#else + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster +#endif + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* try to rename a file to something invalid */ + status = fx_file_rename(&ram_disk, "MYTEST", ""); + return_if_fail( status == FX_INVALID_NAME); + + /* try to rename a file before the media is opened */ + status = fx_file_rename(&ram_disk, "MYTEST", "OURTEST"); + return_if_fail( status == FX_MEDIA_NOT_OPEN); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + +#ifdef FX_ENABLE_FAULT_TOLERANT + /* Enable fault tolerant if FX_ENABLE_FAULT_TOLERANT is defined. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); +#endif + + /* try to rename a file while the media is write protected */ + ram_disk.fx_media_driver_write_protect = FX_TRUE; + status = fx_file_rename(&ram_disk, "MYTEST", "OURTEST"); + ram_disk.fx_media_driver_write_protect = FX_FALSE; + return_if_fail( status == FX_WRITE_PROTECT); + + /* try to rename a file that cant be found */ + status = fx_file_rename(&ram_disk, "MYTEST", "OURTEST"); + return_if_fail( status != FX_SUCCESS); + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + + /* send null pointer to generate an error */ + status = fx_file_rename(FX_NULL, "", ""); + return_if_fail( status == FX_PTR_ERROR); + +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Simple 8.3 rename in the root directory. */ + status = fx_file_create(&ram_disk, "MYTEST"); + status += fx_file_rename(&ram_disk, "MYTEST", "OURTEST"); + return_if_fail( status == FX_SUCCESS); + + /* rename the files to lower case and upper case versions of themselves */ + status = fx_file_rename(&ram_disk, "OURTEST", "ourtest"); + status += fx_file_rename(&ram_disk, "ourtest", "OURTEST"); + return_if_fail( status == FX_SUCCESS); + + /* try to rename a file into a subdirectory that does not exist */ + status = fx_file_rename(&ram_disk, "OURTEST", "DOES_NOT_EXITS/OURTEST"); + return_if_fail( status == FX_INVALID_NAME); + + /* Now create a 2 sub-directories. */ + status = fx_directory_create(&ram_disk, "/subdir1"); + status += fx_directory_create(&ram_disk, "/subdir2"); + return_if_fail( status == FX_SUCCESS); + + /* try to rename a file to something that already exists */ + status = fx_file_rename(&ram_disk, "OURTEST", "subdir1"); + return_if_fail( status == FX_ALREADY_CREATED); + +#ifndef FX_DONT_UPDATE_OPEN_FILES + + /* rename a file while it is open and while another is opened to get code coverage */ + status = fx_file_create(&ram_disk, "OPEN_FILE"); + status += fx_file_open(&ram_disk, &open_file, "OPEN_FILE", FX_OPEN_FOR_WRITE); + status += fx_file_rename(&ram_disk, "OURTEST", "NEWTEST"); + status += fx_file_rename(&ram_disk, "NEWTEST", "OURTEST"); + status += fx_file_rename(&ram_disk, "OPEN_FILE", "NEW_OPEN_FILE"); + return_if_fail( status == FX_SUCCESS); + +#endif + + /* move the file through the subdirectories */ + status = fx_file_rename(&ram_disk, "OURTEST", "/subdir1/NEWOURTEST"); + status += fx_file_rename(&ram_disk, "/subdir1/NEWOURTEST", "/subdir2/OURTEST"); + status += fx_file_rename(&ram_disk, "/subdir2/OURTEST", "OURTEST"); + status += fx_file_rename(&ram_disk, "OURTEST", ".OURTEST"); + status += fx_file_delete(&ram_disk, ".OURTEST"); + return_if_fail( status == FX_SUCCESS); + + /* try to rename a file that isnt a file */ + status = fx_file_rename(&ram_disk, "subdir1", "OURTEST"); + return_if_fail( status == FX_NOT_A_FILE); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 128, // Directory Entries + 0, // Hidden sectors + 6000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + return_if_fail( status == FX_SUCCESS); + + /* Loop to create a very long name. */ + for (i = 0; i < FX_MAX_LONG_NAME_LEN; i++) + { + long_name1[i] = 'a'; + long_name2[i] = 'a'; + } + + /* Put NULL and special character at the end of the file. */ + long_name1[i] = 0; + long_name1[i-1] = 0; + long_name1[i-2] = '~'; + long_name2[i] = 0; + long_name2[i-1] = 0; + long_name2[i-2] = '~'; + + /* Create a super short file name. */ + status = fx_file_create(&ram_disk, "sname"); + status += fx_file_create(&ram_disk, long_name1); + return_if_fail( status == FX_SUCCESS); + + /* Attempt to change the short name with leading spaces. */ + status = fx_file_rename(&ram_disk, "sname", " name"); + return_if_fail( status == FX_SUCCESS); + + /* Now attempt to change the short name with middle spaces. */ + status = fx_file_rename(&ram_disk, "name", "\\new name"); + return_if_fail( status == FX_SUCCESS); + + /* Make the second long name too big, to test the max size of the new long file name. */ + long_name2[FX_MAX_LONG_NAME_LEN-1] = '~'; + status = fx_file_rename(&ram_disk, long_name1, long_name2); + return_if_fail( status == FX_INVALID_NAME); + + /* Make the second long file name the correct max length again. */ + long_name2[FX_MAX_LONG_NAME_LEN-1] = 0; + + /* Change the name of the second long file name. */ + long_name2[FX_MAX_LONG_NAME_LEN-3] = 'a'; + status = fx_file_rename(&ram_disk, long_name1, long_name2); + return_if_fail( status == FX_SUCCESS); + + /* Change back to the first long file name. */ + status = fx_file_rename(&ram_disk, long_name2, long_name1); + return_if_fail( status == FX_SUCCESS); + + /* Now open the long file name so we can get that logic excercised in the rename logic. */ + status = fx_file_open(&ram_disk, &my_file, long_name1, FX_OPEN_FOR_WRITE); + status += fx_file_rename(&ram_disk, long_name1, long_name2); + return_if_fail( status == FX_SUCCESS); + + /* Close the file. */ + fx_file_close(&my_file); + + /* Now test the directory free search logic when there are no more entries. */ + + /* First, fill up the root directory. */ + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1000"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1001"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1002"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1003"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1004"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1005"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1006"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1007"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1008"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1009"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1010"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1011"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1012"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1013"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1014"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1015"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1016"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1017"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1018"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1019"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1020"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1021"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1022"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1023"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1024"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1025"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1026"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1027"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1028"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1029"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1030"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1031"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1032"); + status += fx_file_create(&ram_disk, "aaaaaaaaaaaaa1033"); + + /* Now attempt to rename the long file again. */ + status += fx_file_rename(&ram_disk, long_name2, long_name1); + + /* Check status - this should fail because we do not have enough room in the root directory. */ + return_if_fail( status == FX_NO_MORE_SPACE); + + /* Now test I/O error on directory entry write inside of the rename processing. */ + fx_media_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + _fx_utility_logical_sector_write_error_request = 1; + status = fx_file_rename(&ram_disk, "new name", "sname"); + _fx_utility_logical_sector_write_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + printf("SUCCESS!\n"); + test_control_return(0); +} diff --git a/test/regression_test/filex_file_seek_test.c b/test/regression_test/filex_file_seek_test.c new file mode 100644 index 0000000..7543a1e --- /dev/null +++ b/test/regression_test/filex_file_seek_test.c @@ -0,0 +1,860 @@ +/* This FileX test concentrates on the file seek operation. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include +#include "fx_ram_driver_test.h" + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 128*128 + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif +static UCHAR buffer[128]; + +/* Define thread prototypes. */ + +void filex_file_seek_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_file_seek_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +ULONG read_value; +ULONG write_value; +ULONG available_bytes; +ULONG i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: File seek test........................................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(2); + } + + /* Attempt to seek before the media is opened to generate an error */ + status = fx_file_extended_seek(&my_file, 0); + if (status != FX_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(2); + } + + /* Attempt to seek before the media is opened to generate an error */ + status = fx_file_extended_relative_seek(&my_file, 0, 0); + if (status != FX_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(3); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(4); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Attempt to seek before the media is opened to generate an error */ + status = fx_file_extended_relative_seek(&my_file, 0xFFFFFFFFFFFFFFFF, FX_SEEK_BACK); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(7); + } + +/* test error checking */ +#ifndef FX_DISABLE_ERROR_CHECKING + /* send null pointer to generate an error */ + status = fx_media_space_available(FX_NULL, FX_NULL); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(8); + } + + /* send an invalid option to generate an error */ + status = fx_file_relative_seek(&my_file, 0xFFFFFFFF, 4); + if (status != FX_INVALID_OPTION) + { + printf("ERROR!\n"); + test_control_return(9); + } + + /* send null pointer to generate an error */ + status = fx_file_extended_seek(FX_NULL, 0xFF); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(10); + } + + /* send null pointer to generate an error */ + status = fx_file_extended_relative_seek(FX_NULL, 0xFF, FX_SEEK_BEGIN); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(11); + } + + /* send null pointer to generate an error */ + status = fx_file_extended_relative_seek(&my_file, 0xFF, 4); + if (status != FX_INVALID_OPTION) + { + printf("ERROR!\n"); + test_control_return(12); + } + +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Pickup the available bytes in the media. */ + status = fx_media_space_available(&ram_disk, &available_bytes); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (available_bytes < sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Loop to write successive bytes out to the file.... to fill the media! */ + i = 0; + write_value = 0; + while (i < available_bytes) + { + + /* Write 4 bytes to the file. */ + status = fx_file_write(&my_file, (void *) &write_value, sizeof(ULONG)); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Increment byte count. */ + i = i + sizeof(ULONG); + + /* Increment write value. */ + write_value++; + } + + /* Pickup the available bytes in the media again. */ + status = fx_media_space_available(&ram_disk, &i); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (i != 0)) + { + + printf("ERROR!\n"); + test_control_return(15); + } + +#ifndef FX_DISABLE_CACHE + /* At this point, we should invalidate the (which also flushes the cache) media to ensure that all + dirty sectors are written. */ + status = fx_media_cache_invalidate(&ram_disk); + + /* Check for invalidate errors. */ + if ((status != FX_SUCCESS) || (ram_disk.fx_media_sector_cache_dirty_count)) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* See if any sectors are still valid in the cache. */ + for (i = 0; i < FX_MAX_SECTOR_CACHE; i++) + { + + /* Determine if this cache entry is still valid. */ + if (ram_disk.fx_media_sector_cache[i].fx_cached_sector_valid) + { + + printf("ERROR!\n"); + test_control_return(17); + } + } +#endif + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Read the 4 bytes at the front of the file... should be 0! */ + status = fx_file_read(&my_file, (void *) &read_value, sizeof(ULONG), &actual); + + /* Determine if it is correct. */ + if ((status) || (read_value != 0) || (actual != sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Read the next 4 bytes at the front of the file... should be 1! */ + status = fx_file_read(&my_file, (void *) &read_value, sizeof(ULONG), &actual); + + /* Determine if it is correct. */ + if ((status) || (read_value != 1) || (actual != sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Seek to near the last 4 bytes of the file. */ + status = fx_file_seek(&my_file, available_bytes - 4); + + /* Read the last 4 bytes of the file... should be available_bytes/sizeof(ULONG)! */ + status += fx_file_read(&my_file, (void *) &read_value, sizeof(ULONG), &actual); + + /* Determine if it is correct. */ + if ((status) || (read_value != (available_bytes/sizeof(ULONG) - 1)) || (actual != sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Read the past the end of the file... should get an error in this case. */ + status = fx_file_read(&my_file, (void *) &read_value, sizeof(ULONG), &actual); + + /* Determine if it is correct. */ + if (status != FX_END_OF_FILE) + { + + printf("ERROR!\n"); + test_control_return(22); + } + + /* Seek to the middle of the file. */ + status = fx_file_seek(&my_file, available_bytes/2); + + /* Read the middle 4 bytes of the file... should be (available_bytes/2)/sizeof(ULONG)! */ + status += fx_file_read(&my_file, (void *) &read_value, sizeof(ULONG), &actual); + + /* Determine if it is correct. */ + if ((status) || (read_value != (available_bytes/(2*sizeof(ULONG)))) || (actual != sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Seek to the end of the file. */ + status = fx_file_seek(&my_file, 0xFFFFFFFF); + + /* Determine if it is correct. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Close the file. */ + status += fx_file_close(&my_file); + + /* Open the test file for fast reading. */ + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_READ_FAST); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Read the 4 bytes at the front of the file... should be 0! */ + status = fx_file_read(&my_file, (void *) &read_value, sizeof(ULONG), &actual); + + /* Determine if it is correct. */ + if ((status) || (read_value != 0) || (actual != sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Read the next 4 bytes at the front of the file... should be 1! */ + status = fx_file_read(&my_file, (void *) &read_value, sizeof(ULONG), &actual); + + /* Determine if it is correct. */ + if ((status) || (read_value != 1) || (actual != sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Seek to near the last 4 bytes of the file. */ + status = fx_file_seek(&my_file, available_bytes - 4); + + /* Read the last 4 bytes of the file... should be available_bytes/sizeof(ULONG)! */ + status += fx_file_read(&my_file, (void *) &read_value, sizeof(ULONG), &actual); + + /* Determine if it is correct. */ + if ((status) || (read_value != (available_bytes/sizeof(ULONG) - 1)) || (actual != sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Read the past the end of the file... should get an error in this case. */ + status = fx_file_read(&my_file, (void *) &read_value, sizeof(ULONG), &actual); + + /* Determine if it is correct. */ + if (status != FX_END_OF_FILE) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Seek to the middle of the file. */ + status = fx_file_seek(&my_file, available_bytes/2); + + /* Read the middle 4 bytes of the file... should be (available_bytes/2)/sizeof(ULONG)! */ + status += fx_file_read(&my_file, (void *) &read_value, sizeof(ULONG), &actual); + + /* Determine if it is correct. */ + if ((status) || (read_value != (available_bytes/(2*sizeof(ULONG)))) || (actual != sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Seek to the end of the file. */ + status = fx_file_seek(&my_file, 0xFFFFFFFF); + + /* Determine if it is correct. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Close the file. */ + status += fx_file_close(&my_file); + + /* Open the test file for reading. */ + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_READ); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + /* send null pointer to generate an error */ + status = fx_file_relative_seek(FX_NULL, 0, FX_SEEK_BEGIN); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(33); + } +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Read the 4 bytes at the front of the file... should be 0! */ + status = fx_file_relative_seek(&my_file, 0, FX_SEEK_BEGIN); + status += fx_file_read(&my_file, (void *) &read_value, sizeof(ULONG), &actual); + + /* Determine if it is correct. */ + if ((status) || (read_value != 0) || (actual != sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Read the next 4 bytes at the front of the file... should be 1! */ + status = fx_file_read(&my_file, (void *) &read_value, sizeof(ULONG), &actual); + + /* Determine if it is correct. */ + if ((status) || (read_value != 1) || (actual != sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Seek to near the last 4 bytes of the file. */ + status = fx_file_relative_seek(&my_file, 0xFFFFFFFF, FX_SEEK_BEGIN); + status += fx_file_relative_seek(&my_file, 4, FX_SEEK_BACK); + + /* Read the last 4 bytes of the file... should be available_bytes/sizeof(ULONG)! */ + status += fx_file_read(&my_file, (void *) &read_value, sizeof(ULONG), &actual); + + /* Determine if it is correct. */ + if ((status) || (read_value != (available_bytes/sizeof(ULONG) - 1)) || (actual != sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(36); + } + + /* Read the past the end of the file... should get an error in this case. */ + status = fx_file_read(&my_file, (void *) &read_value, sizeof(ULONG), &actual); + + /* Determine if it is correct. */ + if (status != FX_END_OF_FILE) + { + + printf("ERROR!\n"); + test_control_return(37); + } + + /* Seek to the middle of the file. */ + status = fx_file_relative_seek(&my_file, 0xFFFFFFFF, FX_SEEK_END); + status += fx_file_relative_seek(&my_file, available_bytes/2, FX_SEEK_FORWARD); + + /* Read the middle 4 bytes of the file... should be (available_bytes/2)/sizeof(ULONG)! */ + status += fx_file_read(&my_file, (void *) &read_value, sizeof(ULONG), &actual); + + /* Determine if it is correct. */ + if ((status) || (read_value != (available_bytes/(2*sizeof(ULONG)))) || (actual != sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(38); + } + + /* Seek to the end of the file. */ + status = fx_file_relative_seek(&my_file, 0, FX_SEEK_END); + + /* Determine if it is correct. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(39); + } + + /* Close the file. */ + status += fx_file_close(&my_file); + + /* Close the media. */ + status += fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(40); + } + + /* Test corner cases of extended seek. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(41); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(42); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(43); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(44); + } + + /* Write data to the file. */ + status = fx_file_write(&my_file, buffer, 128); + status += fx_file_write(&my_file, buffer, 128); + status += fx_file_write(&my_file, buffer, 128); + status += fx_file_write(&my_file, buffer, 128); + + status += fx_file_write(&my_file, buffer, 128); + status += fx_file_write(&my_file, buffer, 128); + status += fx_file_write(&my_file, buffer, 128); + status += fx_file_write(&my_file, buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(45); + } + + /* Seek to the beginning of the file. */ + status = fx_file_extended_seek(&my_file, 0); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(46); + } + + /* Seek to the second to beginning of the last cluster of the file. */ + status = fx_file_extended_seek(&my_file, 1024-128); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(47); + } + + /* Seek to the end of the file with an I/O error. */ + _fx_utility_fat_entry_read_error_request = 1; + status = fx_file_extended_seek(&my_file, 1024); + _fx_utility_fat_entry_read_error_request = 0; + if (status != FX_IO_ERROR) + { + printf("ERROR!\n"); + test_control_return(48); + } + + /* Seek to the end of the file with a FAT entry of 1 error. */ + status = fx_file_extended_seek(&my_file, 0); + _fx_utility_fat_entry_read_error_request = 10001; + status += fx_file_extended_seek(&my_file, 1024); + _fx_utility_fat_entry_read_error_request = 0; + if (status != FX_FILE_CORRUPT) + { + printf("ERROR!\n"); + test_control_return(49); + } + + /* Seek to the end of the file with a FAT entry of max fat value error. */ + _fx_utility_fat_entry_read_error_request = 20001; + status = fx_file_extended_seek(&my_file, 1024); + _fx_utility_fat_entry_read_error_request = 0; + if (status != FX_FILE_CORRUPT) + { + printf("ERROR!\n"); + test_control_return(50); + } + + /* Seek to the end of the file with a FAT entry of minimal value error. */ + _fx_utility_fat_entry_read_error_request = 10008; + status = fx_file_extended_seek(&my_file, 1024); + _fx_utility_fat_entry_read_error_request = 0; + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(51); + } + + /* Close the file and the media. */ + status = fx_file_close(&my_file); + status += fx_media_close(&ram_disk); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(52); + } + + /* Test zero divisor checking in fx_file_extended_seek. */ + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(53); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(54); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(55); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(56); + } + + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(57); + } + + /* Corrupt the media. */ + ram_disk.fx_media_bytes_per_sector = 0; + + status = fx_file_extended_seek(&my_file, 1); + + if (status != FX_MEDIA_INVALID) + { + printf("ERROR!\n"); + test_control_return(58); + } + ram_disk.fx_media_bytes_per_sector = 128; + + /* Write data to the file. */ + status = fx_file_write(&my_file, buffer, 128); + + /* Corrupt the media. */ + ram_disk.fx_media_sectors_per_FAT = 0; + + status = fx_file_write(&my_file, buffer, 128); + + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(59); + } + + /* Close the file and the media. */ + status = fx_file_close(&my_file); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(60); + } + + /* Corrupt the media. */ + ram_disk.fx_media_bytes_per_sector = 0; + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_MEDIA_INVALID) + { + + printf("ERROR!\n"); + test_control_return(56); + } + ram_disk.fx_media_bytes_per_sector = 128; + + status = fx_media_close(&ram_disk); + + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(60); + } + else + { + + printf("SUCCESS!\n"); + test_control_return(0); + } +} + diff --git a/test/regression_test/filex_file_write_available_cluster_test.c b/test/regression_test/filex_file_write_available_cluster_test.c new file mode 100644 index 0000000..5d302f0 --- /dev/null +++ b/test/regression_test/filex_file_write_available_cluster_test.c @@ -0,0 +1,336 @@ +/* This FileX test concentrates on the available clusters after file seek and write operation. */ +/* A fix-me is in this file. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_ram_driver_test.h" +#include "fx_fault_tolerant.h" +#include + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 128*128 +#ifdef FX_ENABLE_FAULT_TOLERANT +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE +#else +#define FAULT_TOLERANT_SIZE 0 +#endif + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; + +/* Define the counters used in the test application... */ + +struct step +{ + ULONG offset; + UINT seek; + UINT size; +}; + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif +static UCHAR test_buffer[4096]; +#ifdef FX_ENABLE_FAULT_TOLERANT +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif /* FX_ENABLE_FAULT_TOLERANT */ +static struct step steps[] = +{ + {0, FX_SEEK_FORWARD, 1136}, + {4 * sizeof(test_buffer), FX_SEEK_FORWARD, sizeof(test_buffer)}, + {112, FX_SEEK_END, sizeof(test_buffer)}, + {(2 * sizeof(test_buffer)), FX_SEEK_BACK, sizeof(test_buffer)}, +}; + +/* Define thread prototypes. */ + +void filex_file_write_available_cluster_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_file_write_available_cluster_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +#ifdef FX_ENABLE_FAULT_TOLERANT +UINT enable_fault_tolerant_this_time = 0; +UINT do_it_again = 0; +ULONG actual; +#endif /* FX_ENABLE_FAULT_TOLERANT */ + +ULONG used_clusters; +ULONG total_bytes; +ULONG total_clusters; +ULONG i, j; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: File write available cluster test......................"); + +#ifdef FX_ENABLE_FAULT_TOLERANT +TEST_START: +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 512, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + +#ifdef FX_ENABLE_FAULT_TOLERANT + if ( enable_fault_tolerant_this_time) + { + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail( status == FX_SUCCESS); + } +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + return_if_fail( status == FX_SUCCESS); + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + return_if_fail( status == FX_SUCCESS); + + total_clusters = ram_disk.fx_media_available_clusters; + + /* Run test steps. */ + total_bytes = 9216; + for (j = 0; j < 100; j++) + { + i = j & 0x03; + fx_file_relative_seek (&my_file, steps[i].offset, steps[i].seek); + fx_file_write (&my_file, test_buffer, steps[i].size); + + if (i == 3) + { + + /* Check available clusters. */ + used_clusters = total_bytes / 4096 + 1; + return_if_fail( total_clusters == ram_disk.fx_media_available_clusters + used_clusters); + + /* Increase 8080 bytes each round. */ + total_bytes += 8080; + } + } +#ifdef FX_ENABLE_FAULT_TOLERANT + if ( enable_fault_tolerant_this_time) + { + /* Make IO error while looking for copy_head_cluster by _fx_utility_FAT_entry_read. */ + _fx_utility_fat_entry_read_error_request = 1; + status = fx_file_relative_seek( &my_file, steps[0].offset, steps[0].seek); + status += fx_file_write( &my_file, test_buffer, steps[0].size); + return_if_fail( status == FX_IO_ERROR); + + /* Make IO error while looking for copy_tail_cluster by _fx_utility_FAT_entry_read. */ + _fx_utility_fat_entry_read_error_request = 94; + status = fx_file_relative_seek( &my_file, steps[3].offset, steps[3].seek); + status += fx_file_write( &my_file, test_buffer, steps[3].size); + return_if_fail( status == FX_IO_ERROR); + + /* Make IO error while copying sectors in replaced clusters at the back in fx_file_write.c.*/ + _fx_ram_driver_io_error_request = 17; + status = fx_file_relative_seek( &my_file, steps[3].offset, steps[3].seek); + status += fx_file_write( &my_file, test_buffer, steps[3].size); + return_if_fail( status == FX_IO_ERROR); + + /* Make IO error while writing back the current logical sector at Line 1599 in fx_file_write.c. */ + _fx_ram_driver_io_error_request = 18; + status = fx_file_relative_seek( &my_file, steps[3].offset, steps[3].seek); + status += fx_file_write( &my_file, test_buffer, steps[3].size); + return_if_fail( status == FX_IO_ERROR); + +#if !defined(FX_ENABLE_FAULT_TOLERANT) && !defined(FX_DISABLE_CACHE) + /*Make IO error while copying sectors in replaced clusters at the front in fx_file_write.c.*/ + _fx_ram_driver_io_error_request = 3; + status = fx_file_relative_seek( &my_file, steps[3].offset, steps[3].seek); + status += fx_file_write( &my_file, test_buffer, steps[3].size); + return_if_fail( status == FX_IO_ERROR); +#endif + + /* Make IO error while writing back the current logical sector at Line 1215 in fx_file_write.c. */ + _fx_ram_driver_io_error_request = 4; + status = fx_file_relative_seek( &my_file, steps[3].offset, steps[3].seek); + status += fx_file_write( &my_file, test_buffer, steps[3].size); + return_if_fail( status == FX_IO_ERROR); + + /* Make IO error while getting the cluster next to the copy tail. */ + _fx_utility_fat_entry_read_error_request = 75; + status = fx_file_relative_seek( &my_file, steps[3].offset, steps[3].seek); + status += fx_file_write( &my_file, test_buffer, steps[3].size); + + /* Make IO error while _fx_file_write is calling _fx_fault_tolerant_set_FAT__chain. */ + _fx_ram_driver_io_error_request = 1; + status = fx_file_relative_seek( &my_file, steps[0].offset, steps[0].seek); + status += fx_file_write( &my_file, test_buffer, steps[0].size); + return_if_fail( status == FX_IO_ERROR); + + /* Backup original data. */ + actual = my_file.fx_file_first_physical_cluster; + +/* Please fix me: + * The following code is determing to cover a branch in fx_file_write.c, which need to get three condition: + * 1. fx_file_total_clusters - fx_file_current_relative_cluster > 0 + * 2. fx_file_current_logical_offset >= fx_media_bytes_per_sector + * 3. fx_file_current_relative_sector >= fx_media_sectors_per_cluster - 1 + * + * In other words, it need to set fx_file_current_logical_offset at the end of a cluster which is in the middle of a file. + * I manually modify FILE_STRUCT and satify the condition for coverage. Should this branch be removed? + */ + /* Produce wrong first_physical_cluster. */ + status = fx_file_relative_seek( &my_file, steps[0].offset, steps[0].seek); + my_file.fx_file_first_physical_cluster = 0; + status += fx_file_write( &my_file, test_buffer, steps[0].size); + return_if_fail( status == FX_NOT_FOUND); + + status = fx_file_relative_seek( &my_file, steps[0].offset, steps[0].seek); + my_file.fx_file_first_physical_cluster = ram_disk.fx_media_fat_reserved; + status += fx_file_write( &my_file, test_buffer, steps[0].size); + return_if_fail( status == FX_NOT_FOUND); + + /* Restore data we just modified. */ + my_file.fx_file_first_physical_cluster = actual; + + if ( do_it_again) + { + goto EXTRA_TEST; + } + + /* Attempt to cover the branch that offset is at the end of a sector but not the end of a file. */ + /* No head cluster need to be replaced. */ + status = fx_file_relative_seek( &my_file, steps[2].offset, steps[2].seek); + my_file.fx_file_current_logical_offset = 512; + status += fx_file_write( &my_file, test_buffer, steps[2].size); + + /* Attempt to cover the branch that offset is at the end of a cluster but not the end of a file. */ + /* No head cluster need to be replaced. */ + status = fx_file_relative_seek( &my_file, steps[2].offset, steps[2].seek); + my_file.fx_file_current_logical_offset = 512; + my_file.fx_file_current_relative_sector = 7; + my_file.fx_file_total_clusters = 50; + status += fx_file_write( &my_file, test_buffer, steps[2].size); + + /* Close the file. */ + status = fx_file_close(&my_file); + status += fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + do_it_again = 1; + goto TEST_START; + +EXTRA_TEST: + /* Covered the branch dealing with the condition that offset is at the end of a cluster but not the end of a file. */ + /* Head cluster need to be replaced is existed. */ + status = fx_file_relative_seek( &my_file, steps[2].offset, steps[2].seek); + my_file.fx_file_current_logical_offset = 512; + my_file.fx_file_current_relative_sector = 7; + status += fx_file_write( &my_file, test_buffer, steps[2].size); + + } +#endif + + /* Close the file. */ + status = fx_file_close(&my_file); + status += fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + +#ifdef FX_ENABLE_FAULT_TOLERANT + if ( enable_fault_tolerant_this_time == 0) + { + enable_fault_tolerant_this_time = 1; + goto TEST_START; + } +#endif + +#if defined(FX_ENABLE_FAULT_TOLERANT) && !defined(FX_DISABLE_CACHE) +FX_CACHED_SECTOR cache_entry; + + ram_disk.fx_media_sector_cache_hashed = 0; + ram_disk.fx_media_memory_buffer = test_buffer; + ram_disk.fx_media_sector_cache_end = test_buffer; + ram_disk.fx_media_fault_tolerant_enabled = 1; + ram_disk.fx_media_fault_tolerant_state |= FX_FAULT_TOLERANT_STATE_STARTED; + ram_disk.fx_media_sector_cache_list_ptr = &cache_entry; + ram_disk.fx_media_sector_cache_size = 1; + cache_entry.fx_cached_sector_memory_buffer = NULL; + cache_entry.fx_cached_sector_next_used = NULL; + + _fx_utility_logical_sector_write(&ram_disk, (ULONG64)3, test_buffer, 0, FX_DATA_SECTOR); + +#endif /* defined(FX_ENABLE_FAULT_TOLERANT) && !defined(FX_DISABLE_CACHE) */ + + printf("SUCCESS!\n"); + test_control_return(0); +} + diff --git a/test/regression_test/filex_file_write_notify_test.c b/test/regression_test/filex_file_write_notify_test.c new file mode 100644 index 0000000..8f5dd5a --- /dev/null +++ b/test/regression_test/filex_file_write_notify_test.c @@ -0,0 +1,260 @@ +/* This FileX test concentrates on the file write and file notify operation. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include +#include "fx_ram_driver_test.h" + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 128*128 + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif +static UCHAR write_data[65535]; +static UCHAR read_data[65535]; +static ULONG notify_counter = 0; + + +/* Define thread prototypes. */ + +void filex_file_write_notify_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); +static VOID file_write_notify(FX_FILE *file_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_file_write_notify_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +ULONG i; +FX_FILE my_file; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: File write notify test................................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 512, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(2); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Initialize the data for write and read. */ + for (i = 0; i < sizeof(write_data); i++) + { + write_data[i] = (UCHAR)i; + read_data[i] = 0; + } + + /* Write 4116 bytes to the file. */ + status = fx_file_write(&my_file, write_data, 4116); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Check the notify counter. */ + if (notify_counter != 0) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Setup write notify callback. */ + status = fx_file_write_notify_set(&my_file, file_write_notify); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Write 4116 bytes to the file. */ + status = fx_file_write(&my_file, write_data + 4116, 4116); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Check the notify counter. */ + if (notify_counter != 1) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Read as much as 4 sectors full of bytes from the file. */ + status = fx_file_read(&my_file, (void *)read_data, sizeof(read_data), &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 4116 * 2)) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Close the file. */ + status = fx_file_close(&my_file); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + else + { + + printf("SUCCESS!\n"); + test_control_return(0); + } +} + +static VOID file_write_notify(FX_FILE *file_ptr) +{ + + FX_PARAMETER_NOT_USED(file_ptr); + + /* Update the notify counter. */ + notify_counter++; +} diff --git a/test/regression_test/filex_file_write_seek_test.c b/test/regression_test/filex_file_write_seek_test.c new file mode 100644 index 0000000..c6c59c8 --- /dev/null +++ b/test/regression_test/filex_file_write_seek_test.c @@ -0,0 +1,346 @@ +/* This FileX test concentrates on the file seek and write operation. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_utility.h" +#include +#include "fx_fault_tolerant.h" +#include "fx_ram_driver_test.h" + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 128*128 +#ifdef FX_ENABLE_FAULT_TOLERANT +#define FAULT_TOLERANT_SIZE FX_FAULT_TOLERANT_MINIMAL_BUFFER_SIZE +#else +#define FAULT_TOLERANT_SIZE 0 +#endif + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif +static UCHAR write_data[65535]; +static UCHAR read_data[65535]; +#ifdef FX_ENABLE_FAULT_TOLERANT +static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + +/* Define thread prototypes. */ + +void filex_file_write_seek_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_file_write_seek_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: File write seek test..................................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 512, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail(FX_SUCCESS == status); + +#ifdef FX_ENABLE_FAULT_TOLERANT + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail(FX_SUCCESS == status); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Create a file called TEST.TXT in the root directory. */ + status += fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + return_if_fail(FX_SUCCESS == status); + + /* Initialize the data for write and read. */ + for (i = 0; i < sizeof(write_data); i++) + { + write_data[i] = (UCHAR)i; + read_data[i] = 0; + } + + /* Write 4116 bytes to the file. */ + status = fx_file_write(&my_file, write_data, 4116); + return_if_fail(FX_SUCCESS == status); + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 16464); + return_if_fail(FX_SUCCESS == status); + + /* Write 4116 bytes to the file. */ + status = fx_file_write(&my_file, write_data + 4116, 4116); + return_if_fail(FX_SUCCESS == status); + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 112); + return_if_fail(FX_SUCCESS == status); + + /* Write 1 byte to the file. */ + status = fx_file_write(&my_file, write_data + 112, 1); + return_if_fail(FX_SUCCESS == status); + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 32928); + return_if_fail(FX_SUCCESS == status); + + /* Write 4116 bytes to the file. */ + status = fx_file_write(&my_file, write_data + 8232, 4116); + return_if_fail(FX_SUCCESS == status); + + fx_file_close(&my_file); + fx_media_close(&ram_disk); + + /* For the coverage of fx_file_write.c. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 512, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail(FX_SUCCESS == status); + +#ifdef FX_ENABLE_FAULT_TOLERANT + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail(FX_SUCCESS == status); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Create a file called TEST.TXT in the root directory. */ + status += fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + return_if_fail(FX_SUCCESS == status); + + /* fx_file_current_relative_cluster < fx_file_consecutive_cluster. */ + status = fx_file_write(&my_file, write_data, 4 * 512); + status += fx_file_seek(&my_file, 2 * 512); + my_file.fx_file_current_relative_cluster = 0; + status += fx_file_write(&my_file, write_data, 4 * 512); + return_if_fail(FX_SUCCESS == status); + + fx_file_close(&my_file); + fx_media_close(&ram_disk); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 512, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail(FX_SUCCESS == status); + +#ifdef FX_ENABLE_FAULT_TOLERANT + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail(FX_SUCCESS == status); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Create a file called TEST.TXT in the root directory. */ + status += fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + return_if_fail(FX_SUCCESS == status); + + status = fx_file_write(&my_file, write_data, 4 * 512); + status += fx_file_seek(&my_file, 0); + return_if_fail(FX_SUCCESS == status); + + /* data_append == FALSE && invalid copy_head_cluster */ + my_file.fx_file_current_physical_cluster = 1; + status = fx_file_write(&my_file, write_data, 2 * 512); + + fx_file_close(&my_file); + fx_media_close(&ram_disk); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 512, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail(FX_SUCCESS == status); + +#ifdef FX_ENABLE_FAULT_TOLERANT + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail(FX_SUCCESS == status); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Create a file called TEST.TXT in the root directory. */ + status += fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + return_if_fail(FX_SUCCESS == status); + + status = fx_file_write(&my_file, write_data, 4 * 512); + status += fx_file_seek(&my_file, 0); + return_if_fail(FX_SUCCESS == status); + + /* data_append == FALSE && invalid copy_head_cluster */ + my_file.fx_file_current_physical_cluster = (ULONG)-1; + status = fx_file_write(&my_file, write_data, 2 * 512); + + my_file.fx_file_current_physical_cluster = 1; + status = fx_file_write(&my_file, write_data, 2 * 512); + + fx_file_close(&my_file); + fx_media_close(&ram_disk); + + /* Format the media by FAT16 which cluster 0 is available. */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 7000, // Total sectors + 512, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail(FX_SUCCESS == status); + +#ifdef FX_ENABLE_FAULT_TOLERANT + + /* Enable the Fault-tolerant feature. */ + status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE); + return_if_fail(FX_SUCCESS == status); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Create a file called TEST.TXT in the root directory. */ + status += fx_file_create(&ram_disk, "TEST.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + return_if_fail(FX_SUCCESS == status); + + status = fx_file_write(&my_file, write_data, 512); + return_if_fail(FX_SUCCESS == status); + + /* Mark the cluster of root directory as Free and give it to the file being written. */ + ram_disk.fx_media_cluster_search_start = 0; + _fx_utility_FAT_entry_write(&ram_disk, 0, FX_FREE_CLUSTER); + status = fx_file_write(&my_file, write_data, 512); + return_if_fail(FX_SECTOR_INVALID == status); + + fx_file_close(&my_file); + fx_media_close(&ram_disk); + + printf("SUCCESS!\n"); + test_control_return(0); +} + diff --git a/test/regression_test/filex_media_abort_test.c b/test/regression_test/filex_media_abort_test.c new file mode 100644 index 0000000..49d674e --- /dev/null +++ b/test/regression_test/filex_media_abort_test.c @@ -0,0 +1,551 @@ +/* This FileX test concentrates on the basic media abort operation. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include +#include "fx_ram_driver_test.h" + +void test_control_return(UINT status); +#ifndef FX_DISABLE_CACHE +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 16*128 + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_MEDIA ram_disk2; +static FX_FILE my_file; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +static UCHAR *ram_disk_memory2; +static UCHAR *cache_buffer2; +#else +static UCHAR ram_disk_memory2[256*256]; +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR cache_buffer2[CACHE_SIZE]; +#endif + + + + +/* Define thread prototypes. */ + +void filex_media_abort_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_media_abort_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + cache_buffer2 = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + pointer = pointer + (256 * 128); + ram_disk_memory2 = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UCHAR local_buffer[32]; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Media abort test......................................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(2); + } + + /* try to abort before the media has been opened */ + status = fx_media_abort(&ram_disk); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(3); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + /* Check for an already created status. This is okay in small sector caches. */ + if (status != FX_ALREADY_CREATED) + { + + printf("ERROR!\n"); + test_control_return(3); + } + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the first 28 bytes of the test file. */ + status = fx_file_read(&my_file, local_buffer, 28, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + /* send null pointer to generate an error */ + status = fx_media_abort(FX_NULL); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(11); + } +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Abort the media. */ + status = fx_media_abort(&ram_disk); + + /* Re-Open the ram_disk. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check for status. */ + if (status != FX_SUCCESS) + { + + /* Check for an already created status... this is okay with small sector cache! */ + if (status != FX_ALREADY_CREATED) + { + + printf("ERROR!\n"); + test_control_return(3); + } + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the first 28 bytes of the test file. */ + status = fx_file_read(&my_file, local_buffer, 28, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Format the second media. This needs to be done before opening it! */ + status += fx_media_format(&ram_disk2, + _fx_ram_driver, // Driver entry + ram_disk_memory2, // RAM disk memory pointer + cache_buffer2, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(12); + } + + /* Open the second ram_disk. */ + status = fx_media_open(&ram_disk2, "RAM DISK2", _fx_ram_driver, ram_disk_memory2, cache_buffer2, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error opening second media. Return to caller. */ + printf("ERROR!\n"); + test_control_return(13); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check for an error. */ + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(14); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Now abort the media with a file open and with another media instance open. */ + status = fx_media_abort(&ram_disk); + status += fx_media_abort(&ram_disk2); + + /* Check the media abort status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Format the second media. This needs to be done before opening it! */ + status += fx_media_format(&ram_disk2, + _fx_ram_driver, // Driver entry + ram_disk_memory2, // RAM disk memory pointer + cache_buffer2, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(18); + } + + /* Open the second ram_disk. */ + status = fx_media_open(&ram_disk2, "RAM DISK2", _fx_ram_driver, ram_disk_memory2, cache_buffer2, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error opening second media. Return to caller. */ + printf("ERROR!\n"); + test_control_return(19); + } + + /* Abort both media pointers. */ + status = fx_media_abort(&ram_disk2); + status += fx_media_abort(&ram_disk); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error opening second media. Return to caller. */ + printf("ERROR!\n"); + test_control_return(20); + } + else + { + + printf("SUCCESS!\n"); + test_control_return(0); + } +} + + +#else +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_media_abort_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Media abort test.......................................N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_media_cache_invalidate_test.c b/test/regression_test/filex_media_cache_invalidate_test.c new file mode 100644 index 0000000..6a883c6 --- /dev/null +++ b/test/regression_test/filex_media_cache_invalidate_test.c @@ -0,0 +1,509 @@ +/* This FileX test concentrates on the media cache invalidate operation. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include +#include "fx_ram_driver_test.h" + +void test_control_return(UINT status); + +#ifndef FX_DISABLE_CACHE +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 16*128 + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + + +/* Define thread prototypes. */ + +void filex_media_cache_invalidate_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_media_cache_invalidate_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +ULONG read_value; +ULONG write_value; +ULONG available_bytes; +ULONG i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Media cache invalidate test............................"); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(2); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(21); + } + +#ifndef FX_DISABLE_ERROR_CHECKING + + /* send a null pointer to generate an error */ + status = fx_media_cache_invalidate(FX_NULL); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(8); + } +#endif + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Pickup the available bytes in the media. */ + status = fx_media_space_available(&ram_disk, &available_bytes); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (available_bytes < sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Loop to write successive bytes out to the file.... to fill the media! */ + i = 0; + write_value = 0; + while (i < available_bytes) + { + + /* Write 4 bytes to the file. */ + status = fx_file_write(&my_file, (void *) &write_value, sizeof(ULONG)); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Increment byte count. */ + i = i + sizeof(ULONG); + + /* Increment write value. */ + write_value++; + } + + /* Pickup the available bytes in the media again. */ + status = fx_media_space_available(&ram_disk, &i); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (i != 0)) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* At this point, we should invalidate the (which also flushes the cache) media to ensure that all + dirty sectors are written. */ + status = fx_media_cache_invalidate(&ram_disk); + + /* Check for flush errors. */ + if ((status != FX_SUCCESS) || (ram_disk.fx_media_sector_cache_dirty_count)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* See if any sectors are still valid in the cache. */ + for (i = 0; i < FX_MAX_SECTOR_CACHE; i++) + { + + /* Determine if this cache entry is still valid. */ + if (ram_disk.fx_media_sector_cache[i].fx_cached_sector_valid) + { + + printf("ERROR!\n"); + test_control_return(81); + } + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Now read in all the bytes again to make sure the file contents are really there. */ + i = 0; + read_value = 0; + while (i < available_bytes) + { + + /* Read 4 bytes from the file. */ + status = fx_file_read(&my_file, (void *) &read_value, sizeof(ULONG), &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 4) || (read_value != i/4)) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Increment byte count. */ + i = i + sizeof(ULONG); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Check the media close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Reformat the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Open the ram_disk, but do so to ensure non-hashed algorithm is used by supplying CACHE_SIZE-1. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE-1); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(14); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Pickup the available bytes in the media. */ + status = fx_media_space_available(&ram_disk, &available_bytes); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (available_bytes < sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Loop to write successive bytes out to the file.... to fill the media! */ + i = 0; + write_value = 0; + while (i < available_bytes) + { + + /* Write 4 bytes to the file. */ + status = fx_file_write(&my_file, (void *) &write_value, sizeof(ULONG)); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Increment byte count. */ + i = i + sizeof(ULONG); + + /* Increment write value. */ + write_value++; + } + + /* Pickup the available bytes in the media again. */ + status = fx_media_space_available(&ram_disk, &i); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (i != 0)) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* At this point, we should invalidate the media to ensure that all + dirty sectors are written. */ + status = fx_media_cache_invalidate(&ram_disk); + + /* Check for flush errors. */ + if ((status != FX_SUCCESS) || (ram_disk.fx_media_sector_cache_dirty_count)) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* See if any sectors are still valid in the cache. */ + for (i = 0; i < ram_disk.fx_media_sector_cache_size; i++) + { + + /* Determine if this cache entry is still valid. */ + if (ram_disk.fx_media_sector_cache[i].fx_cached_sector_valid) + { + + printf("ERROR!\n"); + test_control_return(81); + } + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(22); + } + + /* Now read in all the bytes again to make sure the file contents are really there. */ + i = 0; + read_value = 0; + while (i < available_bytes) + { + + /* Read 4 bytes from the file. */ + status = fx_file_read(&my_file, (void *) &read_value, sizeof(ULONG), &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 4) || (read_value != i/4)) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Increment byte count. */ + i = i + sizeof(ULONG); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Check the media close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(26); + } + else + { + + printf("SUCCESS!\n"); + test_control_return(0); + } +} + + +#else +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_media_cache_invalidate_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Media cache invalidate test............................N/A\n"); + + test_control_return(255); +} +#endif diff --git a/test/regression_test/filex_media_check_test.c b/test/regression_test/filex_media_check_test.c new file mode 100644 index 0000000..2cc7401 --- /dev/null +++ b/test/regression_test/filex_media_check_test.c @@ -0,0 +1,2246 @@ +/* This FileX test concentrates on the basic media check operation. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_ram_driver_test.h" +#include + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 16*128 +#define SCRATCH_MEMORY_SIZE 11000 + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; + +static UCHAR raw_sector_buffer[512]; +static UCHAR raw_sector_buffer_check[512]; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +static UCHAR *scratch_memory; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR scratch_memory[SCRATCH_MEMORY_SIZE]; +#endif + + +/* Define thread prototypes. */ + +void filex_media_check_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_media_check_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + pointer = pointer + (256*128); + scratch_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG errors_detected; +UINT i, j; +// A file name whose first byte is 0xe5. +UCHAR specified_ascii_name[] = { 0xe5, 'a', 'b', 'c', 0}; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Media check test......................................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Try to check the media before the media has been opened */ + status = fx_media_check(&ram_disk, scratch_memory, SCRATCH_MEMORY_SIZE, 0, &errors_detected); + return_if_fail( status == FX_MEDIA_NOT_OPEN); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + + /* send null pointer to generate an error */ + status = fx_media_check(FX_NULL, scratch_memory, SCRATCH_MEMORY_SIZE, 0, &errors_detected); + return_if_fail( status == FX_PTR_ERROR); + +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Check the media for errors. */ + status = fx_media_check(&ram_disk, scratch_memory, SCRATCH_MEMORY_SIZE, 0, &errors_detected); + return_if_fail( (status == FX_SUCCESS) && (errors_detected == 0)); + + /* Close the media and reopen. */ + status = fx_media_close(&ram_disk); + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Invalidate the cache. */ + status = fx_media_cache_invalidate(&ram_disk); + + /* Read the first FAT sector. */ + status += fx_media_read(&ram_disk, 1, (VOID *) raw_sector_buffer); + + /* Check the status. */ + return_if_fail((status == FX_SUCCESS) && + (raw_sector_buffer[0] == 0xF8) && /* _fx_media_format_media_type value set during media format */ + (raw_sector_buffer[1] == 0xFF) && + (raw_sector_buffer[2] == 0xFF)); + + /* Change the FAT table to introduce lost clusters! */ + raw_sector_buffer[30] = 2; + raw_sector_buffer[31] = 3; + raw_sector_buffer[32] = 4; + + /* Write the FAT sector back... with the errors. */ + status = fx_media_write(&ram_disk, 1, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Attempt to check the media but give it a bad scratch_memory_size to throw an error */ + status = fx_media_check(&ram_disk, scratch_memory, 0, 0, &errors_detected); + return_if_fail( status == FX_NOT_ENOUGH_MEMORY); + + /* Attempt to check the media but give it a bad scratch_memory_size to throw an error */ + status = fx_media_check(&ram_disk, scratch_memory, (ram_disk.fx_media_total_clusters / 8), 0, &errors_detected); + return_if_fail( status == FX_NOT_ENOUGH_MEMORY); + + /* Attempt to check the media but give it another bad scratch_memory_size to throw an error */ + status = fx_media_check(&ram_disk, scratch_memory, 977, 0, &errors_detected); + return_if_fail( status == FX_NOT_ENOUGH_MEMORY); + + /* Check the media for errors. */ + status = fx_media_check(&ram_disk, scratch_memory, SCRATCH_MEMORY_SIZE, 0, &errors_detected); + return_if_fail( (status == FX_SUCCESS) && (errors_detected == FX_LOST_CLUSTER_ERROR)); + + /* Check the media for errors again, but correct them this time! */ + status = fx_media_check(&ram_disk, scratch_memory, SCRATCH_MEMORY_SIZE, FX_LOST_CLUSTER_ERROR, &errors_detected); + return_if_fail( (status == FX_SUCCESS) && (errors_detected == FX_LOST_CLUSTER_ERROR)); + + /* Check the media for errors again, but this time it should be successful. */ + status = fx_media_check(&ram_disk, scratch_memory, SCRATCH_MEMORY_SIZE, FX_LOST_CLUSTER_ERROR, &errors_detected); + + /* Determine if any were found - should not have found any errors at this point. */ + return_if_fail( (status == FX_SUCCESS) && (errors_detected == 0)); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Create a file in the root directory. */ + status = fx_file_create(&ram_disk, "TEXT.TXT"); + + // Create the file whose name's first byte is 0xe5. + status += fx_file_create(&ram_disk, (CHAR *)specified_ascii_name); + + status += fx_file_create(&ram_disk, "A somewhat long file name.txt"); + status += fx_file_open(&ram_disk, &my_file, "TEXT.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, raw_sector_buffer, 128); + status += fx_file_write(&my_file, raw_sector_buffer, 128); + status += fx_file_write(&my_file, raw_sector_buffer, 128); + status += fx_media_check(&ram_disk, scratch_memory, SCRATCH_MEMORY_SIZE, 0, &errors_detected); /* try to check the media while a file is open */ + status += fx_file_close(&my_file); + status += fx_media_close(&ram_disk); + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Loop to clear the raw sector buffer... */ + for (i = 0; i < sizeof(raw_sector_buffer); i++) + { + + raw_sector_buffer[i] = 0; + } + + /* Setup original FAT table to break the File's cluster! */ + raw_sector_buffer[0] = 0x0F; + raw_sector_buffer[1] = 0x0f; + raw_sector_buffer[2] = 0xFF; + + /* Write the FAT sector back... with the errors. */ + status += fx_media_write(&ram_disk, 1, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_ACCESS_ERROR); + + /* Check the media for errors. */ + status = fx_media_check(&ram_disk, scratch_memory, SCRATCH_MEMORY_SIZE, 0, &errors_detected); + + /* Determine if any were found - should not have found any errors at this point. */ + return_if_fail( (status == FX_SUCCESS) && (errors_detected == (FX_FAT_CHAIN_ERROR | FX_DIRECTORY_ERROR))); + + /* Check the media for errors... and correct them this time! */ + status = fx_media_check(&ram_disk, scratch_memory, SCRATCH_MEMORY_SIZE, (FX_FAT_CHAIN_ERROR | FX_DIRECTORY_ERROR), &errors_detected); + + /* Determine if any were found - should not have found any errors at this point. */ + return_if_fail( (status == FX_SUCCESS) && (errors_detected == (FX_FAT_CHAIN_ERROR | FX_DIRECTORY_ERROR))); + + /* Check the media for errors... there should be none this time! */ + status = fx_media_check(&ram_disk, scratch_memory, SCRATCH_MEMORY_SIZE, (FX_FAT_CHAIN_ERROR | FX_DIRECTORY_ERROR), &errors_detected); + + /* Determine if any were found - should not have found any errors at this point. */ + return_if_fail( (status == FX_SUCCESS) && (errors_detected == 0)); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_file_create(&ram_disk, "TEXT.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEXT.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, raw_sector_buffer, 128); + status += fx_file_write(&my_file, raw_sector_buffer, 128); + status += fx_file_write(&my_file, raw_sector_buffer, 128); + status += fx_file_close(&my_file); + status += fx_media_close(&ram_disk); + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Read the root directory's first sector. */ + status += fx_media_read(&ram_disk, ram_disk.fx_media_root_sector_start, (VOID *) raw_sector_buffer); + + /* Set the size to 1 to generate a file size error. */ + raw_sector_buffer[0x1C] = 1; + raw_sector_buffer[0x1D] = 0; + raw_sector_buffer[0x1E] = 0; + raw_sector_buffer[0x1F] = 0; + + /* Write the root directory sector back... with the errors. */ + status += fx_media_write(&ram_disk, ram_disk.fx_media_root_sector_start, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Check the media for errors. */ + status = fx_media_check(&ram_disk, scratch_memory, SCRATCH_MEMORY_SIZE, 0, &errors_detected); + + /* Determine if any were found - should not have found any errors at this point. */ + return_if_fail( (status == FX_SUCCESS) && (errors_detected == FX_FILE_SIZE_ERROR)); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Clear the initial part of the RAM disk memory. */ + for (i = 0; i < 4096; i++) + { + ram_disk_memory[i] = 0; + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Setup a new, more complicated directory structure. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_file_create(&ram_disk, "ROOTF1.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF2.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_directory_create(&ram_disk, "\\SUB1"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "\\SUB1\\SUB1F1.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "\\SUB1\\SUB1F2.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_directory_create(&ram_disk, "\\SUB1\\SUB2"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "\\SUB1\\SUB2\\SUB2F1.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "\\SUB1\\SUB2\\SUB2F2.TXT"); + return_if_fail( status == FX_SUCCESS); + + /* Write data to the files... */ + status += fx_file_open(&ram_disk, &my_file, "ROOTF1.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOT1_CONTENTS", 14); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF2.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOT2_CONTENTS", 14); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "\\SUB1\\SUB1F1.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "SUB1F1_CONTENTS", 15); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "\\SUB1\\SUB1F2.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "SUB1F2_CONTENTS", 15); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "\\SUB1\\SUB2\\SUB2F1.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "SUB2F1_CONTENTS", 15); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "\\SUB1\\SUB2\\SUB2F2.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "SUB2F2_CONTENTS", 15); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Check the media for errors. */ + status = fx_media_check(&ram_disk, scratch_memory, SCRATCH_MEMORY_SIZE, 0, &errors_detected); + return_if_fail( status == FX_SUCCESS); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Clear the initial part of the RAM disk memory. */ + for (i = 0; i < 4096; i++) + { + ram_disk_memory[i] = 0; + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Setup a new, more complicated directory structure. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_file_create(&ram_disk, "ROOTF1.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF2.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_directory_create(&ram_disk, "\\SUB1"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "\\SUB1\\SUB1F1.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "\\SUB1\\SUB1F2.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_directory_create(&ram_disk, "\\SUB1\\SUB2"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "\\SUB1\\SUB2\\SUB2F1.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "\\SUB1\\SUB2\\SUB2F2.TXT"); + return_if_fail( status == FX_SUCCESS); + + /* Write data to the files... */ + status += fx_file_open(&ram_disk, &my_file, "ROOTF1.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOT1_CONTENTS", 14); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF2.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOT2_CONTENTS", 14); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "\\SUB1\\SUB1F1.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "SUB1F1_CONTENTS", 15); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "\\SUB1\\SUB1F2.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "SUB1F2_CONTENTS", 15); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "\\SUB1\\SUB2\\SUB2F1.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "SUB2F1_CONTENTS", 15); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "\\SUB1\\SUB2\\SUB2F2.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "SUB2F2_CONTENTS", 15); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Check the media for errors. */ + status = fx_media_check(&ram_disk, scratch_memory, 11000, 0, &errors_detected); + return_if_fail( status == FX_SUCCESS); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Test for two files with the same FAT chain. */ + + /* Clear the initial part of the RAM disk memory. */ + for (i = 0; i < 4096; i++) + { + ram_disk_memory[i] = 0; + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 6000, // Total sectors - FAT16 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Setup a new, more complicated directory structure. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_file_create(&ram_disk, "ROOTF1.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF2.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF3.TXT"); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF1.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOTF1_CONTENTS", 130); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF2.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOTF2_CONTENTS", 130); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF3.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOTF3_CONTENTS", 130); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Read the root directory's first sector. */ + status += fx_media_read(&ram_disk, ram_disk.fx_media_root_sector_start, (VOID *) raw_sector_buffer); + + /* Set the second file's FAT chain to the same as the first. */ + raw_sector_buffer[0x3A] = raw_sector_buffer[0x1A]; + raw_sector_buffer[0x3B] = raw_sector_buffer[0x1B]; + + /* Write the root directory sector back... with the errors. */ + status += fx_media_write(&ram_disk, ram_disk.fx_media_root_sector_start, (VOID *) raw_sector_buffer); + status += fx_media_flush(&ram_disk); + + /* Read the first FAT sector. */ + status += fx_media_read(&ram_disk, ram_disk.fx_media_reserved_sectors, (VOID *) raw_sector_buffer); + + /* Break the FAT chain of the ROOTF1.TXT file. */ + raw_sector_buffer[14] = 0; + raw_sector_buffer[15] = 0; + + /* Write the root directory sector back... with the errors. */ + status += fx_media_write(&ram_disk, ram_disk.fx_media_reserved_sectors, (VOID *) raw_sector_buffer); + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Check the media for errors. */ + status = fx_media_check(&ram_disk, scratch_memory, 4000, (FX_FAT_CHAIN_ERROR | FX_DIRECTORY_ERROR | FX_LOST_CLUSTER_ERROR), &errors_detected); + return_if_fail((status == FX_SUCCESS) && (errors_detected == (FX_FAT_CHAIN_ERROR | FX_DIRECTORY_ERROR | FX_LOST_CLUSTER_ERROR))); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Test I/O errors in the two files with the same FAT chain test.... */ + + /* Clear the initial part of the RAM disk memory. */ + for (i = 0; i < 4096; i++) + { + ram_disk_memory[i] = 0; + } + + /* Loop to run through multiple tests... */ + for (i = 0; i < 1000; i++) + { + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 6000, // Total sectors - FAT16 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Setup a new, more complicated directory structure. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128 /*CACHE_SIZE*/); + status += fx_file_create(&ram_disk, "ROOTF1.TXT"); + status += fx_file_create(&ram_disk, "ROOTF2.TXT"); + status += fx_file_create(&ram_disk, "ROOTF3.TXT"); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF1.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOTF1_CONTENTS", 130); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF2.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOTF2_CONTENTS", 130); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF3.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOTF3_CONTENTS", 130); + status += fx_file_close(&my_file); + + /* Create some more files and sub-directories to give the media check some more work to do. */ + status += fx_file_create(&ram_disk, "ROOTF4.TXT"); + status += fx_file_create(&ram_disk, "ROOTF5.TXT"); + status += fx_file_create(&ram_disk, "ROOTF6.TXT"); + status += fx_file_create(&ram_disk, "ROOTF7.TXT"); + status += fx_file_create(&ram_disk, "ROOTF8.TXT"); + status += fx_file_create(&ram_disk, "ROOTF9.TXT"); + status += fx_file_create(&ram_disk, "ROOTF10.TXT"); + status += fx_file_create(&ram_disk, "ROOTF11.TXT"); + status += fx_file_create(&ram_disk, "ROOTF12.TXT"); + status += fx_file_create(&ram_disk, "ROOTF13.TXT"); + status += fx_file_create(&ram_disk, "ROOTF14.TXT"); + status += fx_file_create(&ram_disk, "ROOTF15.TXT"); + status += fx_file_create(&ram_disk, "ROOTF16.TXT"); + status += fx_file_create(&ram_disk, "ROOTF17.TXT"); + status += fx_file_create(&ram_disk, "ROOTF18.TXT"); + status += fx_file_create(&ram_disk, "ROOTF19.TXT"); + status += fx_file_create(&ram_disk, "ROOTF20.TXT"); + status += fx_directory_create(&ram_disk, "SUB4"); + status += fx_directory_create(&ram_disk, "SUB5"); + status += fx_directory_create(&ram_disk, "SUB6"); + status += fx_directory_create(&ram_disk, "SUB7"); + status += fx_directory_create(&ram_disk, "SUB8"); + status += fx_directory_create(&ram_disk, "SUB9"); + status += fx_directory_create(&ram_disk, "SUB10"); + status += fx_directory_create(&ram_disk, "SUB11"); + status += fx_directory_create(&ram_disk, "SUB12"); + status += fx_directory_create(&ram_disk, "SUB13"); + status += fx_file_create(&ram_disk, "\\SUB4\\ROOTF14.TXT"); + status += fx_file_create(&ram_disk, "\\SUB4\\ROOTF15.TXT"); + status += fx_file_create(&ram_disk, "\\SUB4\\ROOTF16.TXT"); + status += fx_file_create(&ram_disk, "\\SUB4\\ROOTF17.TXT"); + status += fx_file_create(&ram_disk, "\\SUB4\\ROOTF18.TXT"); + status += fx_file_create(&ram_disk, "\\SUB4\\ROOTF19.TXT"); + status += fx_file_create(&ram_disk, "\\SUB4\\ROOTF20.TXT"); + + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Read the root directory's first sector. */ + status += fx_media_read(&ram_disk, ram_disk.fx_media_root_sector_start, (VOID *) raw_sector_buffer); + + /* Set the second file's FAT chain starting cluster to the same as the first. */ + raw_sector_buffer[0x3A] = raw_sector_buffer[0x1A]; + raw_sector_buffer[0x3B] = raw_sector_buffer[0x1B]; + + /* Write the root directory sector back... with the errors. */ + status += fx_media_write(&ram_disk, ram_disk.fx_media_root_sector_start, (VOID *) raw_sector_buffer); + status += fx_media_flush(&ram_disk); + + /* Read the first FAT sector. */ + status += fx_media_read(&ram_disk, ram_disk.fx_media_reserved_sectors, (VOID *) raw_sector_buffer); + + /* Break the FAT chain of the ROOTF3.TXT file. */ + if (i & 1) + { + /* Break with bad FAT value. */ + raw_sector_buffer[14] = 0; + raw_sector_buffer[15] = 0; + } + else + { + + /* Break with reserved fAT value. */ + raw_sector_buffer[14] = 0xF0; + raw_sector_buffer[15] = 0xFF; + } + + /* Write the root directory sector back... with the errors. */ + status += fx_media_write(&ram_disk, ram_disk.fx_media_reserved_sectors, (VOID *) raw_sector_buffer); + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Create I/O errors via the RAM driver interface. */ + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + j = (UINT)(rand() % 20); + if (j == 0) + j = 1; + _fx_ram_driver_io_error_request = j; + + /* Check the media for errors. */ + fx_media_check(&ram_disk, scratch_memory, 4000, (((ULONG) i) % 16), &errors_detected); + + /* Clear the error generation flags. */ + _fx_ram_driver_io_error_request = 0; + + /* Abort the media. */ + fx_media_abort(&ram_disk); + } + + /* FAT32 I/O error check. */ + + /* Loop to run through multiple tests... */ + for (i = 0; i < 1000; i++) + { + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors - FAT32 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Setup a new, more complicated directory structure. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128 /*CACHE_SIZE*/); + status += fx_file_create(&ram_disk, "ROOTF1.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF2.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF3.TXT"); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF1.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOTF1_CONTENTS", 130); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF2.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOTF2_CONTENTS", 130); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF3.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOTF3_CONTENTS", 130); + status += fx_file_close(&my_file); + + /* Create some more files and sub-directories to give the media check some more work to do. */ + status += fx_file_create(&ram_disk, "ROOTF4.TXT"); + status += fx_file_create(&ram_disk, "ROOTF5.TXT"); + status += fx_file_create(&ram_disk, "ROOTF6.TXT"); + status += fx_file_create(&ram_disk, "ROOTF7.TXT"); + status += fx_file_create(&ram_disk, "ROOTF8.TXT"); + status += fx_file_create(&ram_disk, "ROOTF9.TXT"); + status += fx_file_create(&ram_disk, "ROOTF10.TXT"); + status += fx_file_create(&ram_disk, "ROOTF11.TXT"); + status += fx_file_create(&ram_disk, "ROOTF12.TXT"); + status += fx_file_create(&ram_disk, "ROOTF13.TXT"); + status += fx_file_create(&ram_disk, "ROOTF14.TXT"); + status += fx_file_create(&ram_disk, "ROOTF15.TXT"); + status += fx_file_create(&ram_disk, "ROOTF16.TXT"); + status += fx_file_create(&ram_disk, "ROOTF17.TXT"); + status += fx_file_create(&ram_disk, "ROOTF18.TXT"); + status += fx_file_create(&ram_disk, "ROOTF19.TXT"); + status += fx_file_create(&ram_disk, "ROOTF20.TXT"); + status += fx_directory_create(&ram_disk, "SUB4"); + status += fx_directory_create(&ram_disk, "SUB5"); + status += fx_directory_create(&ram_disk, "SUB6"); + status += fx_directory_create(&ram_disk, "SUB7"); + status += fx_directory_create(&ram_disk, "SUB8"); + status += fx_directory_create(&ram_disk, "SUB9"); + status += fx_directory_create(&ram_disk, "SUB10"); + status += fx_directory_create(&ram_disk, "SUB11"); + status += fx_directory_create(&ram_disk, "SUB12"); + status += fx_file_create(&ram_disk, "\\SUB4\\ROOTF14.TXT"); + status += fx_file_create(&ram_disk, "\\SUB4\\ROOTF15.TXT"); + status += fx_file_create(&ram_disk, "\\SUB4\\ROOTF16.TXT"); + status += fx_file_create(&ram_disk, "\\SUB4\\ROOTF17.TXT"); + status += fx_file_create(&ram_disk, "\\SUB4\\ROOTF18.TXT"); + status += fx_file_create(&ram_disk, "\\SUB4\\ROOTF19.TXT"); + status += fx_file_create(&ram_disk, "\\SUB4\\ROOTF20.TXT"); + + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Read the root directory's first sector. */ + status += fx_media_read(&ram_disk, ram_disk.fx_media_root_sector_start, (VOID *) raw_sector_buffer); + + /* Set the second file's FAT chain starting cluster to the same as the first. */ + raw_sector_buffer[0x3A] = raw_sector_buffer[0x1A]; + raw_sector_buffer[0x3B] = raw_sector_buffer[0x1B]; + + /* Write the root directory sector back... with the errors. */ + status += fx_media_write(&ram_disk, ram_disk.fx_media_root_sector_start, (VOID *) raw_sector_buffer); + status += fx_media_flush(&ram_disk); + + /* Read the first FAT sector. */ + status += fx_media_read(&ram_disk, ram_disk.fx_media_reserved_sectors, (VOID *) raw_sector_buffer); + + /* Break the FAT chain of the ROOTF3.TXT file. */ + if (i & 1) + { + /* Break with NULL FAT value. */ + raw_sector_buffer[32] = 0; + raw_sector_buffer[33] = 0; + raw_sector_buffer[34] = 0; + raw_sector_buffer[35] = 0; + } + else + { + /* Break with Reserved FAT value. */ + raw_sector_buffer[32] = 0xF0; + raw_sector_buffer[33] = 0xFF; + raw_sector_buffer[34] = 0xFF; + raw_sector_buffer[35] = 0xFF; + } + + /* Write the root directory sector back... with the errors. */ + status += fx_media_write(&ram_disk, ram_disk.fx_media_reserved_sectors, (VOID *) raw_sector_buffer); + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Create I/O errors via the RAM driver interface. */ + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + j = (UINT)(rand() % 10); + if (j == 0) + j = 1; + _fx_ram_driver_io_error_request = j; + + /* Check the media for errors. */ + fx_media_check(&ram_disk, scratch_memory, 4000, (((ULONG) i) % 16), &errors_detected); + + /* Clear the error generation flags. */ + _fx_ram_driver_io_error_request = 0; + + /* Abort the media. */ + fx_media_abort(&ram_disk); + } + + /* FAT32 Test for two files with the same FAT chain. */ + + /* Clear the initial part of the RAM disk memory. */ + for (i = 0; i < 4096; i++) + { + ram_disk_memory[i] = 0; + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors - FAT32 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Setup a new, more complicated directory structure. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_file_create(&ram_disk, "ROOTF1.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF2.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF3.TXT"); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF1.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOTF1_CONTENTS", 130); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF2.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOTF2_CONTENTS", 130); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF3.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOTF3_CONTENTS", 130); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Read the root directory's first sector. */ + status += fx_media_read(&ram_disk, ram_disk.fx_media_root_sector_start, (VOID *) raw_sector_buffer); + + /* Set the second file's FAT chain to the same as the first. */ + raw_sector_buffer[0x3A] = raw_sector_buffer[0x1A]; + raw_sector_buffer[0x3B] = raw_sector_buffer[0x1B]; + + /* Write the root directory sector back... with the errors. */ + status += fx_media_write(&ram_disk, ram_disk.fx_media_root_sector_start, (VOID *) raw_sector_buffer); + status += fx_media_flush(&ram_disk); + + /* Read the first FAT sector. */ + status += fx_media_read(&ram_disk, ram_disk.fx_media_reserved_sectors, (VOID *) raw_sector_buffer); + + /* Break the FAT chain of the ROOTF3.TXT file. */ + raw_sector_buffer[32] = 0; + raw_sector_buffer[33] = 0; + raw_sector_buffer[34] = 0; + raw_sector_buffer[35] = 0; + + /* Write the root directory sector back... with the errors. */ + status += fx_media_write(&ram_disk, ram_disk.fx_media_reserved_sectors, (VOID *) raw_sector_buffer); + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Check the media for errors. */ + status = fx_media_check(&ram_disk, scratch_memory, 11000, (FX_FAT_CHAIN_ERROR | FX_DIRECTORY_ERROR | FX_LOST_CLUSTER_ERROR), &errors_detected); + + /* Determine if any were found - should not have found any errors at this point. */ + return_if_fail((status == FX_SUCCESS) && (errors_detected == (FX_FAT_CHAIN_ERROR | FX_DIRECTORY_ERROR | FX_LOST_CLUSTER_ERROR))); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* FAT32 Test for two files with the same FAT chain and a broken root directory FAT chain. */ + + /* Clear the initial part of the RAM disk memory. */ + for (i = 0; i < 4096; i++) + { + ram_disk_memory[i] = 0; + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors - FAT32 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Setup a new, more complicated directory structure. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_file_create(&ram_disk, "ROOTF1.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF2.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF3.TXT"); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF1.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOTF1_CONTENTS", 130); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF2.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOTF2_CONTENTS", 130); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF3.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOTF3_CONTENTS", 130); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Read the root directory's first sector. */ + status += fx_media_read(&ram_disk, ram_disk.fx_media_root_sector_start, (VOID *) raw_sector_buffer); + + /* Set the second file's FAT chain to the same as the first. */ + raw_sector_buffer[0x3A] = raw_sector_buffer[0x1A]; + raw_sector_buffer[0x3B] = raw_sector_buffer[0x1B]; + + /* Write the root directory sector back... with the errors. */ + status += fx_media_write(&ram_disk, ram_disk.fx_media_root_sector_start, (VOID *) raw_sector_buffer); + status += fx_media_flush(&ram_disk); + + /* Read the first FAT sector. */ + status += fx_media_read(&ram_disk, ram_disk.fx_media_reserved_sectors, (VOID *) raw_sector_buffer); + + /* Break the FAT chain of the root directory in FAT32. */ + raw_sector_buffer[8] = 0; + raw_sector_buffer[9] = 0; + raw_sector_buffer[10] = 0; + raw_sector_buffer[11] = 0; + + /* Break the FAT chain of the ROOTF3.TXT file. */ + raw_sector_buffer[32] = 0; + raw_sector_buffer[33] = 0; + raw_sector_buffer[34] = 0; + raw_sector_buffer[35] = 0; + + /* Write the root directory sector back... with the errors. */ + status += fx_media_write(&ram_disk, ram_disk.fx_media_reserved_sectors, (VOID *) raw_sector_buffer); + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Check the media for errors. */ + status = fx_media_check(&ram_disk, scratch_memory, 11000, (FX_FAT_CHAIN_ERROR | FX_DIRECTORY_ERROR | FX_LOST_CLUSTER_ERROR), &errors_detected); + return_if_fail( status == FX_ERROR_NOT_FIXED); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Test a full disk with a maximum cluster FAT chain. */ + + /* Clear the initial part of the RAM disk memory. */ + for (i = 0; i < 4096; i++) + { + ram_disk_memory[i] = 0; + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 6000, // Total sectors - FAT32 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Setup a new, more complicated directory structure. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_file_create(&ram_disk, "ROOTF1.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_open(&ram_disk, &my_file, "ROOTF1.TXT", FX_OPEN_FOR_WRITE); + do + { + status = fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + } while (status == FX_SUCCESS); + + /* Determine if the write had anything other than a no more space error. */ + return_if_fail( status == FX_NO_MORE_SPACE); + + status = fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Reduce the amount of clusters just to exercise the branch in the FAT chain search. */ + ram_disk.fx_media_total_clusters = ram_disk.fx_media_total_clusters - 4; + + /* Check the media for errors. */ + status = fx_media_check(&ram_disk, scratch_memory, 11000, (FX_FAT_CHAIN_ERROR | FX_DIRECTORY_ERROR | FX_LOST_CLUSTER_ERROR), &errors_detected); + + /* Undo the cluster adjustment. */ + ram_disk.fx_media_total_clusters = ram_disk.fx_media_total_clusters + 4; + return_if_fail( status == FX_SUCCESS); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Test sub-directory corruption problems. */ + + /* Clear the initial part of the RAM disk memory. */ + for (i = 0; i < 4096; i++) + { + ram_disk_memory[i] = 0; + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 6000, // Total sectors - FAT16 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Setup a deep directory structure. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_directory_create(&ram_disk, "\\DIR1"); + status += fx_file_create(&ram_disk, "\\DIR1\\SUB1"); + status += fx_file_create(&ram_disk, "\\DIR1\\SUB2"); + status += fx_file_create(&ram_disk, "\\DIR1\\SUB3"); + status += fx_file_create(&ram_disk, "\\DIR1\\SUB4"); + status += fx_file_create(&ram_disk, "\\DIR1\\SUB5"); + status += fx_file_create(&ram_disk, "\\DIR1\\SUB6"); + status += fx_file_create(&ram_disk, "\\DIR1\\SUB7"); + status += fx_file_create(&ram_disk, "\\DIR1\\SUB8"); + status += fx_file_create(&ram_disk, "\\DIR1\\SUB9"); + status += fx_file_create(&ram_disk, "\\DIR1\\SUB10"); + status += fx_file_create(&ram_disk, "\\DIR1\\SUB11"); + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Read the first FAT sector. */ + status += fx_media_read(&ram_disk, ram_disk.fx_media_reserved_sectors, (VOID *) raw_sector_buffer); + + /* Break the FAT chain of the in the first sub directory. */ + raw_sector_buffer[10] = 0; + raw_sector_buffer[11] = 0; + + /* Write the root directory sector back... with the errors. */ + status += fx_media_write(&ram_disk, ram_disk.fx_media_reserved_sectors, (VOID *) raw_sector_buffer); + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Check the media for errors. */ + status = fx_media_check(&ram_disk, scratch_memory, 4000, (FX_FAT_CHAIN_ERROR | FX_DIRECTORY_ERROR | FX_LOST_CLUSTER_ERROR), &errors_detected); + + /* Determine if any were found - should not have found any errors at this point. */ + return_if_fail((status == FX_SUCCESS) && (errors_detected == FX_FAT_CHAIN_ERROR)); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* FAT32 Test for root directory FAT chain recovery. */ + + /* Clear the initial part of the RAM disk memory. */ + for (i = 0; i < 4096; i++) + { + ram_disk_memory[i] = 0; + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors - FAT32 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Setup a new, more complicated directory structure. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_file_create(&ram_disk, "ROOTF1.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF2.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF3.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF4.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF5.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF6.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF7.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF8.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF9.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF10.TXT"); + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Read the first FAT sector. */ + status += fx_media_read(&ram_disk, ram_disk.fx_media_reserved_sectors, (VOID *) raw_sector_buffer); + + /* Break the FAT chain of the root directory in FAT32. */ + raw_sector_buffer[12] = 0; + raw_sector_buffer[13] = 0; + raw_sector_buffer[14] = 0; + raw_sector_buffer[15] = 0; + + /* Write the root directory sector back... with the errors. */ + status += fx_media_write(&ram_disk, ram_disk.fx_media_reserved_sectors, (VOID *) raw_sector_buffer); + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Check the media for errors. */ + status = fx_media_check(&ram_disk, scratch_memory, 11000, (FX_FAT_CHAIN_ERROR | FX_DIRECTORY_ERROR | FX_LOST_CLUSTER_ERROR), &errors_detected); +#if 0 + return_if_fail( status == FX_SECTOR_INVALID); +#else + return_if_fail( status == FX_SUCCESS); +#endif + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Test a full disk with a FAT chain that has a bad pointer to itself. */ + + /* Clear the initial part of the RAM disk memory. */ + for (i = 0; i < 4096; i++) + { + ram_disk_memory[i] = 0; + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 6000, // Total sectors - FAT32 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Setup a new, more complicated directory structure. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_file_create(&ram_disk, "ROOTF1.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_open(&ram_disk, &my_file, "ROOTF1.TXT", FX_OPEN_FOR_WRITE); + do + { + status = fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + } while (status == FX_SUCCESS); + + /* Determine if the write had anything other than a no more space error. */ + return_if_fail( status == FX_NO_MORE_SPACE); + + status = fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, ram_disk.fx_media_reserved_sectors, (VOID *) raw_sector_buffer); + + /* Make the FAT chain recursive. */ + raw_sector_buffer[6] = raw_sector_buffer[4]; + raw_sector_buffer[7] = raw_sector_buffer[5]; + + /* Write the root directory sector back... with the errors. */ + status += fx_media_write(&ram_disk, ram_disk.fx_media_reserved_sectors, (VOID *) raw_sector_buffer); + status += fx_media_flush(&ram_disk); + + /* Check the media for errors. */ + status += fx_media_check(&ram_disk, scratch_memory, 11000, (FX_FAT_CHAIN_ERROR | FX_DIRECTORY_ERROR | FX_LOST_CLUSTER_ERROR), &errors_detected); + return_if_fail( status == FX_SUCCESS); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Test a full disk with a sub-directory that has a bad FAT chain. */ + + /* Clear the initial part of the RAM disk memory. */ + for (i = 0; i < 4096; i++) + { + ram_disk_memory[i] = 0; + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 6000, // Total sectors - FAT32 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Setup a new, more complicated directory structure. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_directory_create(&ram_disk, "SUB1"); + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, ram_disk.fx_media_reserved_sectors, (VOID *) raw_sector_buffer); + + /* Make the FAT chain recursive. */ + raw_sector_buffer[4] = 0; + raw_sector_buffer[5] = 0; + + /* Write the FAT sector back... with the errors. */ + status += fx_media_write(&ram_disk, ram_disk.fx_media_reserved_sectors, (VOID *) raw_sector_buffer); + status += fx_media_flush(&ram_disk); + + /* Read the root directory's first sector. */ + status += fx_media_read(&ram_disk, ram_disk.fx_media_root_sector_start, (VOID *) raw_sector_buffer); + + /* Check the media for errors. */ + status += fx_media_check(&ram_disk, scratch_memory, 11000, (FX_FAT_CHAIN_ERROR | FX_DIRECTORY_ERROR | FX_LOST_CLUSTER_ERROR), &errors_detected); + return_if_fail( status == FX_SUCCESS); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Test sub-directory depth that exceeds FX_MAX_DIRECTORY_NESTING (which is by default 20). */ + + /* Clear the initial part of the RAM disk memory. */ + for (i = 0; i < 4096; i++) + { + ram_disk_memory[i] = 0; + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 6000, // Total sectors - FAT32 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Setup a new, more complicated directory structure. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_directory_create(&ram_disk, "\\SUB1"); + status += fx_directory_create(&ram_disk, "\\SUB1\\SUB2"); + status += fx_directory_create(&ram_disk, "\\SUB1\\SUB2\\SUB3"); + status += fx_directory_create(&ram_disk, "\\SUB1\\SUB2\\SUB3\\SUB4"); + status += fx_directory_create(&ram_disk, "\\SUB1\\SUB2\\SUB3\\SUB4\\SUB5"); + status += fx_directory_create(&ram_disk, "\\SUB1\\SUB2\\SUB3\\SUB4\\SUB5\\SUB6"); + status += fx_directory_create(&ram_disk, "\\SUB1\\SUB2\\SUB3\\SUB4\\SUB5\\SUB6\\SUB7"); + status += fx_directory_create(&ram_disk, "\\SUB1\\SUB2\\SUB3\\SUB4\\SUB5\\SUB6\\SUB7\\SUB8"); + status += fx_directory_create(&ram_disk, "\\SUB1\\SUB2\\SUB3\\SUB4\\SUB5\\SUB6\\SUB7\\SUB8\\SUB9"); + status += fx_directory_create(&ram_disk, "\\SUB1\\SUB2\\SUB3\\SUB4\\SUB5\\SUB6\\SUB7\\SUB8\\SUB9\\SUB10"); + status += fx_directory_create(&ram_disk, "\\SUB1\\SUB2\\SUB3\\SUB4\\SUB5\\SUB6\\SUB7\\SUB8\\SUB9\\SUB10\\SUB11"); + status += fx_directory_create(&ram_disk, "\\SUB1\\SUB2\\SUB3\\SUB4\\SUB5\\SUB6\\SUB7\\SUB8\\SUB9\\SUB10\\SUB11\\SUB12"); + status += fx_directory_create(&ram_disk, "\\SUB1\\SUB2\\SUB3\\SUB4\\SUB5\\SUB6\\SUB7\\SUB8\\SUB9\\SUB10\\SUB11\\SUB12\\SUB13"); + status += fx_directory_create(&ram_disk, "\\SUB1\\SUB2\\SUB3\\SUB4\\SUB5\\SUB6\\SUB7\\SUB8\\SUB9\\SUB10\\SUB11\\SUB12\\SUB13\\SUB14"); + status += fx_directory_create(&ram_disk, "\\SUB1\\SUB2\\SUB3\\SUB4\\SUB5\\SUB6\\SUB7\\SUB8\\SUB9\\SUB10\\SUB11\\SUB12\\SUB13\\SUB14\\SUB15"); + status += fx_directory_create(&ram_disk, "\\SUB1\\SUB2\\SUB3\\SUB4\\SUB5\\SUB6\\SUB7\\SUB8\\SUB9\\SUB10\\SUB11\\SUB12\\SUB13\\SUB14\\SUB15\\SUB16"); + status += fx_directory_create(&ram_disk, "\\SUB1\\SUB2\\SUB3\\SUB4\\SUB5\\SUB6\\SUB7\\SUB8\\SUB9\\SUB10\\SUB11\\SUB12\\SUB13\\SUB14\\SUB15\\SUB16\\SUB17"); + status += fx_directory_create(&ram_disk, "\\SUB1\\SUB2\\SUB3\\SUB4\\SUB5\\SUB6\\SUB7\\SUB8\\SUB9\\SUB10\\SUB11\\SUB12\\SUB13\\SUB14\\SUB15\\SUB16\\SUB17\\SUB18"); + status += fx_directory_create(&ram_disk, "\\SUB1\\SUB2\\SUB3\\SUB4\\SUB5\\SUB6\\SUB7\\SUB8\\SUB9\\SUB10\\SUB11\\SUB12\\SUB13\\SUB14\\SUB15\\SUB16\\SUB17\\SUB18\\SUB19"); + status += fx_directory_create(&ram_disk, "\\SUB1\\SUB2\\SUB3\\SUB4\\SUB5\\SUB6\\SUB7\\SUB8\\SUB9\\SUB10\\SUB11\\SUB12\\SUB13\\SUB14\\SUB15\\SUB16\\SUB17\\SUB18\\SUB19\\SUB20"); + status += fx_directory_create(&ram_disk, "\\SUB1\\SUB2\\SUB3\\SUB4\\SUB5\\SUB6\\SUB7\\SUB8\\SUB9\\SUB10\\SUB11\\SUB12\\SUB13\\SUB14\\SUB15\\SUB16\\SUB17\\SUB18\\SUB19\\SUB20\\SUB21"); + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Check the media for errors. */ + status = fx_media_check(&ram_disk, scratch_memory, 11000, (FX_FAT_CHAIN_ERROR | FX_DIRECTORY_ERROR | FX_LOST_CLUSTER_ERROR), &errors_detected); + return_if_fail( status == FX_NOT_ENOUGH_MEMORY); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Test a full disk with a sub-directory that has a bad FAT chain, but without directory correction selected. */ + + /* Clear the initial part of the RAM disk memory. */ + for (i = 0; i < 4096; i++) + { + ram_disk_memory[i] = 0; + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 6000, // Total sectors - FAT32 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Setup a new, more complicated directory structure. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_directory_create(&ram_disk, "SUB1"); + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, ram_disk.fx_media_reserved_sectors, (VOID *) raw_sector_buffer); + + /* Make the FAT chain recursive. */ + raw_sector_buffer[4] = 0; + raw_sector_buffer[5] = 0; + + /* Write the FAT sector back... with the errors. */ + status += fx_media_write(&ram_disk, ram_disk.fx_media_reserved_sectors, (VOID *) raw_sector_buffer); + status += fx_media_flush(&ram_disk); + + /* Read the root directory's first sector. */ + status += fx_media_read(&ram_disk, ram_disk.fx_media_root_sector_start, (VOID *) raw_sector_buffer); + + /* Check the media for errors. */ + status += fx_media_check(&ram_disk, scratch_memory, 11000, (FX_FAT_CHAIN_ERROR | FX_LOST_CLUSTER_ERROR), &errors_detected); + return_if_fail( status == FX_SUCCESS); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* FAT32 Test for sixteen files with a broken root directory FAT chain. */ + + /* Clear the initial part of the RAM disk memory. */ + for (i = 0; i < 4096; i++) + { + ram_disk_memory[i] = 0; + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors - FAT32 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Setup a new, more complicated directory structure. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_file_create(&ram_disk, "ROOTF1.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF2.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF3.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF4.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF5.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF6.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF7.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF8.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF9.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF10.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF11.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF12.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF13.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF14.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF15.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF16.TXT"); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF1.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOTF1_CONTENTS", 130); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF2.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOTF2_CONTENTS", 130); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF3.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOTF3_CONTENTS", 130); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Read the root directory's first sector. */ + status += fx_media_read(&ram_disk, ram_disk.fx_media_root_sector_start, (VOID *) raw_sector_buffer); + + /* Set the second file's FAT chain to the same as the first. */ + raw_sector_buffer[0x3A] = raw_sector_buffer[0x1A]; + raw_sector_buffer[0x3B] = raw_sector_buffer[0x1B]; + + /* Write the root directory sector back... with the errors. */ + status += fx_media_write(&ram_disk, ram_disk.fx_media_root_sector_start, (VOID *) raw_sector_buffer); + status += fx_media_flush(&ram_disk); + + /* Read the first FAT sector. */ + status += fx_media_read(&ram_disk, ram_disk.fx_media_reserved_sectors, (VOID *) raw_sector_buffer); + + /* Break the FAT chain of the root directory in FAT32. */ + raw_sector_buffer[12] = 0; + raw_sector_buffer[13] = 0; + raw_sector_buffer[14] = 0; + raw_sector_buffer[15] = 0; + + /* Write the root directory sector back... with the errors. */ + status += fx_media_write(&ram_disk, ram_disk.fx_media_reserved_sectors, (VOID *) raw_sector_buffer); + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Check the media for errors. */ + status = fx_media_check(&ram_disk, scratch_memory, 11000, (FX_DIRECTORY_ERROR | FX_LOST_CLUSTER_ERROR), &errors_detected); + return_if_fail( status == FX_ERROR_NOT_FIXED); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + +#ifndef FX_DISABLE_ERROR_CHECKING + + /* Test NULL media pointer error checking. */ + status = fx_media_check(FX_NULL, scratch_memory, 11000, (FX_DIRECTORY_ERROR | FX_LOST_CLUSTER_ERROR), &errors_detected); + return_if_fail( status == FX_PTR_ERROR); + + /* Test NULL media pointer error checking. */ + status = fx_media_check(&ram_disk, FX_NULL, 11000, (FX_DIRECTORY_ERROR | FX_LOST_CLUSTER_ERROR), &errors_detected); + return_if_fail( status == FX_PTR_ERROR); + +#endif //FX_DISABLE_ERROR_CHECKING + + /* FAT32 Test for sixteen files with a broken root directory FAT chain and 512 byte sectors. */ + + /* Clear the initial part of the RAM disk memory. */ + for (i = 0; i < 4096; i++) + { + ram_disk_memory[i] = 0; + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors - FAT32 + 512, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Setup a new, more complicated directory structure. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_file_create(&ram_disk, "ROOTF1.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF2.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF3.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF4.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF5.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF6.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF7.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF8.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF9.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF10.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF11.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF12.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF13.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF14.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF15.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF16.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF17.TXT"); /* Ensure the root directory exceeds the 512 byte sector size and thus has a cluster chain! */ + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF1.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOTF1_CONTENTS", 130); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF2.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOTF2_CONTENTS", 130); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF3.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOTF3_CONTENTS", 130); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Read the root directory's first sector. */ + status += fx_media_read(&ram_disk, ram_disk.fx_media_root_sector_start, (VOID *) raw_sector_buffer); + + /* Set the second file's FAT chain to the same as the first. */ + raw_sector_buffer[0x3A] = raw_sector_buffer[0x1A]; + raw_sector_buffer[0x3B] = raw_sector_buffer[0x1B]; + + /* Write the root directory sector back... with the errors. */ + status += fx_media_write(&ram_disk, ram_disk.fx_media_root_sector_start, (VOID *) raw_sector_buffer); + status += fx_media_flush(&ram_disk); + + /* Read the first FAT sector. */ + status += fx_media_read(&ram_disk, ram_disk.fx_media_reserved_sectors, (VOID *) raw_sector_buffer); + + /* Break the FAT chain of the root directory in FAT32. */ + raw_sector_buffer[12] = 0; + raw_sector_buffer[13] = 0; + raw_sector_buffer[14] = 0; + raw_sector_buffer[15] = 0; + + /* Write the root directory sector back... with the errors. */ + status += fx_media_write(&ram_disk, ram_disk.fx_media_reserved_sectors, (VOID *) raw_sector_buffer); + status += fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Check the media for errors. */ + status = fx_media_check(&ram_disk, scratch_memory, 11000, (FX_DIRECTORY_ERROR | FX_LOST_CLUSTER_ERROR), &errors_detected); + return_if_fail( status == FX_ERROR_NOT_FIXED); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + +#ifndef FX_DISABLE_ERROR_CHECKING + + /* Test NULL media pointer error checking. */ + status = fx_media_check(FX_NULL, scratch_memory, 11000, (FX_DIRECTORY_ERROR | FX_LOST_CLUSTER_ERROR), &errors_detected); + return_if_fail( status == FX_PTR_ERROR); + + /* Test NULL media pointer error checking. */ + status = fx_media_check(&ram_disk, FX_NULL, 11000, (FX_DIRECTORY_ERROR | FX_LOST_CLUSTER_ERROR), &errors_detected); + return_if_fail( status == FX_PTR_ERROR); + +#endif //FX_DISABLE_ERROR_CHECKING + + /* FAT32 Test for formatting with I/O errors. */ + + /* Clear the initial part of the RAM disk memory. */ + for (i = 0; i < 4096; i++) + { + ram_disk_memory[i] = 0; + } + for (i = 0; i < 4000; i++) + { + + /* Create I/O errors via the RAM driver interface. */ + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + j = (UINT)(rand() % 10); + if (j == 0) + j = 1; + _fx_ram_driver_io_error_request = j; + + /* Format the media. This needs to be done before opening it! */ + fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MYDISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors - FAT32 + 512, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + j = (UINT)(rand() % 10); + if (j == 0) + j = 1; + _fx_ram_driver_io_error_request = j; + + /* Open media with errors. */ + fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 512); + + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + j = (UINT)(rand() % 10); + if (j == 0) + j = 1; + _fx_ram_driver_io_error_request = j; + + /* Attempt to close media with errors. */ + fx_media_close(&ram_disk); + + _fx_utility_FAT_flush(&ram_disk); + + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + j = (UINT)(rand() % 10); + if (j == 0) + j = 1; + _fx_ram_driver_io_error_request = j; + + /* Attempt to abort media with errors. */ + fx_media_abort(&ram_disk); + + _fx_ram_driver_io_error_request = 0; + } + + /* Test I/O errors in _fx_media_check_lost_cluster_check. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 6000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + return_if_fail( status == FX_SUCCESS); + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 2, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Change the FAT table to introduce lost clusters! */ + raw_sector_buffer[30] = 2; + raw_sector_buffer[31] = 3; + raw_sector_buffer[32] = 4; + + /* Write the FAT sector back... with the errors. */ + status = fx_media_write(&ram_disk, 2, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 3, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Change the FAT table to introduce lost clusters! */ + raw_sector_buffer[30] = 5; + raw_sector_buffer[31] = 6; + raw_sector_buffer[32] = 7; + + /* Write the FAT sector back... with the errors. */ + status = fx_media_write(&ram_disk, 3, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 4, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Change the FAT table to introduce lost clusters! */ + raw_sector_buffer[30] = 8; + raw_sector_buffer[31] = 9; + raw_sector_buffer[32] = 10; + + /* Write the FAT sector back... with the errors. */ + status = fx_media_write(&ram_disk, 4, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Flush and invalidate the cache. */ + _fx_utility_logical_sector_flush(&ram_disk, 0, 4000, FX_TRUE); + + /* Force media errors. */ + _fx_utility_fat_entry_read_error_request = 1; + status = fx_media_check(&ram_disk, scratch_memory, SCRATCH_MEMORY_SIZE, FX_LOST_CLUSTER_ERROR, &errors_detected); + _fx_utility_fat_entry_read_error_request = 1000; + fx_media_check(&ram_disk, scratch_memory, SCRATCH_MEMORY_SIZE, FX_LOST_CLUSTER_ERROR, &errors_detected); + _fx_utility_fat_entry_read_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 6000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + return_if_fail( status == FX_SUCCESS); + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 2, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Change the FAT table to introduce lost clusters! */ + raw_sector_buffer[30] = 2; + raw_sector_buffer[31] = 3; + raw_sector_buffer[32] = 4; + + /* Write the FAT sector back... with the errors. */ + status = fx_media_write(&ram_disk, 2, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 3, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Change the FAT table to introduce lost clusters! */ + raw_sector_buffer[30] = 5; + raw_sector_buffer[31] = 6; + raw_sector_buffer[32] = 7; + + /* Write the FAT sector back... with the errors. */ + status = fx_media_write(&ram_disk, 3, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 4, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Change the FAT table to introduce lost clusters! */ + raw_sector_buffer[30] = 8; + raw_sector_buffer[31] = 9; + raw_sector_buffer[32] = 10; + + /* Write the FAT sector back... with the errors. */ + status = fx_media_write(&ram_disk, 4, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Flush and invalidate the cache. */ + _fx_utility_logical_sector_flush(&ram_disk, 0, 4000, FX_TRUE); + + /* Check the media for errors. */ + _fx_utility_fat_entry_write_error_request = 1; + status = fx_media_check(&ram_disk, scratch_memory, SCRATCH_MEMORY_SIZE, FX_LOST_CLUSTER_ERROR, &errors_detected); + _fx_utility_fat_entry_write_error_request = 10000; + fx_media_check(&ram_disk, scratch_memory, SCRATCH_MEMORY_SIZE, FX_LOST_CLUSTER_ERROR, &errors_detected); + _fx_utility_fat_entry_write_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Test the error paths in FAT32 root directory traversal. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors - FAT32 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + return_if_fail( status == FX_SUCCESS); + + /* Now create a series to sub-directories to expand the root directory FAT chain. */ + status = fx_file_create(&ram_disk, "FILE1"); + status += fx_file_create(&ram_disk, "FILE2"); + status += fx_file_create(&ram_disk, "FILE3"); + status += fx_file_create(&ram_disk, "FILE4"); + status += fx_file_create(&ram_disk, "FILE5"); + status += fx_file_create(&ram_disk, "FILE6"); + status += fx_file_create(&ram_disk, "FILE7"); + status += fx_file_create(&ram_disk, "FILE8"); + status += fx_file_create(&ram_disk, "FILE9"); + status += fx_file_create(&ram_disk, "FILE10"); + status += fx_file_create(&ram_disk, "FILE11"); + status += fx_file_create(&ram_disk, "FILE12"); + status += fx_file_create(&ram_disk, "FILE13"); + status += fx_file_create(&ram_disk, "FILE14"); + status += fx_file_create(&ram_disk, "FILE15"); + status += fx_file_create(&ram_disk, "FILE16"); + status += fx_file_create(&ram_disk, "FILE17"); + status += fx_file_create(&ram_disk, "FILE18"); + status += fx_file_create(&ram_disk, "FILE19"); + status += fx_file_create(&ram_disk, "FILE20"); + status += fx_file_create(&ram_disk, "FILE21"); + status += fx_file_create(&ram_disk, "FILE22"); + status += fx_file_create(&ram_disk, "FILE23"); + status += fx_file_create(&ram_disk, "FILE24"); + status += fx_file_create(&ram_disk, "FILE25"); + status += fx_file_create(&ram_disk, "FILE26"); + status += fx_file_create(&ram_disk, "FILE27"); + status += fx_file_create(&ram_disk, "FILE28"); + status += fx_file_create(&ram_disk, "FILE29"); + status += fx_file_create(&ram_disk, "FILE30"); + status += fx_file_create(&ram_disk, "FILE31"); + status += fx_media_flush(&ram_disk); + + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 1, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Break the FAT chain of the root directory! */ + raw_sector_buffer[36] = 0; + raw_sector_buffer[37] = 0; + raw_sector_buffer[38] = 0; + raw_sector_buffer[39] = 0; + + /* Write the FAT sector back... with the errors. */ + status = fx_media_write(&ram_disk, 1, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Flush and invalidate the cache. */ + fx_media_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 0, 4000, FX_TRUE); + + /* Check the media for errors. */ + _fx_utility_fat_entry_read_error_request = 1; + status = fx_media_check(&ram_disk, scratch_memory, 11000, (FX_LOST_CLUSTER_ERROR | FX_DIRECTORY_ERROR | FX_FAT_CHAIN_ERROR), &errors_detected); + _fx_utility_fat_entry_read_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Check the media for errors. */ + _fx_utility_fat_entry_write_error_request = 1; + status = fx_media_check(&ram_disk, scratch_memory, 11000, (FX_LOST_CLUSTER_ERROR | FX_DIRECTORY_ERROR | FX_FAT_CHAIN_ERROR), &errors_detected); + _fx_utility_fat_entry_write_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Test the error paths in file FAT traversal. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors - FAT32 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + return_if_fail( status == FX_SUCCESS); + + /* Now create a series to sub-directories to expand the root directory FAT chain. */ + status = fx_file_create(&ram_disk, "FILE1"); + + /* Flush and invalidate the cache. */ + fx_media_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 0, 4000, FX_TRUE); + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 1, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Open the file. */ + status += fx_file_open(&ram_disk, &my_file, "FILE1", FX_OPEN_FOR_WRITE); + + /* Write to the file. */ + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_write(&my_file, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 1, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Break the FAT chain of the root directory! */ + raw_sector_buffer[36] = 0; + raw_sector_buffer[37] = 0; + raw_sector_buffer[38] = 0; + raw_sector_buffer[39] = 0; + + /* Write the FAT sector back... with the errors. */ + status = fx_media_write(&ram_disk, 1, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Flush and invalidate the cache. */ + fx_media_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 0, 4000, FX_TRUE); + + /* Check the media for errors. */ + _fx_utility_fat_entry_write_error_request = 1; + status = fx_media_check(&ram_disk, scratch_memory, 11000, (FX_LOST_CLUSTER_ERROR | FX_DIRECTORY_ERROR | FX_FAT_CHAIN_ERROR), &errors_detected); + _fx_utility_fat_entry_write_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Test the error paths in sub-directory delete path. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors - FAT32 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + return_if_fail( status == FX_SUCCESS); + + /* Now create a sub-directory. */ + status = fx_directory_create(&ram_disk, "SUB1"); + + /* Flush and invalidate the cache. */ + fx_media_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 0, 4000, FX_TRUE); + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 1, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Open the file. */ + status += fx_file_open(&ram_disk, &my_file, "FILE1", FX_OPEN_FOR_WRITE); + status += fx_media_flush(&ram_disk); + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 1, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Break the FAT chain of the sub-directory! */ + raw_sector_buffer[12] = 0; + raw_sector_buffer[13] = 0; + raw_sector_buffer[14] = 0; + raw_sector_buffer[15] = 0; + + /* Write the FAT sector back... with the errors. */ + status = fx_media_write(&ram_disk, 1, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Flush and invalidate the cache. */ + fx_media_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 0, 4000, FX_TRUE); + + /* Check the media for errors. */ + _fx_ram_driver_io_error_request = 4; + status = fx_media_check(&ram_disk, scratch_memory, 11000, (FX_LOST_CLUSTER_ERROR | FX_DIRECTORY_ERROR | FX_FAT_CHAIN_ERROR), &errors_detected); + _fx_ram_driver_io_error_request = 0; + return_if_fail( status == FX_IO_ERROR); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Create a file in the root directory. */ + status = fx_file_create(&ram_disk, "TEXT.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEXT.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, raw_sector_buffer, 128); + status += fx_file_close(&my_file); + return_if_fail( status == FX_SUCCESS); + + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 1, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Change the FAT table to introduce lost clusters! */ + memcpy(raw_sector_buffer_check, raw_sector_buffer, ram_disk.fx_media_bytes_per_sector); + raw_sector_buffer[3] = (ram_disk.fx_media_total_clusters + FX_FAT_ENTRY_START) & 0xFF; + raw_sector_buffer[4] = (UCHAR)((ram_disk.fx_media_total_clusters + FX_FAT_ENTRY_START) >> 8); + + /* Write the FAT sector back... with the errors. */ + status = fx_media_write(&ram_disk, 1, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Attempt to check the media but give it a bad scratch_memory_size to throw an error */ + status = fx_media_check(&ram_disk, scratch_memory, SCRATCH_MEMORY_SIZE, FX_FAT_CHAIN_ERROR, + &errors_detected); + return_if_fail( (status == FX_SUCCESS) && + (errors_detected == (FX_FAT_CHAIN_ERROR| FX_DIRECTORY_ERROR| FX_LOST_CLUSTER_ERROR))); + +#if 0 + /* Read the first FAT sector again. */ + status = fx_media_read(&ram_disk, 1, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Compare the */ + return_if_fail( memcmp(raw_sector_buffer_check, + raw_sector_buffer, + ram_disk.fx_media_bytes_per_sector) == 0); +#endif + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + printf("SUCCESS!\n"); + test_control_return(0); +} + diff --git a/test/regression_test/filex_media_flush_test.c b/test/regression_test/filex_media_flush_test.c new file mode 100644 index 0000000..58aaa8d --- /dev/null +++ b/test/regression_test/filex_media_flush_test.c @@ -0,0 +1,497 @@ +/* This FileX test concentrates on the media flush operation. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include +#include "fx_ram_driver_test.h" + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 16*128 + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + + +/* Define thread prototypes. */ + +void filex_media_flush_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_media_flush_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +ULONG read_value; +ULONG write_value; +ULONG available_bytes; +ULONG i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Media flush test......................................."); + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + /* send null pointer to generate an error */ + status = fx_media_format(FX_NULL, _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE, "MY_RAM_DISK", 1, 32, 0, 256, 128, 1, 1, 1); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(11); + } +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(2); + } + + /* try to flush the media before it is opened */ + status = fx_media_flush(&ram_disk); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(11); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(21); + } + + /* try to flush the media while it is write protected */ + ram_disk.fx_media_driver_write_protect = FX_TRUE; + status = fx_media_flush(&ram_disk); + if (status != FX_WRITE_PROTECT) + { + printf("ERROR!\n"); + test_control_return(11); + } + ram_disk.fx_media_driver_write_protect = FX_FALSE; + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Pickup the available bytes in the media. */ + status = fx_media_space_available(&ram_disk, &available_bytes); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (available_bytes < sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Loop to write successive bytes out to the file.... to fill the media! */ + i = 0; + write_value = 0; + while (i < available_bytes) + { + + /* Write 4 bytes to the file. */ + status = fx_file_write(&my_file, (void *) &write_value, sizeof(ULONG)); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Increment byte count. */ + i = i + sizeof(ULONG); + + /* Increment write value. */ + write_value++; + } + + /* Pickup the available bytes in the media again. */ + status = fx_media_space_available(&ram_disk, &i); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (i != 0)) + { + + printf("ERROR!\n"); + test_control_return(7); + } + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + /* send null pointer to generate an error */ + status = fx_media_flush(FX_NULL); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(11); + } +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* At this point, we should flush the media to ensure that all + dirty sectors are written. */ + status = fx_media_flush(&ram_disk); + +#ifndef FX_DISABLE_CACHE + /* Check for flush errors. */ + if ((status != FX_SUCCESS) || (ram_disk.fx_media_sector_cache_dirty_count)) + { + + printf("ERROR!\n"); + test_control_return(8); + } +#endif + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Now read in all the bytes again to make sure the file contents are really there. */ + i = 0; + read_value = 0; + while (i < available_bytes) + { + + /* Read 4 bytes from the file. */ + status = fx_file_read(&my_file, (void *) &read_value, sizeof(ULONG), &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 4) || (read_value != i/4)) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Increment byte count. */ + i = i + sizeof(ULONG); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Check the media close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Reformat the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Open the ram_disk, but do so to ensure non-hashed algorithm is used by supplying CACHE_SIZE-1. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE-1); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(14); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Pickup the available bytes in the media. */ + status = fx_media_space_available(&ram_disk, &available_bytes); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (available_bytes < sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Loop to write successive bytes out to the file.... to fill the media! */ + i = 0; + write_value = 0; + while (i < available_bytes) + { + + /* Write 4 bytes to the file. */ + status = fx_file_write(&my_file, (void *) &write_value, sizeof(ULONG)); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* Increment byte count. */ + i = i + sizeof(ULONG); + + /* Increment write value. */ + write_value++; + } + + /* Pickup the available bytes in the media again. */ + status = fx_media_space_available(&ram_disk, &i); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (i != 0)) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* At this point, we should flush the media to ensure that all + dirty sectors are written. */ + status = fx_media_flush(&ram_disk); + +#ifndef FX_DISABLE_CACHE + /* Check for flush errors. */ + if ((status != FX_SUCCESS) || (ram_disk.fx_media_sector_cache_dirty_count)) + { + + printf("ERROR!\n"); + test_control_return(20); + } +#endif + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(22); + } + + /* Now read in all the bytes again to make sure the file contents are really there. */ + i = 0; + read_value = 0; + while (i < available_bytes) + { + + /* Read 4 bytes from the file. */ + status = fx_file_read(&my_file, (void *) &read_value, sizeof(ULONG), &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 4) || (read_value != i/4)) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Increment byte count. */ + i = i + sizeof(ULONG); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Check the media close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(26); + } + else + { + + printf("SUCCESS!\n"); + test_control_return(0); + } +} + diff --git a/test/regression_test/filex_media_format_open_close_test.c b/test/regression_test/filex_media_format_open_close_test.c new file mode 100644 index 0000000..3aff351 --- /dev/null +++ b/test/regression_test/filex_media_format_open_close_test.c @@ -0,0 +1,1056 @@ +/* This FileX test concentrates on the basic media format, open, close operation. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include +#include "fx_ram_driver_test.h" + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 16*128 + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + + +/* Define thread prototypes. */ + +void filex_media_format_open_close_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_media_format_open_close_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +UCHAR local_buffer[32]; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + actual = FX_MAX_LONG_NAME_LEN; + if (actual < 256) + printf("**** ERROR ***** FileX and tests must be built with FX_MAX_LONG_NAME_LEN=256\n"); + actual = FX_MAX_LAST_NAME_LEN; + if (actual < 256) + printf("**** ERROR ***** FileX and tests must be built with FX_MAX_LAST_NAME_LEN=256\n"); + + /* Print out some test information banners. */ + printf("FileX Test: Media format, open, and close test....................."); + + /* Format the media with invalid parameters. */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 4097, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Check status. */ + if (status != FX_SECTOR_INVALID) + { + + printf("ERROR!\n"); + test_control_return(2); + } + + /* Format the media with invalid parameters. */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 0, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Check status. */ + if (status != FX_SECTOR_INVALID) + { + + printf("ERROR!\n"); + test_control_return(2); + } + + /* Format the media with invalid parameters. */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 129, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Check status. */ + if (status != FX_SECTOR_INVALID) + { + + printf("ERROR!\n"); + test_control_return(2); + } + + /* Format the media with invalid parameters. */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 0, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Check status. */ + if (status != FX_SECTOR_INVALID) + { + + printf("ERROR!\n"); + test_control_return(2); + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(2); + } + + /* Try to close the media before it has been opened */ + status = fx_media_close(&ram_disk); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(11); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + /* Check for an already created status. This is not fatal, just + let the user know. */ + if (status != FX_ALREADY_CREATED) + { + + printf("ERROR!\n"); + test_control_return(3); + } + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + +/* test error checking */ +#ifndef FX_DISABLE_ERROR_CHECKING + + /* send a null pointer to generate an error */ + status = fx_media_close(FX_NULL); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(8); + } + + /* send null pointer to generate an error */ + status = fx_media_open(FX_NULL, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(11); + } + + /* try to open an already open media to generate an error */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(11); + } + +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the first 28 bytes of the test file. */ + status = fx_file_read(&my_file, local_buffer, 28, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Re-Open the ram_disk. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check for an already created status. This is not fatal, just + let the user know. */ + if (status != FX_ALREADY_CREATED) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the first 28 bytes of the test file. */ + status = fx_file_read(&my_file, local_buffer, 28, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Corrupt the bytes per sector field. */ + _fx_utility_16_unsigned_write(&ram_disk_memory[FX_BYTES_SECTOR], 0); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_MEDIA_INVALID) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(12); + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Corrupt the total sectors field. */ + _fx_utility_16_unsigned_write(&ram_disk_memory[FX_SECTORS], 0); + _fx_utility_32_unsigned_write(&ram_disk_memory[FX_HUGE_SECTORS], 0); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_MEDIA_INVALID) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(14); + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Corrupt the reserved sectors field. */ + _fx_utility_16_unsigned_write(&ram_disk_memory[FX_RESERVED_SECTORS], 0); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_MEDIA_INVALID) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(16); + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Corrupt the sectors per cluster field. */ + ram_disk_memory[FX_SECTORS_CLUSTER] = 0; + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_MEDIA_INVALID) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(18); + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Corrupt the sectors per FAT field. */ + _fx_utility_16_unsigned_write(&ram_disk_memory[FX_SECTORS_PER_FAT], 0); + _fx_utility_32_unsigned_write(&ram_disk_memory[FX_SECTORS_PER_FAT_32], 0); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_MEDIA_INVALID) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(20); + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Corrupt the number of FATs field. */ + ram_disk_memory[FX_NUMBER_OF_FATS] = 0; + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_MEDIA_INVALID) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Format the media with an even number of FAT12 sectors. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256+9, // Total sectors - need 256 clusters for even FAT table logic to be tested + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_media_close(&ram_disk); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(24); + } + + /* Format the media with an even number of FAT16 sectors. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 6144+9, // Total sectors - need 6144 clusters for even FAT table logic to be tested + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_media_close(&ram_disk); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(26); + } + + + /* Format the media with an even number of FAT12 sectors. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256+9, // Total sectors - need 256 clusters for even FAT table logic to be tested + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Clear 480 bytes from offset 30. Verify the compatibility. + * They are reserved for FDC Descriptor. Some of them are defined in Extended FDC Descriptor. */ + memset(&ram_disk_memory[30], 0, 480); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_media_close(&ram_disk); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(24); + } + + /* Format the media with an even number of FAT16 sectors. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 6144+9, // Total sectors - need 6144 clusters for even FAT table logic to be tested + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Clear 480 bytes from offset 30. Verify the compatibility. + * They are reserved for FDC Descriptor. Some of them are defined in Extended FDC Descriptor. */ + memset(&ram_disk_memory[30], 0, 480); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_media_close(&ram_disk); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(26); + } + + /* Format the media with an even number of FAT32 sectors. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70656+9, // Total sectors - FAT32 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_media_close(&ram_disk); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(28); + } + + + /* Format the media with an even number of FAT32 sectors. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70656+9, // Total sectors - FAT32 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Corrupt the root cluster field. */ + ram_disk_memory[FX_ROOT_CLUSTER_32] = 0; + ram_disk_memory[FX_ROOT_CLUSTER_32 + 1] = 0; + ram_disk_memory[FX_ROOT_CLUSTER_32 + 2] = 0; + ram_disk_memory[FX_ROOT_CLUSTER_32 + 3] = 0; + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_MEDIA_INVALID) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(28); + } + + /* Format the media with volume name containing space! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "NO NAME", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + status += fx_media_volume_get(&ram_disk, (CHAR *)local_buffer, FX_BOOT_SECTOR); + + /* Check for status. */ + if ((status != FX_SUCCESS) || (memcmp(local_buffer, "NO NAME", 7))) + { + printf("ERROR!\n"); + test_control_return(30); + } + + status = fx_media_close(&ram_disk); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(31); + } + + memset(ram_disk_memory, 0, sizeof(ram_disk_memory)); + + /* Format the media with an even number of FAT32 sectors. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70656+9, // Total sectors - FAT32 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + unsigned index =0; + /* Check the FAT entry values, first byte starts with 0xF(_fx_media_format_media_type) */ + for(index=0; index< ((70656+9)*128);index++) + { + if((ram_disk_memory[index]==0xF8) && (ram_disk_memory[index+1]==0xFF) && (ram_disk_memory[index+2]==0xFF) && + (ram_disk_memory[index+3]==0x0F) && (ram_disk_memory[index+4]==0xFF) && (ram_disk_memory[index+5]==0xFF) && + (ram_disk_memory[index+6]==0xFF) && (ram_disk_memory[index+7]==0x0F)) + { + break; + } + } + + if(index ==(70656+9)*128) + { + + printf("ERROR!\n"); + test_control_return(33); + } + else + { + printf("SUCCESS!\n"); + test_control_return(0); + + } + +} + diff --git a/test/regression_test/filex_media_hidden_sectors_test.c b/test/regression_test/filex_media_hidden_sectors_test.c new file mode 100644 index 0000000..2ae0fff --- /dev/null +++ b/test/regression_test/filex_media_hidden_sectors_test.c @@ -0,0 +1,286 @@ +/* This FileX test concentrates on the media with hidden sectors. */ + +#include "fx_api.h" +#include "fx_ram_driver_test.h" + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 128*128 + +#define HIDDEN_SECTORS 8 + +#define SECTOR_SIZE 512 + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + + +/* Define thread prototypes. */ + +void filex_media_hidden_sectors_test_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); +static VOID hidden_sectors_driver(FX_MEDIA *media_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_media_hidden_sectors_test_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Media Hidden Sectors Test.............................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + hidden_sectors_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + HIDDEN_SECTORS, // Hidden sectors + 256, // Total sectors + SECTOR_SIZE, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(2); + } + + memset(&ram_disk, 0, sizeof(ram_disk)); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", hidden_sectors_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + memset(&ram_disk, 0, sizeof(ram_disk)); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", hidden_sectors_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(6); + } + + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Close the file. */ + status = fx_file_close(&my_file); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + else + { + + printf("SUCCESS!\n"); + test_control_return(0); + } +} + +static VOID hidden_sectors_driver(FX_MEDIA *media_ptr) +{ +UCHAR *source_buffer; +UCHAR *destination_buffer; +UINT bytes_per_sector; + + if (media_ptr -> fx_media_driver_request == FX_DRIVER_BOOT_READ) + { + + /* Read the boot record and return to the caller. */ + + /* Calculate the RAM disk boot sector offset, which is at the very beginning of the + RAM disk. Note the RAM disk memory is pointed to by the fx_media_driver_info pointer, + which is supplied by the application in the call to fx_media_open. */ + source_buffer = (UCHAR *) media_ptr -> fx_media_driver_info; + source_buffer += SECTOR_SIZE * HIDDEN_SECTORS; + + /* For RAM driver, determine if the boot record is valid. */ + if ((source_buffer[0] != (UCHAR) 0xEB) || + ((source_buffer[1] != (UCHAR) 0x34) && + (source_buffer[1] != (UCHAR) 0x76)) || + (source_buffer[2] != (UCHAR) 0x90)) + { + + /* Invalid boot record, return an error! */ + media_ptr -> fx_media_driver_status = FX_MEDIA_INVALID; + return; + } + + /* For RAM disk only, pickup the bytes per sector. */ + bytes_per_sector = _fx_utility_16_unsigned_read(&source_buffer[FX_BYTES_SECTOR]); + + + /* Ensure this is less than the destination. */ + + /* Copy the RAM boot sector into the destination. */ + _fx_utility_memory_copy(source_buffer, media_ptr -> fx_media_driver_buffer, bytes_per_sector); + + /* Successful driver request. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + } + else if (media_ptr -> fx_media_driver_request == FX_DRIVER_BOOT_WRITE) + { + + /* Write the boot record and return to the caller. */ + + /* Calculate the RAM disk boot sector offset, which is at the very beginning of the + RAM disk. Note the RAM disk memory is pointed to by the fx_media_driver_info pointer, + which is supplied by the application in the call to fx_media_open. */ + destination_buffer = (UCHAR *) media_ptr -> fx_media_driver_info; + destination_buffer += SECTOR_SIZE * HIDDEN_SECTORS; + + /* Copy the RAM boot sector into the destination. */ + _fx_utility_memory_copy(media_ptr -> fx_media_driver_buffer, destination_buffer, media_ptr -> fx_media_bytes_per_sector); + + /* Successful driver request. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + } + else + { + if ((media_ptr -> fx_media_driver_request == FX_DRIVER_WRITE) || (media_ptr -> fx_media_driver_request == FX_DRIVER_READ)) + { + if (media_ptr -> fx_media_hidden_sectors == 0) + { + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + return; + } + } + _fx_ram_driver(media_ptr); + } +} diff --git a/test/regression_test/filex_media_multiple_open_close_test.c b/test/regression_test/filex_media_multiple_open_close_test.c new file mode 100644 index 0000000..a9de735 --- /dev/null +++ b/test/regression_test/filex_media_multiple_open_close_test.c @@ -0,0 +1,2027 @@ +/* This FileX test concentrates on the basic media format, open, close operation. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_system.h" +#include "fx_utility.h" +#include +#include "fx_ram_driver_test.h" + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 256*128 + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; +static FX_MEDIA ram_disk1; +static FX_FILE my_file1; +static FX_FILE my_file2; +static FX_FILE my_file3; +static FX_FILE my_file4; +static FX_FILE my_file5; +static FX_FILE my_file6; +static FX_FILE my_file7; +static FX_FILE my_file8; +static FX_FILE my_file9; +static FX_FILE my_file10; +static FX_FILE my_file11; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *ram_disk_memory1; +static UCHAR *cache_buffer; +static UCHAR *cache_buffer1; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR cache_buffer1[CACHE_SIZE]; +#endif + + +/* Define thread prototypes. */ + +void filex_media_multiple_format_open_close_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + +static ULONG open_count; +static ULONG close_count; + +static void ram_disk_open(FX_MEDIA *media_ptr) +{ + + FX_PARAMETER_NOT_USED(media_ptr); + + open_count++; +} + +static void ram_disk_close(FX_MEDIA *media_ptr) +{ + + FX_PARAMETER_NOT_USED(media_ptr); + + close_count++; +} + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_media_multiple_format_open_close_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + (256*128) /* CACHE_SIZE */; + cache_buffer1 = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + pointer = pointer + (256*128); + ram_disk_memory1 = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status, status1; +ULONG actual; +UINT i, j; +UCHAR local_buffer[32]; +UCHAR raw_sector_buffer[512]; +UCHAR volume_buffer[100]; +ULONG temp; +ULONG temp1; +ULONG temp2; +ULONG temp3; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Multiple Media format, open and close test............."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Format the second media. This needs to be done before opening it! */ + status += fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory1, // RAM disk memory pointer + cache_buffer1, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(2); + } + + /* Open each ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_media_open(&ram_disk1,"RAM DISK", _fx_ram_driver, ram_disk_memory1,cache_buffer1,CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status1= fx_file_create(&ram_disk1, "TEST.TXT"); + + /* Check the first create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Check the second create status. */ + if (status1 != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test files. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk1, &my_file1, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Seek to the beginning of the test files. */ + status = fx_file_seek(&my_file, 0); + status += fx_file_seek(&my_file1, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test files. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + status += fx_file_write(&my_file1, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test files. */ + status = fx_file_seek(&my_file, 0); + status += fx_file_seek(&my_file1, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the first 28 bytes of the test files. */ + status = fx_file_read(&my_file, local_buffer, 28, &actual); + status += fx_file_read(&my_file1, local_buffer, 28, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test files. */ + status = fx_file_close(&my_file); + status += fx_file_close(&my_file1); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Close the medias. */ + status = fx_media_close(&ram_disk); + status += fx_media_close(&ram_disk1); + + /* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + /* send null pointer to generate an error */ + status = fx_media_close(FX_NULL); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(11); + } +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Re-Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_media_open(&ram_disk1,"RAM DISK", _fx_ram_driver, ram_disk_memory1,cache_buffer1,CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(3); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status1= fx_file_create(&ram_disk1, "TEST.TXT"); + + /* Check for an already created status. This is not fatal, just + let the user know. */ + if (status != FX_ALREADY_CREATED) + { + + printf("ERROR!\n"); + test_control_return(3); + } + + /* Check for an already created status. This is not fatal, just + let the user know. */ + if (status1 != FX_ALREADY_CREATED) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the test files. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk1, &my_file1, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Seek to the beginning of the test files. */ + status = fx_file_seek(&my_file, 0); + status += fx_file_seek(&my_file1, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Write a string to the test files. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + status += fx_file_write(&my_file1, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Seek to the beginning of the test files. */ + status = fx_file_seek(&my_file, 0); + status += fx_file_seek(&my_file1, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + /* Read the first 28 bytes of the test files. */ + status = fx_file_read(&my_file, local_buffer, 28, &actual); + status += fx_file_read(&my_file1, local_buffer, 28, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the test files. */ + status = fx_file_close(&my_file); + status += fx_file_close(&my_file1); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(9); + } + + /* Close the medias. */ + status = fx_media_close(&ram_disk); + status += fx_media_close(&ram_disk1); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Test multiple files open at time of close. */ + + /* Re-Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_media_open(&ram_disk1,"RAM DISK", _fx_ram_driver, ram_disk_memory1,cache_buffer1,CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(10); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + status1= fx_file_create(&ram_disk1, "TEST.TXT"); + + /* Check for an already created status. This is not fatal, just + let the user know. */ + if (status != FX_ALREADY_CREATED) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + /* Check for an already created status. This is not fatal, just + let the user know. */ + if (status1 != FX_ALREADY_CREATED) + { + + printf("ERROR!\n"); + test_control_return(12); + } + + /* Open the test files. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_READ); + status += fx_file_open(&ram_disk1, &my_file1, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(13); + } + + /* Seek to the beginning of the test files. */ + status = fx_file_seek(&my_file, 0); + status += fx_file_seek(&my_file1, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(14); + } + + /* Write a string to the test files. */ + status = fx_file_write(&my_file1, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(15); + } + + /* Seek to the beginning of the test files. */ + status = fx_file_seek(&my_file, 0); + status += fx_file_seek(&my_file1, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(16); + } + + /* Read the first 28 bytes of the test files. */ + status = fx_file_read(&my_file, local_buffer, 28, &actual); + status += fx_file_read(&my_file1, local_buffer, 28, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + printf("ERROR!\n"); + test_control_return(17); + } + + /* Close the medias. */ + status = fx_media_close(&ram_disk1); + status += fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(18); + } + + /* FAT32 Test for sixteen files and multiple open/close calls. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors - FAT32 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Setup a new, more complicated directory structure. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_file_create(&ram_disk, "ROOTF1.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF2.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF3.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF4.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF5.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF6.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF7.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF8.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF9.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF10.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF11.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF12.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF13.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF14.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF15.TXT"); + status += fx_media_flush(&ram_disk); + status += fx_file_create(&ram_disk, "ROOTF16.TXT"); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF1.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOTF1_CONTENTS", 130); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF2.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOTF2_CONTENTS", 130); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + status += fx_file_open(&ram_disk, &my_file, "ROOTF3.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, "ROOTF3_CONTENTS", 130); + status += fx_file_close(&my_file); + status += fx_media_flush(&ram_disk); + + /* Determine if the files were written properly. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(20); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Open the media again. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* And close it again. */ + status += fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(21); + } + + /* Test the notify registration functions. */ + + /* Setup notify routines for open and close. */ + status = fx_media_open_notify_set(&ram_disk, ram_disk_open); + status += fx_media_close_notify_set(&ram_disk,ram_disk_close); + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(22); + } + + /* Setup a new, more complicated directory structure. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Determine if the files were written properly. */ + if ((status) || (open_count != 1)) + { + + printf("ERROR!\n"); + test_control_return(23); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + status += fx_media_open_notify_set(&ram_disk, FX_NULL); + status += fx_media_close_notify_set(&ram_disk, FX_NULL); + + /* Determine if the test was successful. */ + if ((status != FX_SUCCESS) || (close_count != 1)) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* FAT32 Test for the additional information sector. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 6000, // Total sectors - FAT16 + 512, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(24); + } + + /* Clear the raw sector buffer. */ + for (i = 0; i < 512; i++) + { + raw_sector_buffer[i] = 0; + } + + /* Setup a new file that we will use for the additional information sector. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_file_create(&ram_disk, "TEXT.TXT"); + status += fx_file_open(&ram_disk, &my_file, "TEXT.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_write(&my_file, raw_sector_buffer, 512); + status += fx_media_flush(&ram_disk); + + /* Determine if the files were written properly. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(25); + } + + /* Now setup the media's additional information sector. */ + ram_disk.fx_media_FAT32_additional_info_sector = (my_file.fx_file_first_physical_cluster - 2) + ram_disk.fx_media_data_sector_start; + + /* Now set the last available clusters to the current available clusters. */ + ram_disk.fx_media_FAT32_additional_info_last_available = ram_disk.fx_media_available_clusters; + + /* Flush the media. */ + status += fx_media_flush(&ram_disk); + + /* Now setup the media's additional information sector. */ + ram_disk.fx_media_FAT32_additional_info_sector = (my_file.fx_file_first_physical_cluster - 2) + ram_disk.fx_media_data_sector_start; + + /* Now set the last available clusters to the current available clusters. */ + ram_disk.fx_media_FAT32_additional_info_last_available = ram_disk.fx_media_available_clusters; + + /* Close the media. */ + status += fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Open the media again. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_file_open(&ram_disk, &my_file, "TEXT.TXT", FX_OPEN_FOR_WRITE); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Now setup the media's additional information sector. */ + ram_disk.fx_media_FAT32_additional_info_sector = (my_file.fx_file_first_physical_cluster - 2) + ram_disk.fx_media_data_sector_start; + + /* Now clear the last available clusters to the current available clusters. */ + ram_disk.fx_media_FAT32_additional_info_last_available = 0; + + /* Now temporarily set the write protect flag. */ + ram_disk.fx_media_driver_write_protect = FX_FALSE; + + /* Flush the media. */ + status += fx_media_flush(&ram_disk); + + /* Now setup the media's additional information sector. */ + ram_disk.fx_media_FAT32_additional_info_sector = (my_file.fx_file_first_physical_cluster - 2) + ram_disk.fx_media_data_sector_start; + + /* Now clear the last available clusters to the current available clusters. */ + ram_disk.fx_media_FAT32_additional_info_last_available = 0; + + /* Now temporarily set the write protect flag. */ + ram_disk.fx_media_driver_write_protect = FX_TRUE; + + /* Close the media. */ + status += fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(28); + } + + /* Open the media again. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_file_open(&ram_disk, &my_file, "TEXT.TXT", FX_OPEN_FOR_WRITE); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(27); + } + + /* Now setup the media's additional information sector. */ + ram_disk.fx_media_FAT32_additional_info_sector = (my_file.fx_file_first_physical_cluster - 2) + ram_disk.fx_media_data_sector_start; + + /* Now clear the last available clusters to the current available clusters. */ + ram_disk.fx_media_FAT32_additional_info_last_available = 0; + + /* Clear the write protect flag. */ + ram_disk.fx_media_driver_write_protect = FX_FALSE; + + /* Flush the media. */ + status += fx_media_flush(&ram_disk); + + /* Now setup the media's additional information sector. */ + ram_disk.fx_media_FAT32_additional_info_sector = (my_file.fx_file_first_physical_cluster - 2) + ram_disk.fx_media_data_sector_start; + + /* Now clear the last available clusters to the current available clusters. */ + ram_disk.fx_media_FAT32_additional_info_last_available = 0; + + /* Clear the write protect flag. */ + ram_disk.fx_media_driver_write_protect = FX_FALSE; + + /* Close the media... This will attempt to look for the additional information sector but fail since the signature is not present. */ + status += fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(29); + } + + /* Open the media again. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_file_open(&ram_disk, &my_file, "TEXT.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_seek(&my_file, 0); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(30); + } + + /* Now setup the media's additional information sector. */ + ram_disk.fx_media_FAT32_additional_info_sector = (my_file.fx_file_first_physical_cluster - 2) + ram_disk.fx_media_data_sector_start; + + /* Now clear the last available clusters to the current available clusters. */ + ram_disk.fx_media_FAT32_additional_info_last_available = 0; + + /* Now temporarily set the write protect flag. */ + ram_disk.fx_media_driver_write_protect = FX_FALSE; + + /* Read the file contents. */ + status = fx_file_read(&my_file, raw_sector_buffer, 512, &actual); + + /* Write the first signature. */ + _fx_utility_32_unsigned_write(&raw_sector_buffer[0], 0x41615252); + + /* Write the signature back to the file. */ + status += fx_file_seek(&my_file, 0); + status += fx_file_write(&my_file, raw_sector_buffer, 512); + + /* Flush the media. */ + status += fx_media_flush(&ram_disk); + + /* Now setup the media's additional information sector. */ + ram_disk.fx_media_FAT32_additional_info_sector = (my_file.fx_file_first_physical_cluster - 2) + ram_disk.fx_media_data_sector_start; + + /* Now clear the last available clusters to the current available clusters. */ + ram_disk.fx_media_FAT32_additional_info_last_available = 0; + + /* Now temporarily set the write protect flag. */ + ram_disk.fx_media_driver_write_protect = FX_FALSE; + + /* Close the media... This will attempt to look for the additional information sector but fail since the second signature is not present. */ + status += fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Open the media again. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_file_open(&ram_disk, &my_file, "TEXT.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_seek(&my_file, 0); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Now setup the media's additional information sector. */ + ram_disk.fx_media_FAT32_additional_info_sector = (my_file.fx_file_first_physical_cluster - 2) + ram_disk.fx_media_data_sector_start; + + /* Now clear the last available clusters to the current available clusters. */ + ram_disk.fx_media_FAT32_additional_info_last_available = 0; + + /* Now clear the write protect flag. */ + ram_disk.fx_media_driver_write_protect = FX_FALSE; + + /* Read the file contents. */ + status = fx_file_read(&my_file, raw_sector_buffer, 512, &actual); + + /* Write the last signature. */ + fx_file_seek(&my_file, 0); + _fx_utility_32_unsigned_write(&raw_sector_buffer[484], 0x61417272); + + /* Write the signature back to the file. */ + status += fx_file_seek(&my_file, 0); + status += fx_file_write(&my_file, raw_sector_buffer, 512); + + /* Flush the media. */ + status += fx_media_flush(&ram_disk); + + /* Now clear the last available clusters to the current available clusters. */ + ram_disk.fx_media_FAT32_additional_info_last_available = 0; + + /* Now clear the write protect flag. */ + ram_disk.fx_media_driver_write_protect = FX_FALSE; + + /* Close the media... This will attempt to look for the additional information sector and succeed in updating the additional sector. */ + status += fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + /* Create some additional files. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_file_create(&ram_disk, "TEXT1.TXT"); + status += fx_file_create(&ram_disk, "TEXT2.TXT"); + status += fx_file_create(&ram_disk, "TEXT3.TXT"); + status += fx_file_create(&ram_disk, "TEXT4.TXT"); + status += fx_file_create(&ram_disk, "TEXT5.TXT"); + status += fx_file_create(&ram_disk, "TEXT6.TXT"); + status += fx_file_create(&ram_disk, "TEXT7.TXT"); + status += fx_file_create(&ram_disk, "TEXT8.TXT"); + status += fx_file_create(&ram_disk, "TEXT9.TXT"); + status += fx_file_create(&ram_disk, "TEXT10.TXT"); + status += fx_file_create(&ram_disk, "TEXT11.TXT"); + status += fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Loop to create the environment for I/O errors... */ + for (i = 0; i < 1000; i++) + { + + /* Open the media again. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 512); + status += fx_file_open(&ram_disk, &my_file, "TEXT.TXT", FX_OPEN_FOR_READ); + status += fx_file_open(&ram_disk, &my_file1, "TEXT1.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file2, "TEXT2.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file3, "TEXT3.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file4, "TEXT4.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file5, "TEXT5.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file6, "TEXT6.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file7, "TEXT7.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file8, "TEXT8.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file9, "TEXT9.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file10, "TEXT10.TXT", FX_OPEN_FOR_WRITE); + status += fx_file_open(&ram_disk, &my_file11, "TEXT11.TXT", FX_OPEN_FOR_WRITE); + raw_sector_buffer[0] = (UCHAR)i; + status += fx_file_write(&my_file1, raw_sector_buffer, 1); + status += fx_file_write(&my_file2, raw_sector_buffer, 1); + status += fx_file_write(&my_file3, raw_sector_buffer, 1); + status += fx_file_write(&my_file4, raw_sector_buffer, 1); + status += fx_file_write(&my_file5, raw_sector_buffer, 1); + status += fx_file_write(&my_file6, raw_sector_buffer, 1); + status += fx_file_write(&my_file7, raw_sector_buffer, 1); + status += fx_file_write(&my_file8, raw_sector_buffer, 1); + status += fx_file_write(&my_file9, raw_sector_buffer, 1); + status += fx_file_write(&my_file10, raw_sector_buffer, 1); + status += fx_file_write(&my_file11, raw_sector_buffer, 1); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Now temporarily set the write protect flag. */ + ram_disk.fx_media_driver_write_protect = FX_FALSE; + status = fx_media_flush(&ram_disk); + status += fx_media_cache_invalidate(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Now setup the media's additional information sector. */ + ram_disk.fx_media_FAT32_additional_info_sector = (my_file.fx_file_first_physical_cluster - 2) + ram_disk.fx_media_data_sector_start; + + /* Now clear the last available clusters to the current available clusters. */ + ram_disk.fx_media_FAT32_additional_info_last_available = 0; + + /* Create I/O errors via the RAM driver interface. */ + j = (UINT)(rand() % 10); + if (j == 0) + j = 1; + _fx_ram_driver_io_error_request = j; + + fx_media_flush(&ram_disk); + + /* Now setup the media's additional information sector. */ + ram_disk.fx_media_FAT32_additional_info_sector = (my_file.fx_file_first_physical_cluster - 2) + ram_disk.fx_media_data_sector_start; + + /* Now clear the last available clusters to the current available clusters. */ + ram_disk.fx_media_FAT32_additional_info_last_available = 0; + + /* Create I/O errors via the RAM driver interface. */ + j = (UINT)(rand() % 10); + if (j == 0) + j = 1; + _fx_ram_driver_io_error_request = j; + + /* Close the media... This will attempt to look for the additional information sector and succeed in updating the additional sector. */ + fx_media_close(&ram_disk); + + /* Create I/O errors via the RAM driver interface. */ + j = (UINT)(rand() % 10); + if (j == 0) + j = 1; + _fx_ram_driver_io_error_request = j; + + /* Set the volume. */ + fx_media_volume_set(&ram_disk, "MYDISK"); + + /* Create I/O errors via the RAM driver interface. */ + j = (UINT)(rand() % 10); + if (j == 0) + j = 1; + _fx_ram_driver_io_error_request = j; + + /* Get the volume. */ + if (i & 1) + fx_media_volume_get(&ram_disk, (CHAR *)volume_buffer, FX_BOOT_SECTOR); + else + fx_media_volume_get(&ram_disk, (CHAR *)volume_buffer, FX_DIRECTORY_SECTOR); + + _fx_ram_driver_io_error_request = 0; + } + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(33); + } + + + /* Additional media open/close corner case tests... including I/O errors. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 6000, // Total sectors - FAT16 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(34); + } + + /* Clear the raw sector buffer. */ + for (i = 0; i < 512; i++) + { + raw_sector_buffer[i] = 0; + } + + /* Save the options. */ + temp = _fx_system_build_options_1; + temp1 = _fx_system_build_options_2; + temp2 = _fx_system_build_options_3; + temp3 = (ULONG)_fx_version_id[0]; + + /* Set all these to 0 to cause the error. */ + _fx_system_build_options_1 = 0; + _fx_system_build_options_2 = 0; + _fx_system_build_options_3 = 0; + + /* Setup a new file that we will use for the additional information sector. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check for the not implemented error, which is expected. */ + if (status != FX_NOT_IMPLEMENTED) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Restore build options and set version_id to 0 to cause the error. */ + _fx_system_build_options_1 = temp; + _fx_system_build_options_2 = temp1; + _fx_system_build_options_3 = temp2; + _fx_version_id[0] = 0; + + /* Setup a new file that we will use for the additional information sector. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check for the not implemented error, which is expected. */ + if (status != FX_NOT_IMPLEMENTED) + { + + printf("ERROR!\n"); + test_control_return(35); + } + + /* Restore version id. */ + _fx_version_id[0] = (CHAR) temp3; + + +#ifndef FX_DISABLE_CACHE + /* Now attempt to call media open with a 0 sized cache. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 0); + + /* Check for the buffer error, which is expected. */ + if (status != FX_BUFFER_ERROR) + { + + printf("ERROR!\n"); + test_control_return(36); + } +#endif + + /* Now attempt to call media open with an over-sized cache... this will be reduced automatically and should open fine. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 257*128); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(37); + } + + /* Abort the media since it wasn't opened properly. */ + fx_media_abort(&ram_disk); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors - FAT32 + 512, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(38); + } + + /* Setup a new file that we will use for the additional information sector. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 512); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(39); + } + + /* Loop to create a bogus FAT chain for the root directory to test the total cluster logic in root directory total entries calculation. */ + for (i = 2; i < ram_disk.fx_media_total_clusters+10; i++) + { + /* Write a FAT chain link that exceeds the FAT area. */ + status += _fx_utility_FAT_entry_write(&ram_disk, i, i+1); + } + + /* Now close the media. */ + status = fx_media_close(&ram_disk); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(40); + } + + /* Open the media again to get the FAT read error. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 512); + + /* Check for error. */ + if (status != FX_FAT_READ_ERROR) + { + + printf("ERROR!\n"); + test_control_return(41); + } + + /* Abort the media. */ + fx_media_abort(&ram_disk); + + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors - FAT32 + 512, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(42); + } + + /* Open the media again. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 512); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(43); + } + + /* Write a FAT chain link to check (cluster_number == FAT_entry). */ + status = _fx_utility_FAT_entry_write(&ram_disk, 2, 2+1); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(46); + } + + /* Now close the media. */ + status = fx_media_close(&ram_disk); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(40); + } + + /* Open the media again to get the FAT read error. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 512); + + /* Check for error. */ + if (status != FX_FAT_READ_ERROR) + { + + printf("ERROR!\n"); + test_control_return(41); + } + + /* Abort the media. */ + fx_media_abort(&ram_disk); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors - FAT32 + 512, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(42); + } + + /* Open the media again. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 512); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(43); + } + + /* Pickup the additional information sector. */ + temp = ram_disk.fx_media_FAT32_additional_info_sector; + + /* Read the additional information sector. */ + fx_media_read(&ram_disk, temp, raw_sector_buffer); + + /* Modify the second signature to exercise the error logic in media open. */ + _fx_utility_32_unsigned_write(&raw_sector_buffer[484], 0); + + /* Write the second signature back. */ + fx_media_write(&ram_disk, temp, raw_sector_buffer); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(44); + } + + /* Now try to re-open with the second signature bad. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 512); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(45); + } + + /* Fix the signature, but corrupt the available clusters. */ + + /* Read the additional information sector. */ + fx_media_read(&ram_disk, temp, raw_sector_buffer); + + /* Modify the second signature to exercise the error logic in media open. */ + _fx_utility_32_unsigned_write(&raw_sector_buffer[484], 0x61417272); + + /* Save the search start and available clusters. */ + temp1 = _fx_utility_32_unsigned_read(&raw_sector_buffer[488]); + temp2 = _fx_utility_32_unsigned_read(&raw_sector_buffer[492]); + + /* Modify the search start... beyond the total clusters */ + _fx_utility_32_unsigned_write(&raw_sector_buffer[492], 0xF0F0F0F0); + + /* Write the search start back. */ + fx_media_write(&ram_disk, temp, raw_sector_buffer); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(46); + } + + /* Now try to re-open with a bad search cluster value - greater than total. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 512); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(47); + } + + /* Read the additional information sector. */ + fx_media_read(&ram_disk, temp, raw_sector_buffer); + + /* Modify the search start... before the starting cluster. */ + _fx_utility_32_unsigned_write(&raw_sector_buffer[492], 0); + + /* Write the search start back. */ + fx_media_write(&ram_disk, temp, raw_sector_buffer); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(48); + } + + /* Now try to re-open with a bad search cluster value - less than the total. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 512); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(49); + } + + /* Read the additional information sector. */ + fx_media_read(&ram_disk, temp, raw_sector_buffer); + + /* Restore the search start... before the starting cluster. */ + _fx_utility_32_unsigned_write(&raw_sector_buffer[492], temp2); + + /* Make the available clusters too big. */ + _fx_utility_32_unsigned_write(&raw_sector_buffer[488], ram_disk.fx_media_total_clusters+1); + + /* Write the search start back. */ + fx_media_write(&ram_disk, temp, raw_sector_buffer); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(50); + } + + /* Now try to re-open with a bad available clusters value - more than the total. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 512); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(51); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(52); + } + + /* Format the media in FAT12 for testing I/O error on available clusters in media open. */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 2000, // Total sectors - FAT12 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(53); + } + + /* Open the media again, but this time introduce an I/O error to cause the FAT read to fail. */ + _fx_ram_driver_io_error_request = 5; + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + _fx_ram_driver_io_error_request = 0; + + /* Check for error. */ + if (status != FX_FAT_READ_ERROR) + { + + printf("ERROR!\n"); + test_control_return(54); + } + + /* Abort the media. */ + fx_media_abort(&ram_disk); + + /* Format the media in FAT16 for testing I/O error on available clusters in media open. */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 7000, // Total sectors - FAT16 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(55); + } + + /* Open the media again, but this time introduce an I/O error to cause the FAT read to fail. */ + _fx_ram_driver_io_error_request = 5; + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + _fx_ram_driver_io_error_request = 0; + + /* Check for error. */ + if (status != FX_FAT_READ_ERROR) + { + + printf("ERROR!\n"); + test_control_return(56); + } + + /* Abort the media. */ + fx_media_abort(&ram_disk); + + /* Open the media again to setup for a media close I/O error. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(57); + } + + /* Create a file. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(58); + } + + /* Open the test files. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(59); + } + + /* Write to the file. */ + status = fx_file_write(&my_file, "1234567890", 10); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(60); + } + + /* Now attemp to close the media, but with an I/O error introduced so the close will fail trying to write out the directory entry of the open file. */ + _fx_ram_driver_io_error_request = 1; + status = fx_media_close(&ram_disk); + _fx_ram_driver_io_error_request = 0; + + /* Check for the I/O error. */ + if (status != FX_IO_ERROR) + { + + printf("ERROR!\n"); + test_control_return(61); + } + + /* Abort media. */ + fx_media_abort(&ram_disk); + + /* Format the media in FAT16 for testing I/O error in media close. */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 7000, // Total sectors - FAT16 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(62); + } + + /* Open the media again to setup for a media close I/O error. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(63); + } + + /* Create a file. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(64); + } + + /* Open the test files. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(65); + } + + /* Write to the file. */ + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_media_read(&ram_disk, 2000, raw_sector_buffer); + status += fx_media_write(&ram_disk, 2000, raw_sector_buffer); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(66); + } + +#ifndef FX_DISABLE_CACHE +#ifdef FX_FAULT_TOLERANT + /* While FX__FAULT_TOLERANT is defined, non data sector will flush directly in _fx_utility_logical_sector_write rather than set dirty flag. */ + /* For this reason, driver won't be called in _fx_utility_logical_sector_flush which is different from non FAULT_TOLERANT code. */ + _fx_utility_logical_sector_flush_error_request = 1; + status = fx_media_close(&ram_disk); +#else + /* Now attemp to close the media, but with an I/O error introduced so the close will fail trying to write out the directory entry of the open file. */ + _fx_ram_driver_io_error_request = 17; + status = fx_media_close(&ram_disk); + _fx_ram_driver_io_error_request = 0; +#endif + + /* Check for the I/O error. */ + if (status != FX_IO_ERROR) + { + + printf("ERROR!\n"); + test_control_return(67); + } +#endif + + /* Finallay, abort the media. */ + fx_media_abort(&ram_disk); + + /* Format the media in FAT16 for testing I/O error in media flush. */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 7000, // Total sectors - FAT16 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(68); + } + + /* Open the media again to setup for a media close I/O error. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(69); + } + + /* Create a file. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(70); + } + + /* Open the test files. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(71); + } + + /* Write to the file. */ + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_media_read(&ram_disk, 2000, raw_sector_buffer); + status += fx_media_write(&ram_disk, 2000, raw_sector_buffer); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(72); + } + +#if defined(FX_FAULT_TOLERANT) && !defined(FX_DISABLE_CACHE) + /* While FX__FAULT_TOLERANT is defined, non data sector will flush directly in _fx_utility_logical_sector_write rather than set dirty flag. */ + /* For this reason, driver won't be called in _fx_utility_logical_sector_flush which is different from non FAULT_TOLERANT code. */ + _fx_utility_logical_sector_flush_error_request = 1; + status = fx_media_close(&ram_disk); +#else + /* Now attemp to flush the media, but with an I/O error introduced so the close will fail trying to write out the directory entry of the open file. */ + _fx_ram_driver_io_error_request = 1; + status = fx_media_flush(&ram_disk); + _fx_ram_driver_io_error_request = 0; +#endif + /* Check for the I/O error. */ + if (status != FX_IO_ERROR) + { + + printf("ERROR!\n"); + test_control_return(73); + } + + /* Finallay, abort the media. */ + fx_media_abort(&ram_disk); + + + /* Format the media in FAT16 for testing I/O error in media flush. */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 7000, // Total sectors - FAT16 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(68); + } + + /* Open the media again to setup for a media close I/O error. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(69); + } + + /* Create a file. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(70); + } + + /* Open the test files. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(71); + } + + /* Write to the file. */ + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_file_write(&my_file, "1234567890", 10); + status += fx_media_read(&ram_disk, 2000, raw_sector_buffer); + status += fx_media_write(&ram_disk, 2000, raw_sector_buffer); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(72); + } + +#ifndef FX_DISABLE_CACHE +#ifdef FX_FAULT_TOLERANT + /* While FX__FAULT_TOLERANT is defined, non data sector will flush directly in _fx_utility_logical_sector_write rather than set dirty flag. */ + /* For this reason, driver won't be called in _fx_utility_logical_sector_flush which is different from non FAULT_TOLERANT code. */ + _fx_utility_logical_sector_flush_error_request = 1; + status = fx_media_close(&ram_disk); +#else + /* Now attemp to flush the media, but with an I/O error introduced so the close will fail trying to flush logical sectors out. */ + _fx_ram_driver_io_error_request = 17; + status = fx_media_flush(&ram_disk); + _fx_ram_driver_io_error_request = 0; +#endif + + /* Check for the I/O error. */ + if (status != FX_IO_ERROR) + { + + printf("ERROR!\n"); + test_control_return(73); + } +#endif + + /* Finallay, abort the media. */ + fx_media_abort(&ram_disk); + + /* Now see if we can test the FAT32 format with I/O corner cases. */ + _fx_ram_driver_io_error_request = 2211; + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70657, // Total sectors - FAT32 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + _fx_ram_driver_io_error_request = 0; + + /* Determine if the format had an error. */ + if (status != FX_IO_ERROR) + { + + printf("ERROR!\n"); + test_control_return(74); + } + + + + printf("SUCCESS!\n"); + test_control_return(0); +} + diff --git a/test/regression_test/filex_media_read_write_sector_test.c b/test/regression_test/filex_media_read_write_sector_test.c new file mode 100644 index 0000000..cebd127 --- /dev/null +++ b/test/regression_test/filex_media_read_write_sector_test.c @@ -0,0 +1,1253 @@ +/* This FileX test concentrates on the basic media read/write sector operation. */ +/* While FX_FAULT_TOLERANT is not defined, fx_utility_logical_sector_write will not cache other types of sectors but FX_DATA_SECTOR. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_ram_driver_test.h" +#include + +void test_control_return(UINT status); +#ifndef FX_DISABLE_CACHE +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 256*128 + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; + +static UCHAR raw_sector_buffer[128]; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *direct_buffer; +static UCHAR *cache_buffer; +#else +static UCHAR direct_buffer[20*128]; +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + + +/* Define thread prototypes. */ + +void filex_media_read_write_sector_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_media_read_write_sector_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + direct_buffer = pointer; + pointer = pointer + 20*128; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +#ifdef FX_FAULT_TOLERANT +UINT first_test = 1; +#endif /* FX_FAULT_TOLERANT */ + +ULONG64 logical_sector; +UINT i, j; +ULONG fat_entry; +ULONG temp; +ULONG64 accessed_sectors[300]; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Media read/write sector test..........................."); + +#ifdef FX_FAULT_TOLERANT +TEST_START: +#endif /* FX_FAULT_TOLERANT */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 2, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* try to read before the media has been opened */ + status = fx_media_read(&ram_disk, 1, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_MEDIA_NOT_OPEN); + + /* try to write before the media has been opened */ + status = fx_media_write(&ram_disk, 1, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_MEDIA_NOT_OPEN); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* try to write while the media is write protected */ + ram_disk.fx_media_driver_write_protect = FX_TRUE; + status = fx_media_write(&ram_disk, 1, (VOID *) raw_sector_buffer); + ram_disk.fx_media_driver_write_protect = FX_FALSE; + return_if_fail( status == FX_WRITE_PROTECT); + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + + /* send null pointer to generate an error */ + status = fx_media_read(FX_NULL, 0, (void *) raw_sector_buffer); + return_if_fail( status == FX_PTR_ERROR); + + /* send null pointer to generate an error */ + status = fx_media_write(FX_NULL, 0, (void *) raw_sector_buffer); + return_if_fail( status == FX_PTR_ERROR); + +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 1, (VOID *) raw_sector_buffer); + return_if_fail ((status == FX_SUCCESS) && + (raw_sector_buffer[0] == 0xF8) && /* _fx_media_format_media_type value set during media format */ + (raw_sector_buffer[1] == 0xFF) && + (raw_sector_buffer[2] == 0xFF)); + + /* Write the same, unchanged sector back. */ + status = fx_media_write(&ram_disk, 1, (VOID *) raw_sector_buffer); + return_if_fail( status == FX_SUCCESS); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 2, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 7000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Test for invalid sector. */ + logical_sector = ram_disk.fx_media_total_sectors; + status = _fx_utility_logical_sector_read(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + return_if_fail( status == FX_SECTOR_INVALID); + + /* Test for invalid sector. */ + logical_sector = 0; + status = _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + return_if_fail( status == FX_SECTOR_INVALID); + + /* Test for invalid sector. */ + logical_sector = 0xFFFFFFFF; + status = _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + return_if_fail( status == FX_SECTOR_INVALID); + + /* Test for invalid sector. */ + logical_sector = ram_disk.fx_media_total_sectors; + status = _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 4, FX_DATA_SECTOR); + return_if_fail( status == FX_SECTOR_INVALID); + + /* Test for invalid sector. */ + logical_sector = 0; + status = _fx_utility_logical_sector_write(&ram_disk, logical_sector, direct_buffer, 4, FX_DATA_SECTOR); + return_if_fail( status == FX_SECTOR_INVALID); + + /* Test for invalid sector. */ + logical_sector = 0xFFFFFFFF; + status = _fx_utility_logical_sector_write(&ram_disk, logical_sector, direct_buffer, 4, FX_DATA_SECTOR); + return_if_fail( status == FX_SECTOR_INVALID); + + /* Test for invalid sector. */ + logical_sector = ram_disk.fx_media_total_sectors; + status = _fx_utility_logical_sector_write(&ram_disk, logical_sector, direct_buffer, 4, FX_DATA_SECTOR); + return_if_fail( status == FX_SECTOR_INVALID); + + /* Write to a sector not in the cache. */ + status = _fx_utility_logical_sector_write(&ram_disk, 6900, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + + + /* Read/write 256 sectors, filling the caches. */ + logical_sector = ram_disk.fx_media_data_sector_start + 4; + for (i = 0; i < 256; i++) + { + + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DIRECTORY_SECTOR); +#ifdef FX_FAULT_TOLERANT + if (first_test) + { + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + } + else + { +#endif + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DIRECTORY_SECTOR); +#ifdef FX_FAULT_TOLERANT + } +#endif + logical_sector++; + } + + /* At this point the cache is complete full. */ + + /* Now perform direct buffer read ram_disk.fx_media_data_sector_start on sectors before the cache. */ + logical_sector = + 2; + status = _fx_utility_logical_sector_read(&ram_disk, logical_sector, direct_buffer, 4, FX_DIRECTORY_SECTOR); + + /* Now perform direct buffer write. */ +#ifdef FX_FAULT_TOLERANT + if (first_test) + { + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, direct_buffer, 4, FX_DATA_SECTOR); + } + else + { +#endif + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, direct_buffer, 4, FX_DIRECTORY_SECTOR); +#ifdef FX_FAULT_TOLERANT + } +#endif + + /* Perform a flush. */ + status += _fx_utility_logical_sector_flush(&ram_disk, logical_sector, 300, FX_TRUE); + + /* Perform another flush with nothing in the cache. */ + status += _fx_utility_logical_sector_flush(&ram_disk, logical_sector, 300, FX_TRUE); + + /* Read/write 256 sectors, filling the caches. */ + logical_sector = ram_disk.fx_media_data_sector_start + 4; + for (i = 0; i < 256; i++) + { + + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DIRECTORY_SECTOR); +#ifdef FX_FAULT_TOLERANT + if (first_test) + { + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + } + else + { +#endif + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DIRECTORY_SECTOR); +#ifdef FX_FAULT_TOLERANT + } +#endif + logical_sector++; + } + + /* Now perform direct buffer read ram_disk.fx_media_data_sector_start on sectors before the cache. */ + logical_sector = (ULONG64)-2; + status = _fx_utility_logical_sector_read(&ram_disk, logical_sector, direct_buffer, 4, FX_DIRECTORY_SECTOR); + + /* Now perform direct buffer write. */ +#ifdef FX_FAULT_TOLERANT + if (first_test) + { + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, direct_buffer, 4, FX_DATA_SECTOR); + } + else + { +#endif + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, direct_buffer, 4, FX_DIRECTORY_SECTOR); +#ifdef FX_FAULT_TOLERANT + } +#endif + + /* Perform a flush. */ + status += _fx_utility_logical_sector_flush(&ram_disk, logical_sector, 300, FX_FALSE); + + /* Read/write 128 sectors, filling the caches and causing cache misses. */ + logical_sector = ram_disk.fx_media_data_sector_start + 4; + for (i = 0; i < 512; i++) + { + + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DIRECTORY_SECTOR); +#ifdef FX_FAULT_TOLERANT + if (first_test) + { + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + } + else + { +#endif + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DIRECTORY_SECTOR); +#ifdef FX_FAULT_TOLERANT + } +#endif + logical_sector++; + } + + /* Perform a flush. */ + status += _fx_utility_logical_sector_flush(&ram_disk, logical_sector, 300, FX_FALSE); + + /* Read 256 sectors, filling the caches. */ + logical_sector = ram_disk.fx_media_data_sector_start + 4; + for (i = 0; i < 256; i++) + { + + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DIRECTORY_SECTOR); + logical_sector++; + } + + /* Write same 256 sectors in reverse order. */ + for (i = 0; i < 256; i++) + { + + logical_sector--; +#ifdef FX_FAULT_TOLERANT + if (first_test) + { + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + } + else + { +#endif + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DIRECTORY_SECTOR); +#ifdef FX_FAULT_TOLERANT + } +#endif + } + + /* Update FAT entires. */ + for (i = 2; i < 6000; i++) + { + status += _fx_utility_FAT_entry_write(&ram_disk, (ULONG) i, (ULONG) i+1); + } + + /* Flush the FAT. */ + status += _fx_utility_FAT_map_flush(&ram_disk); + + /* Indicate that the driver needs to know! */ + ram_disk.fx_media_driver_free_sector_update = FX_TRUE; + + /* Update FAT entries again, but this time with free marker. */ + for (i = 2; i < 6000; i++) + { + status += _fx_utility_FAT_entry_write(&ram_disk, (ULONG) i, FX_FREE_CLUSTER); + } + + /* Flush the FAT. */ + status += _fx_utility_FAT_map_flush(&ram_disk); + ram_disk.fx_media_driver_free_sector_update = FX_FALSE; + + /* Random test of 300 sectors. */ + for (i = 0; i < 300; i++) + { + accessed_sectors[i] = logical_sector + (ULONG64)(rand() % 5000); + status += _fx_utility_logical_sector_read(&ram_disk, accessed_sectors[i], ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + if ((i > 0) && (rand() & 1)) + { + j = ((UINT)rand() % i); + status += _fx_utility_logical_sector_write(&ram_disk, accessed_sectors[j], ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + } + } + + /* Random test of 300 sectors with random logical sector flush. */ + for (i = 0; i < 300; i++) + { + accessed_sectors[i] = logical_sector + (ULONG64)(rand() % 5000); + status += _fx_utility_logical_sector_read(&ram_disk, accessed_sectors[i], ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + if ((i > 0) && (rand() & 1)) + { + j = ((UINT)rand() % i); + status += _fx_utility_logical_sector_write(&ram_disk, accessed_sectors[j], ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + } + if ((i > 0) && (rand() & 1)) + { + j = ((UINT)rand() % i); + if (rand() & 1) + status += _fx_utility_logical_sector_flush(&ram_disk, accessed_sectors[j], 1, FX_TRUE); + else + status += _fx_utility_logical_sector_flush(&ram_disk, accessed_sectors[j], 1, FX_FALSE); + } + } + + /* Random test of 1000 FAT entry reads/writes. */ + for (i = 0; i < 1000; i++) + { + + j = ((UINT)rand() % 100); + status += _fx_utility_FAT_entry_read(&ram_disk, j+2, &fat_entry); + + if (rand() & 1) + { + j = ((UINT)rand() % 100); + if (rand() & 1) + status += _fx_utility_FAT_entry_write(&ram_disk, j+2, j+3); + else + status += _fx_utility_FAT_entry_write(&ram_disk, j+2, FX_FREE_CLUSTER); + } + } + + /* Random test of 1000 FAT entry reads/writes with random FAT flush. */ + for (i = 0; i < 1000; i++) + { + + j = ((UINT)rand() % 100); + status += _fx_utility_FAT_entry_read(&ram_disk, j+2, &fat_entry); + + if (rand() & 1) + { + j = ((UINT)rand() % 100); + if (rand() & 1) + status += _fx_utility_FAT_entry_write(&ram_disk, j+2, j+3); + else + status += _fx_utility_FAT_entry_write(&ram_disk, j+2, FX_FREE_CLUSTER); + } + + if (rand() & 1) + { + + status += _fx_utility_FAT_flush(&ram_disk); + } + + if (rand() & 1) + { + + status += _fx_utility_FAT_map_flush(&ram_disk); + } + } + return_if_fail( status == FX_SUCCESS); + + /* Set the free sector update. */ + ram_disk.fx_media_driver_free_sector_update = FX_TRUE; + + /* Random test of 1000 FAT entry reads/writes with random FAT flush and random errors. */ + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, ram_disk.fx_media_data_sector_start, 60000, FX_TRUE); + _fx_ram_driver_io_error_request = 1000; + for (i = 0; i < 300000; i++) + { + + j = ((UINT)rand() % 100); + _fx_utility_FAT_entry_read(&ram_disk, j+2, &fat_entry); + + if (rand() & 1) + { + j = ((UINT)rand() % 100); + if (rand() & 1) + _fx_utility_FAT_entry_write(&ram_disk, j+2, j+3); + else + _fx_utility_FAT_entry_write(&ram_disk, j+2, FX_FREE_CLUSTER); + } + + if (i & 1) + _fx_utility_FAT_flush(&ram_disk); + else + _fx_utility_FAT_map_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, ram_disk.fx_media_data_sector_start, 60000, FX_TRUE); + } + _fx_ram_driver_io_error_request = 0; + + /* Set the free sector update. */ + ram_disk.fx_media_driver_free_sector_update = FX_FALSE; + + /* Flush out the logical sector cache. */ + status = _fx_utility_logical_sector_flush(&ram_disk, ram_disk.fx_media_data_sector_start, 60000, FX_TRUE); + + /* Read/write 256 sectors, filling the caches. */ + logical_sector = ram_disk.fx_media_data_sector_start + 4; + for (i = 0; i < 256; i++) + { + + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DIRECTORY_SECTOR); +#ifdef FX_FAULT_TOLERANT + if (first_test) + { + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + } + else + { +#endif + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DIRECTORY_SECTOR); +#ifdef FX_FAULT_TOLERANT + } +#endif + logical_sector++; + } + + /* Now test the flush logic with write protection enabled. */ + ram_disk.fx_media_driver_write_protect = FX_TRUE; + status += _fx_utility_logical_sector_flush(&ram_disk, ram_disk.fx_media_data_sector_start, 60000, FX_TRUE); + ram_disk.fx_media_driver_write_protect = FX_FALSE; + return_if_fail( status == FX_SUCCESS); + + /* Flush out the logical sector cache. */ + _fx_utility_logical_sector_flush(&ram_disk, ram_disk.fx_media_data_sector_start, 60000, FX_TRUE); + + /* Read/write 256 sectors, filling the caches. */ + logical_sector = ram_disk.fx_media_data_sector_start + 4; + for (i = 0; i < 256; i++) + { + + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DIRECTORY_SECTOR); +#ifdef FX_FAULT_TOLERANT + if (first_test) + { + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + } + else + { +#endif + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DIRECTORY_SECTOR); +#ifdef FX_FAULT_TOLERANT + } +#endif + logical_sector++; + } + + /* Now test the flush logic with an I/O error. */ + _fx_ram_driver_io_error_request = 1; + status += _fx_utility_logical_sector_flush(&ram_disk, ram_disk.fx_media_data_sector_start, 60000, FX_TRUE); + _fx_ram_driver_io_error_request = 0; + + /* Flush out the logical sector cache. */ + status = _fx_utility_logical_sector_flush(&ram_disk, ram_disk.fx_media_data_sector_start, 60000, FX_TRUE); + + /* Read/write 256 sectors, filling the caches. */ + logical_sector = ram_disk.fx_media_data_sector_start + 4; + for (i = 0; i < 256; i++) + { + + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DIRECTORY_SECTOR); +#ifdef FX_FAULT_TOLERANT + if (first_test) + { + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + } + else + { +#endif + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DIRECTORY_SECTOR); +#ifdef FX_FAULT_TOLERANT + } +#endif + logical_sector++; + } + + /* Now test the flush logic with an I/O error. */ + status += _fx_utility_logical_sector_flush(&ram_disk, ram_disk.fx_media_data_sector_start, 60000, FX_TRUE); + + /* Close the media. */ + status += fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 2, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 7000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Open the ram_disk. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE-1); + return_if_fail( status == FX_SUCCESS); + + /* Test for invalid sector. */ + logical_sector = ram_disk.fx_media_total_sectors; + status = _fx_utility_logical_sector_read(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + return_if_fail( status == FX_SECTOR_INVALID); + + /* Test for invalid sector. */ + logical_sector = 0; + status = _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + return_if_fail( status == FX_SECTOR_INVALID); + + /* Test for invalid sector. */ + logical_sector = 0xFFFFFFFF; + status = _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + return_if_fail( status == FX_SECTOR_INVALID); + + /* Test for invalid sector. */ + logical_sector = ram_disk.fx_media_total_sectors; + status = _fx_utility_logical_sector_read(&ram_disk, logical_sector, direct_buffer, 4, FX_DATA_SECTOR); + return_if_fail( status == FX_SECTOR_INVALID); + + /* Test for invalid sector. */ + logical_sector = 0; + status = _fx_utility_logical_sector_write(&ram_disk, logical_sector, direct_buffer, 4, FX_DATA_SECTOR); + return_if_fail( status == FX_SECTOR_INVALID); + + /* Test for invalid sector. */ + logical_sector = 0xFFFFFFFF; + status = _fx_utility_logical_sector_write(&ram_disk, logical_sector, direct_buffer, 4, FX_DATA_SECTOR); + return_if_fail( status == FX_SECTOR_INVALID); + + /* Test for invalid sector. */ + logical_sector = ram_disk.fx_media_total_sectors; + status = _fx_utility_logical_sector_write(&ram_disk, logical_sector, direct_buffer, 4, FX_DATA_SECTOR); + return_if_fail( status == FX_SECTOR_INVALID); + + status = 0; + + /* Read/write 256 sectors, filling the caches. */ + logical_sector = ram_disk.fx_media_data_sector_start + 4; + for (i = 0; i < 256; i++) + { + + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DIRECTORY_SECTOR); + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + logical_sector++; + } + + /* At this point the cache is complete full. */ + + /* Now perform direct buffer read ram_disk.fx_media_data_sector_start on sectors before the cache. */ + logical_sector = + 2; + status = _fx_utility_logical_sector_read(&ram_disk, logical_sector, direct_buffer, 4, FX_DIRECTORY_SECTOR); + + /* Now perform direct buffer write. */ + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, direct_buffer, 4, FX_DATA_SECTOR); + + /* Perform a flush. */ + status += _fx_utility_logical_sector_flush(&ram_disk, logical_sector, 300, FX_TRUE); + + /* Perform another flush with nothing in the cache. */ + status += _fx_utility_logical_sector_flush(&ram_disk, logical_sector, 300, FX_TRUE); + + /* Read/write 256 sectors, filling the caches. */ + logical_sector = ram_disk.fx_media_data_sector_start + 4; + for (i = 0; i < 256; i++) + { + + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DIRECTORY_SECTOR); + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + logical_sector++; + } + + /* Now perform direct buffer read ram_disk.fx_media_data_sector_start on sectors before the cache. */ + logical_sector = (ULONG64)-2; + status = _fx_utility_logical_sector_read(&ram_disk, logical_sector, direct_buffer, 4, FX_DIRECTORY_SECTOR); + + /* Now perform direct buffer write. */ + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, direct_buffer, 4, FX_DATA_SECTOR); + + /* Perform a flush. */ + status += _fx_utility_logical_sector_flush(&ram_disk, logical_sector, 300, FX_FALSE); + + /* Read/write 300 sectors, filling the caches and causing cache misses. */ + logical_sector = ram_disk.fx_media_data_sector_start + 4; + for (i = 0; i < 300; i++) + { + + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DIRECTORY_SECTOR); + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + logical_sector++; + } + + /* Perform a flush. */ + status += _fx_utility_logical_sector_flush(&ram_disk, logical_sector, 300, FX_FALSE); + + /* Read 256 sectors, filling the caches. */ + logical_sector = ram_disk.fx_media_data_sector_start + 4; + for (i = 0; i < 256; i++) + { + + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DIRECTORY_SECTOR); + logical_sector++; + } + + /* Write same 256 sectors in reverse order. */ + for (i = 0; i < 256; i++) + { + + logical_sector--; + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + } + + /* Update FAT entires. */ + for (i = 2; i < 6000; i++) + { + status += _fx_utility_FAT_entry_write(&ram_disk, (ULONG) i, (ULONG) i+1); + } + + /* Flush the FAT. */ + status += _fx_utility_FAT_map_flush(&ram_disk); + + /* Indicate that the driver needs to know! */ + ram_disk.fx_media_driver_free_sector_update = FX_TRUE; + + /* Update FAT entries again, but this time with free marker. */ + for (i = 2; i < 6000; i++) + { + status += _fx_utility_FAT_entry_write(&ram_disk, (ULONG) i, FX_FREE_CLUSTER); + } + + /* Flush the FAT. */ + status += _fx_utility_FAT_map_flush(&ram_disk); + ram_disk.fx_media_driver_free_sector_update = FX_FALSE; + + /* Test the case where the logical sector cache has something in it, but the write hasn't been found. */ + logical_sector = ram_disk.fx_media_data_sector_start + 4; + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector+1, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector+2, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector+3, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector+4, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector+1, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + + /* Random test of 300 sectors. */ + for (i = 0; i < 300; i++) + { + accessed_sectors[i] = logical_sector + (ULONG64)(rand() % 5000); + status += _fx_utility_logical_sector_read(&ram_disk, accessed_sectors[i], ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + if ((i > 0) && (rand() & 1)) + { + j = ((UINT)rand() % i); + status += _fx_utility_logical_sector_write(&ram_disk, accessed_sectors[j], ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + } + } + + /* Random test of 300 sectors with random logical sector flush. */ + for (i = 0; i < 300; i++) + { + accessed_sectors[i] = logical_sector + (ULONG64)(rand() % 5000); + status += _fx_utility_logical_sector_read(&ram_disk, accessed_sectors[i], ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + if ((i > 0) && (rand() & 1)) + { + j = ((UINT)rand() % i); + status += _fx_utility_logical_sector_write(&ram_disk, accessed_sectors[j], ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + } + if ((i > 0) && (rand() & 1)) + { + j = ((UINT)rand() % i); + if (rand() & 1) + status += _fx_utility_logical_sector_flush(&ram_disk, accessed_sectors[j], 1, FX_TRUE); + else + status += _fx_utility_logical_sector_flush(&ram_disk, accessed_sectors[j], 1, FX_FALSE); + } + } + + /* Random test of 1000 FAT entry reads/writes. */ + for (i = 0; i < 1000; i++) + { + + j = ((UINT)rand() % 100); + status += _fx_utility_FAT_entry_read(&ram_disk, j+2, &fat_entry); + + if (rand() & 1) + { + j = ((UINT)rand() % 100); + if (rand() & 1) + status += _fx_utility_FAT_entry_write(&ram_disk, j+2, j+3); + else + status += _fx_utility_FAT_entry_write(&ram_disk, j+2, FX_FREE_CLUSTER); + } + } + + /* Random test of 1000 FAT entry reads/writes with random FAT flush. */ + for (i = 0; i < 1000; i++) + { + + j = ((UINT)rand() % 100); + status += _fx_utility_FAT_entry_read(&ram_disk, j+2, &fat_entry); + + if (rand() & 1) + { + j = ((UINT)rand() % 100); + if (rand() & 1) + status += _fx_utility_FAT_entry_write(&ram_disk, j+2, j+3); + else + status += _fx_utility_FAT_entry_write(&ram_disk, j+2, FX_FREE_CLUSTER); + } + + if (rand() & 1) + { + + status += _fx_utility_FAT_flush(&ram_disk); + } + + if (rand() & 1) + { + + status += _fx_utility_FAT_map_flush(&ram_disk); + } + } + return_if_fail( status == FX_SUCCESS); + + /* Set the free sector update. */ + ram_disk.fx_media_driver_free_sector_update = FX_TRUE; + + /* Random test of 1000 FAT entry reads/writes with random FAT flush and random errors. */ + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, ram_disk.fx_media_data_sector_start, 60000, FX_TRUE); + _fx_ram_driver_io_error_request = 1000; + for (i = 0; i < 300000; i++) + { + + + j = ((UINT)rand() % 100); + _fx_utility_logical_sector_read(&ram_disk, (ULONG64) j, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + + if (rand() & 1) + _fx_utility_logical_sector_write(&ram_disk, (ULONG64) j, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + + j = ((UINT)rand() % 100); + _fx_utility_FAT_entry_read(&ram_disk, j+2, &fat_entry); + + if (rand() & 1) + { + j = ((UINT)rand() % 100); + if (rand() & 1) + _fx_utility_FAT_entry_write(&ram_disk, j+2, j+3); + else + _fx_utility_FAT_entry_write(&ram_disk, j+2, FX_FREE_CLUSTER); + } + + if (i & 1) + _fx_utility_FAT_flush(&ram_disk); + else + _fx_utility_FAT_map_flush(&ram_disk); + } + _fx_ram_driver_io_error_request = 0; + + /* Set the free sector update. */ + ram_disk.fx_media_driver_free_sector_update = FX_FALSE; + + /* Read/write 256 sectors, filling the caches. */ + logical_sector = ram_disk.fx_media_data_sector_start + 4; + for (i = 0; i < 256; i++) + { + + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DIRECTORY_SECTOR); + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + logical_sector++; + } + + /* Now perform direct buffer read ram_disk.fx_media_data_sector_start on sectors before the cache. */ + logical_sector = ram_disk.fx_media_data_sector_start; + status = _fx_utility_logical_sector_read(&ram_disk, logical_sector, direct_buffer, 12, FX_DIRECTORY_SECTOR); + + /* Now perform direct buffer write. */ + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, direct_buffer, 12, FX_DATA_SECTOR); + + /* Flush out the logical sector cache. */ + status += _fx_utility_logical_sector_flush(&ram_disk, ram_disk.fx_media_data_sector_start, 60000, FX_TRUE); + + /* Read/write 256 sectors, filling the caches. */ + logical_sector = ram_disk.fx_media_data_sector_start + 4; + for (i = 0; i < 256; i++) + { + + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DIRECTORY_SECTOR); + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + logical_sector++; + } + return_if_fail( status == FX_SUCCESS); + + /* Now direct read a logical sector outside the cache and cause an I/O error in the process. */ + _fx_ram_driver_io_error_request = 2; + logical_sector = ram_disk.fx_media_data_sector_start; + status = _fx_utility_logical_sector_read(&ram_disk, logical_sector, direct_buffer, 1, FX_DIRECTORY_SECTOR); + _fx_ram_driver_io_error_request = 0; + + /* Flush out the logical sector cache. */ + status = _fx_utility_logical_sector_flush(&ram_disk, ram_disk.fx_media_data_sector_start, 60000, FX_TRUE); + + /* Read/write 256 sectors, filling the caches. */ + logical_sector = ram_disk.fx_media_data_sector_start + 4; + for (i = 0; i < 256; i++) + { + + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DIRECTORY_SECTOR); + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + logical_sector++; + } + + /* Perform a direct read that exceeds the amount that can fit into the cache easily. */ + logical_sector = 6800; + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector, direct_buffer, 20, FX_UNKNOWN_SECTOR); + return_if_fail( status == FX_SUCCESS); + + /* Flush out the logical sector cache. */ + status = _fx_utility_logical_sector_flush(&ram_disk, ram_disk.fx_media_data_sector_start, 60000, FX_TRUE); + + /* Read/write 256 sectors, filling the caches. */ + logical_sector = ram_disk.fx_media_data_sector_start + 4; + for (i = 0; i < 256; i++) + { + + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + logical_sector++; + } + + /* Perform a direct read that suffers an I/O error. */ + logical_sector = 6800; + _fx_ram_driver_io_error_request = 1; + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector, direct_buffer, 1, FX_UNKNOWN_SECTOR); + _fx_ram_driver_io_error_request = 0; + + /* Flush out the logical sector cache. */ + status = _fx_utility_logical_sector_flush(&ram_disk, ram_disk.fx_media_data_sector_start, 60000, FX_TRUE); + + /* Read/write 256 sectors, filling the caches. */ + logical_sector = ram_disk.fx_media_data_sector_start + 4; + for (i = 0; i < 256; i++) + { + + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + logical_sector++; + } + + /* Perform a direct read that should be successful. */ + logical_sector = 6800; + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector, direct_buffer, 1, FX_UNKNOWN_SECTOR); + return_if_fail( status == FX_SUCCESS); + + /* Flush out the logical sector cache. */ + status = _fx_utility_logical_sector_flush(&ram_disk, ram_disk.fx_media_data_sector_start, 60000, FX_TRUE); + + /* Read/write 256 sectors, filling the caches. */ + logical_sector = ram_disk.fx_media_data_sector_start + 4; + for (i = 0; i < 256; i++) + { + + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DIRECTORY_SECTOR); + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + logical_sector++; + } + + /* Now test the flush logic with write protection enabled. */ + ram_disk.fx_media_driver_write_protect = FX_TRUE; + status += _fx_utility_logical_sector_flush(&ram_disk, ram_disk.fx_media_data_sector_start, 60000, FX_TRUE); + ram_disk.fx_media_driver_write_protect = FX_FALSE; + return_if_fail( status == FX_SUCCESS); + + /* Flush out the logical sector cache. */ + _fx_utility_logical_sector_flush(&ram_disk, ram_disk.fx_media_data_sector_start, 60000, FX_TRUE); + + /* Read/write 256 sectors, filling the caches. */ + logical_sector = ram_disk.fx_media_data_sector_start + 4; + for (i = 0; i < 256; i++) + { + + status += _fx_utility_logical_sector_read(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DIRECTORY_SECTOR); + status += _fx_utility_logical_sector_write(&ram_disk, logical_sector, ram_disk.fx_media_memory_buffer, 1, FX_DATA_SECTOR); + logical_sector++; + } + + /* Now test the flush logic with an I/O error. */ + _fx_ram_driver_io_error_request = 1; + status += _fx_utility_logical_sector_flush(&ram_disk, ram_disk.fx_media_data_sector_start, 60000, FX_TRUE); + _fx_ram_driver_io_error_request = 0; + + /* Flush the FAT. */ + _fx_utility_logical_sector_flush(&ram_disk, ram_disk.fx_media_data_sector_start, 60000, FX_TRUE); + _fx_utility_FAT_flush(&ram_disk); + + /* Now test the FAT map flush with an I/O error on the read of the primary FAT sector. */ + _fx_ram_driver_io_error_request = 1; + status = _fx_utility_FAT_map_flush(&ram_disk); + _fx_ram_driver_io_error_request = 0; + + /* Now test the FAT map flush with an I/O error on the write of the secondary FAT sector. */ + _fx_ram_driver_io_error_request = 3; + status = _fx_utility_FAT_map_flush(&ram_disk); + _fx_ram_driver_io_error_request = 0; + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* FAT12 FAT flush I/O error testing. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 2, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 2000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Open the ram_disk. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + return_if_fail( status == FX_SUCCESS); + + /* Now loop to write the FAT entries out with random I/O erorrs. */ + fat_entry = 4; + for (i = 0; i < 500000; i++) + { + + _fx_ram_driver_io_error_request = ((UINT)rand() % 5); + if (_fx_ram_driver_io_error_request == 0) + _fx_ram_driver_io_error_request = 1; + _fx_utility_FAT_entry_read(&ram_disk, fat_entry, &temp); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + _fx_utility_FAT_entry_write(&ram_disk, fat_entry, fat_entry+1); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + + /* Move to next FAT entry. */ + fat_entry++; + if (fat_entry >= (ram_disk.fx_media_available_clusters-1)) + fat_entry = 4; + } + for (i = 0; i < 500000; i++) + { + + _fx_ram_driver_io_error_request = ((UINT)rand() % 5); + if (_fx_ram_driver_io_error_request == 0) + _fx_ram_driver_io_error_request = 1; + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + _fx_utility_FAT_entry_write(&ram_disk, fat_entry, fat_entry+1); + + /* Move to next FAT entry. */ + fat_entry++; + if (fat_entry >= (ram_disk.fx_media_available_clusters-1)) + fat_entry = 4; + } + _fx_ram_driver_io_error_request = 0; + + /* Set the error flag to make logical sector write return an error. */ + _fx_utility_logical_sector_write_error_request = 1; + status = _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_write_error_request = 70000; /* This should not hit, but satisfy the code coverage. */ + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_write_error_request = 0; + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* FAT16 FAT flush I/O error testing. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 2, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 7000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + return_if_fail( status == FX_SUCCESS); + + /* Now loop to write the FAT entries out with random I/O erorrs. */ + fat_entry = 4; + for (i = 0; i < 500000; i++) + { + + _fx_ram_driver_io_error_request = ((UINT)rand() % 5); + if (_fx_ram_driver_io_error_request == 0) + _fx_ram_driver_io_error_request = 1; + _fx_utility_FAT_entry_read(&ram_disk, fat_entry, &temp); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + _fx_utility_FAT_entry_write(&ram_disk, fat_entry, fat_entry+1); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + + /* Move to next FAT entry. */ + fat_entry++; + if (fat_entry >= (ram_disk.fx_media_available_clusters-1)) + fat_entry = 4; + } + for (i = 0; i < 500000; i++) + { + + _fx_ram_driver_io_error_request = ((UINT)rand() % 5); + if (_fx_ram_driver_io_error_request == 0) + _fx_ram_driver_io_error_request = 1; + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + _fx_utility_FAT_entry_write(&ram_disk, fat_entry, fat_entry+1); + + /* Move to next FAT entry. */ + fat_entry++; + if (fat_entry >= (ram_disk.fx_media_available_clusters-1)) + fat_entry = 4; + } + _fx_ram_driver_io_error_request = 0; + + /* Set the error flag to make logical sector write return an error. */ + _fx_utility_logical_sector_write_error_request = 1; + status = _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_write_error_request = 70000; /* This should not hit, but satisfy the code coverage. */ + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_write_error_request = 0; + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* FAT32 FAT flush I/O error testing. */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 2, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70128, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + return_if_fail( status == FX_SUCCESS); + + /* Now loop to write the FAT entries out with random I/O erorrs. */ + fat_entry = 4; + for (i = 0; i < 500000; i++) + { + + _fx_ram_driver_io_error_request = ((UINT)rand() % 5); + if (_fx_ram_driver_io_error_request == 0) + _fx_ram_driver_io_error_request = 1; + _fx_utility_FAT_entry_read(&ram_disk, fat_entry, &temp); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + _fx_utility_FAT_entry_write(&ram_disk, fat_entry, fat_entry+1); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + + /* Move to next FAT entry. */ + fat_entry++; + if (fat_entry >= (ram_disk.fx_media_available_clusters-1)) + fat_entry = 4; + } + for (i = 0; i < 500000; i++) + { + + _fx_ram_driver_io_error_request = ((UINT)rand() % 5); + if (_fx_ram_driver_io_error_request == 0) + _fx_ram_driver_io_error_request = 1; + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + _fx_utility_FAT_entry_write(&ram_disk, fat_entry, fat_entry+1); + + /* Move to next FAT entry. */ + fat_entry++; + if (fat_entry >= (ram_disk.fx_media_available_clusters-1)) + fat_entry = 4; + } + + _fx_ram_driver_io_error_request = 0; + + /* Set the error flag to make logical sector write return an error. */ + _fx_utility_logical_sector_write_error_request = 1; + status = _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_write_error_request = 70000; /* This should not hit, but satisfy the code coverage. */ + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_write_error_request = 0; + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + +#ifdef FX_FAULT_TOLERANT + if (first_test) + { + first_test = 0; + goto TEST_START; + } +#endif + printf("SUCCESS!\n"); + test_control_return(0); +} + + +#else +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_media_read_write_sector_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Media read/write sector test...........................N/A\n"); + + test_control_return(255); +} +#endif + diff --git a/test/regression_test/filex_media_volume_directory_entry_test.c b/test/regression_test/filex_media_volume_directory_entry_test.c new file mode 100644 index 0000000..73adf5f --- /dev/null +++ b/test/regression_test/filex_media_volume_directory_entry_test.c @@ -0,0 +1,179 @@ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#include "tx_thread.h" +#endif +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_ram_driver_test.h" +#include +#include + +#define DEMO_STACK_SIZE 8192 +/* Set the cache size as the size of one sector causing frequently IO operation. */ +#define CACHE_SIZE 128 +#define CACHE_SIZE1 512 + +#define VOLUME_LABEL "Vlabel" + +/* Define the global variable we may use in the future. */ +extern ULONG _fx_ram_driver_copy_default_format; +extern UCHAR large_file_name_format[]; + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +static UCHAR *cache_buffer1; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +static UCHAR cache_buffer1[CACHE_SIZE1]; +#endif + +/* Define thread prototypes. */ + +void filex_media_volume_directory_entry_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_media_volume_directory_entry_application_define(void *first_unused_memory) +#endif +{ + + #ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *)first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + cache_buffer1 = pointer; + pointer = pointer + CACHE_SIZE1; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + +static UCHAR specified_unicode_file_name[] = { 'Z', 0, '1', 0, '2', 0, '3', 0, '4', 0, '5', 0, '6', 0, '7', 0, '.', 0, 't', 0, 'x', 0, 't', 0, 0, 0 }; + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + UINT status; + ULONG ulength; + UCHAR destination_name[128]; + UCHAR name_buffer[128]; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Media volume directory entry test......................"); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Create a file with long file name. */ + ulength = fx_unicode_length_get( specified_unicode_file_name); + status = fx_unicode_file_create( &ram_disk, specified_unicode_file_name, ulength, (CHAR *)destination_name); + return_if_fail( status == FX_SUCCESS); + + /* Set and get the volume name. */ + status = fx_media_volume_set( &ram_disk, "NO NAME"); + return_if_fail( status == FX_SUCCESS); + + status = fx_media_volume_get( &ram_disk, (CHAR *)name_buffer, FX_DIRECTORY_SECTOR); + return_if_fail( ( status == FX_SUCCESS) && ( 0 == strcmp( (CHAR *)name_buffer, "NO NAME"))); + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Test for creating direcoty with same name as volume" */ + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + status = fx_media_volume_set(&ram_disk, VOLUME_LABEL); + return_if_fail( status == FX_SUCCESS); + + status = fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + status = fx_directory_create(&ram_disk, VOLUME_LABEL); + return_if_fail( status == FX_SUCCESS); + + status = fx_media_flush(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + + + printf("SUCCESS!\n"); + test_control_return(0); +} diff --git a/test/regression_test/filex_media_volume_get_set_test.c b/test/regression_test/filex_media_volume_get_set_test.c new file mode 100644 index 0000000..d81c994 --- /dev/null +++ b/test/regression_test/filex_media_volume_get_set_test.c @@ -0,0 +1,1006 @@ +/* This FileX test concentrates on the basic media volume get/set operation. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_media.h" +#include "fx_utility.h" +#include "fx_ram_driver_test.h" +#include + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 16*128 + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + + +/* Define thread prototypes. */ + +void filex_media_volume_get_set_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_media_volume_get_set_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +CHAR volume_buffer[32]; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Media volume get/set test.............................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(2); + } + + /* try to set the media volume name before the media is opened */ + status = fx_media_volume_set(&ram_disk, "NEW"); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(11); + } + + /* try to get the media volume name before the media is opened */ + status = fx_media_volume_get(&ram_disk, volume_buffer, FX_BOOT_SECTOR); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(11); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(3); + } + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + /* send null pointer to generate an error */ + status = fx_media_volume_get(FX_NULL, volume_buffer, FX_DIRECTORY_SECTOR); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(11); + } + + /* send null pointer to generate an error for extended version */ + status = fx_media_volume_get_extended(FX_NULL, volume_buffer, 0, FX_DIRECTORY_SECTOR); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(11); + } + + /* send null pointer to generate an error */ + status = fx_media_volume_set(FX_NULL, ""); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(11); + } +#else + /* try to set the media volume name to something invalid */ + status = fx_media_volume_set(&ram_disk, ""); + if (status != FX_INVALID_NAME) + { + printf("ERROR!\n"); + test_control_return(11); + } +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Get the current volume name - from boot record. */ + status = fx_media_volume_get(&ram_disk, volume_buffer, FX_BOOT_SECTOR); + + /* Determine if there was an error getting the volume name. */ + if ((status != FX_SUCCESS) || + (volume_buffer[0] != 'M') || + (volume_buffer[1] != 'Y') || + (volume_buffer[2] != '_') || + (volume_buffer[3] != 'R') || + (volume_buffer[4] != 'A') || + (volume_buffer[5] != 'M') || + (volume_buffer[6] != '_') || + (volume_buffer[7] != 'D') || + (volume_buffer[8] != 'I') || + (volume_buffer[9] != 'S') || + (volume_buffer[10] != 'K')) + { + + /* Error getting the volume name. */ + printf("ERROR!\n"); + test_control_return(4); + } + + /* Set volume_buffer to known state. */ + memcpy(volume_buffer, "ORIGINALSTRING", 15); + + /* Get the current volume name - from boot record, using extended version. */ + status = fx_media_volume_get_extended(&ram_disk, volume_buffer, 2, FX_BOOT_SECTOR); + + /* Determine if there was an error getting the volume name. */ + if ((status != FX_BUFFER_ERROR) || + (volume_buffer[0] != 'M') || + (volume_buffer[1] != '\0') || + (volume_buffer[2] != 'I') || + (volume_buffer[3] != 'G')) + { + + /* Error getting the volume name. */ + printf("ERROR!\n"); + test_control_return(4); + } + + + /* Get the current volume name - from root directory. */ + status = fx_media_volume_get(&ram_disk, volume_buffer, FX_DIRECTORY_SECTOR); + + /* Determine if we found a volume name in the root directory. + * When not, the value from boot sector will be returned. */ + if ((status != FX_SUCCESS) || + (volume_buffer[0] != 'M') || + (volume_buffer[1] != 'Y') || + (volume_buffer[2] != '_') || + (volume_buffer[3] != 'R') || + (volume_buffer[4] != 'A') || + (volume_buffer[5] != 'M') || + (volume_buffer[6] != '_') || + (volume_buffer[7] != 'D') || + (volume_buffer[8] != 'I') || + (volume_buffer[9] != 'S') || + (volume_buffer[10] != 'K')) + { + + /* Error getting the volume name. */ + printf("ERROR!\n"); + test_control_return(5); + } + + /* It is now time to set the volume name. */ + status = fx_media_volume_set(&ram_disk, "NEW"); + + /* Determine if the volume name set was successful... */ + if (status != FX_SUCCESS) + { + + /* Error setting the volume name. */ + printf("ERROR!\n"); + test_control_return(6); + } + + /* Flush and invalidate the media... */ + fx_media_cache_invalidate(&ram_disk); + + /* Get the current volume name - from boot record. */ + status = fx_media_volume_get(&ram_disk, volume_buffer, FX_BOOT_SECTOR); + + /* Determine if there was an error getting the volume name. */ + if ((status != FX_SUCCESS) || + (volume_buffer[0] != 'N') || + (volume_buffer[1] != 'E') || + (volume_buffer[2] != 'W') || + (volume_buffer[3] != (CHAR) 0)) + { + + /* Error getting the volume name. */ + printf("ERROR!\n"); + test_control_return(7); + } + + /* Get the current volume name - from root directory. */ + status = fx_media_volume_get(&ram_disk, volume_buffer, FX_DIRECTORY_SECTOR); + + /* Determine if we found a volume name in the root directory... should not since our + format doesn't do that! */ + if ((status != FX_SUCCESS) || + (volume_buffer[0] != 'N') || + (volume_buffer[1] != 'E') || + (volume_buffer[2] != 'W') || + (volume_buffer[3] != (CHAR) 0)) + { + + /* Error getting the volume name. */ + printf("ERROR!\n"); + test_control_return(8); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + + /* Format the media in FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors - FAT 32 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(11); + } + + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(12); + } + + /* Set the volume name to a NULL volume name. */ + volume_buffer[0] = 0; + status = _fx_media_volume_set(&ram_disk, volume_buffer); + if (status != FX_INVALID_NAME) + { + printf("ERROR!\n"); + test_control_return(13); + } + + /* Now set the volume name to something longer than 11 characters that are lower case. */ + status = fx_media_volume_set(&ram_disk, "thisisareallylongvolumename"); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(14); + } + + /* Now set the volume name to something small with lower case. */ + status = fx_media_volume_set(&ram_disk, "sm"); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(15); + } + + /* Now get the volume name from the boot sector. */ + status = fx_media_volume_get(&ram_disk, volume_buffer, FX_BOOT_SECTOR); + if ((status != FX_SUCCESS) || (volume_buffer[0] != 'S') || (volume_buffer[1] != 'M') || (volume_buffer[2] != 0)) + { + printf("ERROR!\n"); + test_control_return(16); + } + + /* Now get the volume name from the directory sector. */ + status = fx_media_volume_get(&ram_disk, volume_buffer, FX_DIRECTORY_SECTOR); + if ((status != FX_SUCCESS) || (volume_buffer[0] != 'S') || (volume_buffer[1] != 'M') || (volume_buffer[2] != 0)) + { + printf("ERROR!\n"); + test_control_return(17); + } + + /* Set volume_buffer to known state. */ + memcpy(volume_buffer, "ORIGINALSTRING", 15); + + /* Get the current volume name - from root directory. */ + status = fx_media_volume_get_extended(&ram_disk, volume_buffer, 2, FX_DIRECTORY_SECTOR); + + /* Determine if we found a volume name in the root directory. + * When not, the value from boot sector will be returned. */ + if ((status != FX_BUFFER_ERROR) || + (volume_buffer[0] != 'S') || + (volume_buffer[1] != '\0') || + (volume_buffer[2] != 'I') || + (volume_buffer[3] != 'G')) + { + + /* Error getting the volume name. */ + printf("ERROR!\n"); + test_control_return(17); + } + /* Close the media. */ + status = fx_media_close(&ram_disk); + + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(18); + } + + + /* Format the media in FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 16, // Directory Entries + 0, // Hidden sectors + 7000, // Total sectors - FAT 16 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(19); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(20); + } + + /* Fill up the root directory (16 entries), so the volume set won't find an empty entry. */ + status = fx_file_create(&ram_disk, "FILE1.TXT"); + status += fx_file_create(&ram_disk, "FILE2.TXT"); + status += fx_file_create(&ram_disk, "FILE3.TXT"); + status += fx_file_create(&ram_disk, "FILE4.TXT"); + status += fx_file_create(&ram_disk, "FILE5.TXT"); + status += fx_file_create(&ram_disk, "FILE6.TXT"); + status += fx_file_create(&ram_disk, "FILE7.TXT"); + status += fx_file_create(&ram_disk, "FILE8.TXT"); + status += fx_file_create(&ram_disk, "FILE9.TXT"); + status += fx_file_create(&ram_disk, "FILE10.TXT"); + status += fx_file_create(&ram_disk, "FILE11.TXT"); + status += fx_file_create(&ram_disk, "FILE12.TXT"); + status += fx_file_create(&ram_disk, "FILE13.TXT"); + status += fx_file_create(&ram_disk, "FILE14.TXT"); + status += fx_file_create(&ram_disk, "FILE15.TXT"); + status += fx_file_create(&ram_disk, "FILE16.TXT"); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(21); + } + + /* Now set the volume name. */ + status = fx_media_volume_set(&ram_disk, "MINE "); + if (status != FX_MEDIA_INVALID) + { + printf("ERROR!\n"); + test_control_return(22); + } + + /* Now delete the first entry so we can find it. */ + status = fx_file_delete(&ram_disk, "FILE1.TXT"); + + /* And set the volume name again. */ + status += fx_media_volume_set(&ram_disk, "MINE "); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(23); + } + + /* Set the volume name again - should just use the same directory entry. */ + status = fx_media_volume_set(&ram_disk, "MINE1 "); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(24); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(25); + } + + /* Format the media in FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 16, // Directory Entries + 0, // Hidden sectors + 7000, // Total sectors - FAT 16 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(26); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(27); + } + + /* Fill up the root directory (16 entries), so the volume set won't find an empty entry. */ + status = fx_file_create(&ram_disk, "FILE1.TXT"); + status += fx_file_create(&ram_disk, "FILE2.TXT"); + status += fx_file_create(&ram_disk, "FILE3.TXT"); + status += fx_file_create(&ram_disk, "FILE4.TXT"); + status += fx_file_create(&ram_disk, "FILE5.TXT"); + status += fx_file_create(&ram_disk, "FILE6.TXT"); + status += fx_file_create(&ram_disk, "FILE7.TXT"); + status += fx_file_create(&ram_disk, "FILE8.TXT"); + status += fx_file_create(&ram_disk, "FILE9.TXT"); + status += fx_file_create(&ram_disk, "FILE10.TXT"); + status += fx_file_create(&ram_disk, "FILE11.TXT"); + status += fx_file_create(&ram_disk, "FILE12.TXT"); + status += fx_file_create(&ram_disk, "FILE13.TXT"); + status += fx_file_create(&ram_disk, "FILE14.TXT"); + status += fx_file_create(&ram_disk, "FILE15.TXT"); + status += fx_file_create(&ram_disk, "FILE16.TXT"); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(28); + } + + /* Now delete the first entry so we can find it. */ + status = fx_file_delete(&ram_disk, "FILE16.TXT"); + + /* And set the volume name again. */ + status += fx_media_volume_set(&ram_disk, "mine~ "); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(29); + } + + /* Set the volume name again - should just use the same directory entry. */ + status = fx_media_volume_set(&ram_disk, "MINE1 "); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(30); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(31); + } + + /* Format the media in FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 16, // Directory Entries + 0, // Hidden sectors + 7000, // Total sectors - FAT 16 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(33); + } + + /* Almost fill up the root directory (15 entries), so the volume set won't find an empty entry. */ + status = fx_file_create(&ram_disk, "FILE1.TXT"); + status += fx_file_create(&ram_disk, "FILE2.TXT"); + status += fx_file_create(&ram_disk, "FILE3.TXT"); + status += fx_file_create(&ram_disk, "FILE4.TXT"); + status += fx_file_create(&ram_disk, "FILE5.TXT"); + status += fx_file_create(&ram_disk, "FILE6.TXT"); + status += fx_file_create(&ram_disk, "FILE7.TXT"); + status += fx_file_create(&ram_disk, "FILE8.TXT"); + status += fx_file_create(&ram_disk, "FILE9.TXT"); + status += fx_file_create(&ram_disk, "FILE10.TXT"); + status += fx_file_create(&ram_disk, "FILE11.TXT"); + status += fx_file_create(&ram_disk, "FILE12.TXT"); + status += fx_file_create(&ram_disk, "FILE13.TXT"); + status += fx_file_create(&ram_disk, "FILE14.TXT"); + status += fx_file_create(&ram_disk, "FILE15.TXT"); + + /* Leave the last entry free for search test. */ + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(34); + } + + /* Set the volume name. */ + status += fx_media_volume_set(&ram_disk, "MY "); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(35); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(36); + } + + /* Format the media in FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 16, // Directory Entries + 0, // Hidden sectors + 7000, // Total sectors - FAT 16 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(37); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(38); + } + + /* Almost fill up the root directory (15 entries), so the volume set won't find an empty entry. */ + status = fx_file_create(&ram_disk, "FILE1.TXT"); + status += fx_file_create(&ram_disk, "FILE2.TXT"); + status += fx_file_create(&ram_disk, "FILE3.TXT"); + status += fx_file_create(&ram_disk, "FILE4.TXT"); + status += fx_file_create(&ram_disk, "FILE5.TXT"); + status += fx_file_create(&ram_disk, "FILE6.TXT"); + status += fx_file_create(&ram_disk, "FILE7.TXT"); + status += fx_file_create(&ram_disk, "FILE8.TXT"); + status += fx_file_create(&ram_disk, "FILE9.TXT"); + status += fx_file_create(&ram_disk, "FILE10.TXT"); + status += fx_file_create(&ram_disk, "FILE11.TXT"); + status += fx_file_create(&ram_disk, "FILE12.TXT"); + status += fx_file_create(&ram_disk, "FILE13.TXT"); + status += fx_file_create(&ram_disk, "FILE14.TXT"); + status += fx_file_create(&ram_disk, "FILE15.TXT"); + + /* Leave the last entry free for search test... and delete file 5 and 6 to create some available slots. */ + status += fx_file_delete(&ram_disk, "FILE6.TXT"); + status += fx_file_delete(&ram_disk, "FILE7.TXT"); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(39); + } + + /* Set the volume name. */ + status = fx_media_volume_set(&ram_disk, "MY "); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(40); + } + + /* Set the volume name again. */ + status = fx_media_volume_set(&ram_disk, "MY "); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(41); + } + + /* Test the I/O errors. */ + + /* Cause an error reading sector 0. */ + _fx_utility_logical_sector_flush(&ram_disk, 0, 60000, FX_TRUE); + _fx_ram_driver_io_error_request = 1; + status = fx_media_volume_set(&ram_disk, "MY "); + _fx_ram_driver_io_error_request = 0; + if (status != FX_IO_ERROR) + { + printf("ERROR!\n"); + test_control_return(42); + } + + /* Cause an error reading the boot sector. */ + _fx_utility_logical_sector_flush(&ram_disk, 0, 60000, FX_TRUE); + _fx_ram_driver_io_error_request = 2; + status = fx_media_volume_set(&ram_disk, "MY "); + _fx_ram_driver_io_error_request = 0; + if (status != FX_IO_ERROR) + { + printf("ERROR!\n"); + test_control_return(43); + } + + /* Cause an error writing the boot sector. */ + _fx_utility_logical_sector_flush(&ram_disk, 0, 60000, FX_TRUE); + _fx_ram_driver_io_error_request = 2; + status = fx_media_volume_set(&ram_disk, "MY "); + _fx_ram_driver_io_error_request = 0; + if (status != FX_IO_ERROR) + { + printf("ERROR!\n"); + test_control_return(44); + } + + /* Cause an error reading the first directory entry. */ + _fx_utility_logical_sector_flush(&ram_disk, 0, 60000, FX_TRUE); + _fx_ram_driver_io_error_request = 4; + status = fx_media_volume_set(&ram_disk, "MY "); + _fx_ram_driver_io_error_request = 0; + if (status != FX_IO_ERROR) + { + printf("ERROR!\n"); + test_control_return(45); + } + + /* Cause an error reading the logical directory sector. */ + _fx_utility_logical_sector_flush(&ram_disk, 0, 60000, FX_TRUE); + _fx_utility_logical_sector_read_error_request = 8; + status = fx_media_volume_set(&ram_disk, "MY "); + _fx_utility_logical_sector_write_error_request = 0; + if (status != FX_IO_ERROR) + { + printf("ERROR!\n"); + test_control_return(46); + } + + /* Cause an error reading sector 0. */ + _fx_utility_logical_sector_flush(&ram_disk, 0, 60000, FX_TRUE); + _fx_ram_driver_io_error_request = 1; + status = fx_media_volume_get(&ram_disk, volume_buffer, FX_BOOT_SECTOR); + _fx_ram_driver_io_error_request = 0; + if (status != FX_IO_ERROR) + { + printf("ERROR!\n"); + test_control_return(47); + } + + /* Cause an error reading the boot sector. */ + _fx_utility_logical_sector_flush(&ram_disk, 0, 60000, FX_TRUE); + _fx_ram_driver_io_error_request = 2; + status = fx_media_volume_get(&ram_disk, volume_buffer, FX_BOOT_SECTOR); + _fx_ram_driver_io_error_request = 0; + if (status != FX_IO_ERROR) + { + printf("ERROR!\n"); + test_control_return(48); + } + + /* Cause an error reading the directory sector. */ + _fx_utility_logical_sector_flush(&ram_disk, 0, 60000, FX_TRUE); + _fx_ram_driver_io_error_request = 1; + status = fx_media_volume_get(&ram_disk, volume_buffer, FX_DIRECTORY_SECTOR); + _fx_ram_driver_io_error_request = 0; + if (status != FX_IO_ERROR) + { + printf("ERROR!\n"); + test_control_return(49); + } + + /* Set a maximum length VOLUME NAME and retrieve it. */ + status = fx_media_volume_set(&ram_disk, "MYVOLUME123"); + status += fx_media_volume_get(&ram_disk, volume_buffer, FX_DIRECTORY_SECTOR); + + /* Check for status. */ + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(50); + } + + /* Set a maximum length VOLUME NAME and retrieve it. */ + status = fx_media_volume_set(&ram_disk, " "); + status += fx_media_volume_get(&ram_disk, volume_buffer, FX_BOOT_SECTOR); + + /* Check for status. */ + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(53); + } + + status = fx_media_volume_get(&ram_disk, volume_buffer, FX_DIRECTORY_SECTOR); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(51); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(52); + } + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail(status == FX_SUCCESS); + + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail(status == FX_SUCCESS); + + status = fx_media_volume_set(&ram_disk, "NEW"); + return_if_fail(status == FX_SUCCESS); + + /* Fail to read the directory entry of the volume. */ + _fx_utility_logical_sector_read_error_request = 2; + status = fx_media_volume_get(&ram_disk, volume_buffer, FX_DIRECTORY_SECTOR); + return_if_fail(status == FX_IO_ERROR); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(53); + } + + /* This test is added to check if fx_media_volume_get() returns volume entry that was marked free */ + memset(ram_disk_memory,'\0',sizeof(ram_disk_memory)); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "NO NAME", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 512, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail(status == FX_SUCCESS); + + status = fx_media_open(&ram_disk, "RAMDISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail(status == FX_SUCCESS);; + + /* volume_buffer will contain "NO NAME" */ + status = fx_media_volume_get(&ram_disk, volume_buffer, FX_DIRECTORY_SECTOR); + return_if_fail(status == FX_SUCCESS); + + status = fx_media_volume_set(&ram_disk, "56781234"); + return_if_fail(status == FX_SUCCESS); + fx_media_cache_invalidate(&ram_disk); + + /* volume_buffer will contain "56781234" */ + status = fx_media_volume_get(&ram_disk, volume_buffer, FX_BOOT_SECTOR); + return_if_fail(status == FX_SUCCESS); + + /* volume_buffer will contain "56781234" */ + status = fx_media_volume_get(&ram_disk, volume_buffer, FX_DIRECTORY_SECTOR); + return_if_fail(status == FX_SUCCESS); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(54); + } + + /* Modify first character of the volume label entry in boot sector. This is done to verify + if the volume entry marked free is returned or not */ + ram_disk_memory[71] = 0x39; + + /* First Character of Volume label in data section + The absolute address was found by looping accross the ram_disk_memory to find the Volume name in data section */ + ram_disk_memory[2241024] = FX_DIR_ENTRY_FREE; + + memset(cache_buffer,'\0',sizeof(cache_buffer)); + + status = fx_media_open(&ram_disk, "RAMDISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail(status == FX_SUCCESS);; + status = fx_media_volume_get(&ram_disk, volume_buffer, FX_DIRECTORY_SECTOR); + return_if_fail(status == FX_SUCCESS); + + if ((status != FX_SUCCESS) || (volume_buffer[0] != '9')) + { + printf("ERROR!\n"); + test_control_return(55); + } + + printf("SUCCESS!\n"); + test_control_return(0); +} + diff --git a/test/regression_test/filex_system_date_time_test.c b/test/regression_test/filex_system_date_time_test.c new file mode 100644 index 0000000..5b28e8b --- /dev/null +++ b/test/regression_test/filex_system_date_time_test.c @@ -0,0 +1,431 @@ +/* This FileX test concentrates on the system date/time set/get operations. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_system.h" +#include +#include "fx_ram_driver_test.h" + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 16*128 + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + + +/* Define thread prototypes. */ + +void filex_system_date_time_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_system_date_time_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UINT save_time; +UINT save_date; +UINT year, month, day, hour, minute, second; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: System date/time get/set test.........................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(2); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(3); + } + +/* test for error checking by sending bad information */ +#ifndef FX_DISABLE_ERROR_CHECKING + + /* check system date get pointer error */ + status = fx_system_date_get(FX_NULL, FX_NULL, FX_NULL); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(4); + } + + /* check system time get pointer error */ + status = fx_system_time_get(FX_NULL, FX_NULL, FX_NULL); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(5); + } + + /* check system time set bad hours */ + status = fx_system_time_set(99, 0, 0); + if (status != FX_INVALID_HOUR) + { + printf("ERROR!\n"); + test_control_return(6); + } + + /* check system time set bad minutes */ + status = fx_system_time_set(0, 99, 0); + if (status != FX_INVALID_MINUTE) + { + printf("ERROR!\n"); + test_control_return(7); + } + + /* check system time set bad seconds */ + status = fx_system_time_set(0, 0, 99); + if (status != FX_INVALID_SECOND) + { + printf("ERROR!\n"); + test_control_return(8); + } + + /* check system date set bad year */ + status = fx_system_date_set(99, 6, 6); + if (status != FX_INVALID_YEAR) + { + printf("ERROR!\n"); + test_control_return(9); + } + + /* check system date set bad month */ + status = fx_system_date_set(2015, 99, 6); + if (status != FX_INVALID_MONTH) + { + printf("ERROR!\n"); + test_control_return(10); + } + + /* check good and bad inputs for each month */ + for (i = 1; i <= 12; i++) + { + /* valid input for month i */ + status = fx_system_date_set(2015, i, 6); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(11); + } + + /* invalid input for month i */ + status = fx_system_date_set(2015, i, 99); + if (status != FX_INVALID_DAY) + { + printf("ERROR!\n"); + test_control_return(12); + } + } + + /* check bad input for feb on a leap year */ + status = fx_system_date_set(2016, 2, 99); + if (status != FX_INVALID_DAY) + { + printf("ERROR!\n"); + test_control_return(13); + } + + +#endif + + /* Set the date and time. */ + status = fx_system_date_set(1999, 12, 31); + + /* Check for successful date set. */ + if (status != FX_SUCCESS) + { + + /* Error setting date. Return to caller. */ + printf("ERROR!\n"); + test_control_return(4); + } + + status = fx_system_time_set(23, 59, 58); + + /* Check for successful time set. */ + if (status != FX_SUCCESS) + { + + /* Error setting time. Return to caller. */ + printf("ERROR!\n"); + test_control_return(14); + } + + /* Call the internal system date get with NULL values. */ + status = _fx_system_date_get(FX_NULL, FX_NULL, FX_NULL); + + /* Get the date. */ + status += fx_system_date_get(&year, &month, &day); + + /* Check for successful date get. */ + if ((status != FX_SUCCESS) || + (year != 1999) || + (month != 12) || + (day != 31)) + { + + /* Error getting date. Return to caller. */ + printf("ERROR!\n"); + test_control_return(15); + } + + /* Call the internal system time get. */ + status = _fx_system_time_get(FX_NULL, FX_NULL, FX_NULL); + + /* Get the time. */ + status += fx_system_time_get(&hour, &minute, &second); + + /* Check for successful time get. */ + if ((status != FX_SUCCESS) || + (hour != 23) || + (minute != 59) || + (second != 58)) + { + + /* Error getting time. Return to caller. */ + printf("ERROR!\n"); + test_control_return(16); + } +#ifndef FX_DISABLE_ERROR_CHECKING + /* Get the time, but with a NULL value for month. */ + status = fx_system_time_get(&hour, FX_NULL, &second); + + /* Check for successful time get. */ + if (status != FX_PTR_ERROR) + { + + /* Error getting time. Return to caller. */ + printf("ERROR!\n"); + test_control_return(17); + } + + /* Get the time, but with a NULL value for second. */ + status = fx_system_time_get(&hour, &minute, FX_NULL); + + /* Check for successful time get. */ + if (status != FX_PTR_ERROR) + { + + /* Error getting time. Return to caller. */ + printf("ERROR!\n"); + test_control_return(18); + } + + /* Get the date, but with a NULL value for month. */ + status = fx_system_date_get(&year, FX_NULL, &day); + + /* Check for successful date get. */ + if (status != FX_PTR_ERROR) + { + + /* Error getting date. Return to caller. */ + printf("ERROR!\n"); + test_control_return(19); + } + + /* Get the date, but with a NULL value for day. */ + status = fx_system_date_get(&year, &month, FX_NULL); + + /* Check for successful date get. */ + if (status != FX_PTR_ERROR) + { + + /* Error getting date. Return to caller. */ + printf("ERROR!\n"); + test_control_return(20); + } + + /* Set the date, but with an invalid year over the max. */ + status = fx_system_date_set(FX_MAXIMUM_YEAR+1, 12, 1); + + /* Check for successful date set. */ + if (status != FX_INVALID_YEAR) + { + + /* Error setting date. Return to caller. */ + printf("ERROR!\n"); + test_control_return(21); + } + + /* Set the date, but with an invalid day (0). */ + status = fx_system_date_set(2016, 12, 0); + + /* Check for successful date set. */ + if (status != FX_INVALID_DAY) + { + + /* Error setting date. Return to caller. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Set the date, but with a leap year an invalid day 30 for February. */ + status = fx_system_date_set(2016, 2, 30); + + /* Check for successful date set. */ + if (status != FX_INVALID_DAY) + { + + /* Error setting date. Return to caller. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Set the date, but with a leap year and a valid day 29 for February. */ + status = fx_system_date_set(2016, 2, 29); + + /* Check for successful date set. */ + if (status != FX_SUCCESS) + { + + /* Error setting date. Return to caller. */ + printf("ERROR!\n"); + test_control_return(23); + } +#endif + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Drive the system time logic. */ + + /* Save the system date/time. */ + save_date = _fx_system_date; + save_time = _fx_system_time; + + /* First call the system timer entry function with an invalid ID. */ + _fx_system_timer_entry(0); + + /* Now setup an invalid month to cause the error logic to hit. */ + _fx_system_date = _fx_system_date | 0x1E0; /* Set month to all ones - invalid > 12 */ + + /* Now loop for the amount of updates (10 seconds per update) to cause the day to wrap. */ + for (i = 0; i < 86400; i++) + { + /* Adjust the time by calling the time update function. */ + _fx_system_timer_entry(FX_TIMER_ID); + } + + /* Restore the system date/time. */ + _fx_system_date = save_date; + _fx_system_time = save_time; + + /* Now call the system timer entry to walk through the maximum system time. */ + for (i = 0; i < 315360000; i++) + { + + /* Adjust the time by calling the time update function. */ + _fx_system_timer_entry(FX_TIMER_ID); + } + + /* Restore the system date/time. */ + _fx_system_date = save_date; + _fx_system_time = save_time; + + /* Determine if the test was successful. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(10); + } + else + { + + printf("SUCCESS!\n"); + test_control_return(0); + } +} + diff --git a/test/regression_test/filex_unicode_2_test.c b/test/regression_test/filex_unicode_2_test.c new file mode 100644 index 0000000..8d9dbb4 --- /dev/null +++ b/test/regression_test/filex_unicode_2_test.c @@ -0,0 +1,212 @@ +/* Test the condition of fat entry broken. */ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_ram_driver_test.h" +#include +#include + +#define DEMO_STACK_SIZE 8192 +/* Set the cache size as the size of one sector causing frequently IO operation. */ +#define CACHE_SIZE 128 + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + +/* Define thread prototypes. */ + +void filex_unicode_2_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + +/* Create a terrible driver. */ +static UINT driver_called_counter = 0; +static void _fx_terrible_driver(FX_MEDIA *media_ptr) +{ + driver_called_counter++; + //printf("\n_fx_terrible_driver has been called %d times.", driver_called_counter); + + // Ensure the variable we want to operate normal at the rest of time. + media_ptr -> fx_media_root_cluster_32 = 2; + // To cover the branch at Line 203 in fx_unicode_directory_entry_read.c, modify fx_media_root_cluster_32 at a particular time. + if ( driver_called_counter == 2) + { + media_ptr -> fx_media_root_cluster_32 = 0xffffffff; + } + if ( driver_called_counter == 9) + { + media_ptr -> fx_media_root_cluster_32 = 1; + } + (* _fx_ram_driver)(media_ptr); +} + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_unicode_2_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + +static UCHAR long_unicode_name1[] = {1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 0, 0}; +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ +UINT status, length; +UCHAR destination_name[128]; +UCHAR destination_name_1[128]; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Unicode 2 test........................................."); + + /* Format the media as FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + 128, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + return_value_if_fail( status == FX_SUCCESS, 1); + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_value_if_fail( status == FX_SUCCESS, 2); + + length = fx_unicode_length_get( long_unicode_name1); + status = fx_unicode_directory_create( &ram_disk, long_unicode_name1, length, (CHAR *)destination_name); + long_unicode_name1[0]++; + length = fx_unicode_length_get( long_unicode_name1); + status = fx_unicode_file_create( &ram_disk, long_unicode_name1, length, (CHAR *)destination_name_1); + long_unicode_name1[0]++; + length = fx_unicode_length_get( long_unicode_name1); + status = fx_unicode_file_create( &ram_disk, long_unicode_name1, length, (CHAR *)destination_name_1); + + status = fx_media_flush(&ram_disk); + return_value_if_fail( status == FX_SUCCESS, 3); + + // Register our terrible driver. + ram_disk.fx_media_driver_entry = _fx_terrible_driver; + + long_unicode_name1[0]++; + length = fx_unicode_length_get( long_unicode_name1); + status = fx_unicode_file_create( &ram_disk, long_unicode_name1, length, (CHAR *)destination_name_1); + return_value_if_fail( status == FX_SUCCESS, 4); + + // Register our terrible driver. + ram_disk.fx_media_driver_entry = _fx_ram_driver; + + /* Corrupt the media. */ + ram_disk.fx_media_bytes_per_sector = 0; + status = fx_unicode_file_create(&ram_disk, long_unicode_name1, length, (CHAR*)destination_name_1); + return_value_if_fail(status == FX_MEDIA_INVALID, 4); + ram_disk.fx_media_bytes_per_sector = 128; + + status = fx_media_abort( &ram_disk); + return_value_if_fail( status == FX_SUCCESS, 5); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + 128, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + return_value_if_fail( status == FX_SUCCESS, 6); + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_value_if_fail( status == FX_SUCCESS, 7); + + length = fx_unicode_length_get( long_unicode_name1); + status = fx_unicode_directory_create( &ram_disk, long_unicode_name1, length, (CHAR *)destination_name); + long_unicode_name1[0]++; + length = fx_unicode_length_get( long_unicode_name1); + status = fx_unicode_file_create( &ram_disk, long_unicode_name1, length, (CHAR *)destination_name_1); + long_unicode_name1[0]++; + length = fx_unicode_length_get( long_unicode_name1); + status = fx_unicode_file_create( &ram_disk, long_unicode_name1, length, (CHAR *)destination_name_1); + + status = fx_media_flush(&ram_disk); + return_value_if_fail( status == FX_SUCCESS, 8); + + // Register our terrible driver. + ram_disk.fx_media_driver_entry = _fx_terrible_driver; + + long_unicode_name1[0]++; + length = fx_unicode_length_get( long_unicode_name1); + status = fx_unicode_file_create( &ram_disk, long_unicode_name1, length, (CHAR *)destination_name_1); + // Our modification will not influence the result. + return_value_if_fail( status == FX_SUCCESS, 9); + + // Register our terrible driver. + ram_disk.fx_media_driver_entry = _fx_ram_driver; + + status = fx_media_abort( &ram_disk); + return_value_if_fail( status == FX_SUCCESS, 10); + + printf("SUCCESS!\n"); + test_control_return(0); +} diff --git a/test/regression_test/filex_unicode_3_test.c b/test/regression_test/filex_unicode_3_test.c new file mode 100644 index 0000000..0926d23 --- /dev/null +++ b/test/regression_test/filex_unicode_3_test.c @@ -0,0 +1,165 @@ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_ram_driver_test.h" +#include +#include + +#define DEMO_STACK_SIZE 8192 +/* Set the cache size as the size of one sector causing frequently IO operation. */ +#define CACHE_SIZE 128 + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + +/* Define thread prototypes. */ + +void filex_unicode_3_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + +/* Create a terrible driver. */ +static UINT driver_called_counter = 0; +static void _fx_terrible_driver(FX_MEDIA *media_ptr) +{ + driver_called_counter++; +// printf("\n_fx_terrible_driver has been called %d times.", driver_called_counter); + if ( + // Make IO ERROR to reach the Line 202 in fx_unicode_directory_search.c + (driver_called_counter == 1) + ) + { + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + return; + } + (* _fx_ram_driver)(media_ptr); +} + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_unicode_3_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + +static UCHAR long_unicode_name1[] = {1, 0, 0, 0}; +static UCHAR long_unicode_name2[] = {1, 0, 2, 0, 0, 0}; + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status, length; +UCHAR destination_name[128]; +FX_LOCAL_PATH local_path; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Unicode 3 test........................................."); + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + 128, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + return_value_if_fail( status == FX_SUCCESS, 1); + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_value_if_fail( status == FX_SUCCESS, 2); + + // Locate at a subdir. + length = fx_unicode_length_get( long_unicode_name1); + status = fx_unicode_directory_create( &ram_disk, long_unicode_name1, length, (CHAR *)destination_name); +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, (CHAR *)destination_name); +#else + status = fx_directory_default_set(&ram_disk, (CHAR *)destination_name); +#endif + + /* Flush everything out. */ + status += fx_media_flush( &ram_disk); + status += _fx_utility_FAT_flush( &ram_disk); + status += _fx_utility_logical_sector_flush( &ram_disk, 1, 60000, FX_TRUE); + return_value_if_fail( status == FX_SUCCESS, 3); + + for (UINT i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + // Register our terrible driver. + ram_disk.fx_media_driver_entry = _fx_terrible_driver; + + // fx_unicode_file_create will call fx_unicode_directory_search where we will make an IO ERROR. + length = fx_unicode_length_get( long_unicode_name2); + status = fx_unicode_file_create( &ram_disk, long_unicode_name2, length, (CHAR *)destination_name); + return_value_if_fail( status == FX_SUCCESS, 4); + + // Register our terrible driver. + ram_disk.fx_media_driver_entry = _fx_ram_driver; + + status = fx_media_close( &ram_disk); + return_value_if_fail( status == FX_SUCCESS, 5); + + printf("SUCCESS!\n"); + test_control_return(0); +} diff --git a/test/regression_test/filex_unicode_4_test.c b/test/regression_test/filex_unicode_4_test.c new file mode 100644 index 0000000..7575db9 --- /dev/null +++ b/test/regression_test/filex_unicode_4_test.c @@ -0,0 +1,503 @@ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#include "tx_thread.h" +#endif +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_ram_driver_test.h" +#include +#include + +#define DEMO_STACK_SIZE 8192 +/* Set the cache size as the size of one sector causing frequently IO operation. */ +#define CACHE_SIZE 128 + +/* Define the global variable we may use in the future. */ +extern ULONG _fx_ram_driver_copy_default_format; +extern UCHAR large_file_name_format[]; + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + +/* Define thread prototypes. */ + +void filex_unicode_4_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_unicode_4_application_define(void *first_unused_memory) +#endif +{ + + #ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *)first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + +static UCHAR long_unicode_name1[] = { 1, 0, 0, 0 }; +static UCHAR ascii_file_name[] = { 'a', 'b', 'c', 0 }; +static UCHAR specified_ascii_file_name[] = { 'Z', '1', '2', '3', '4', '5', '6', '7', '.', 't', 'x', 't', 0 }; +static UCHAR specified_unicode_file_name[] = { 'Z', 0, '1', 0, '2', 0, '3', 0, '4', 0, '5', 0, '6', 0, '7', 0, '.', 0, 't', 0, 'x', 0, 't', 0, 0, 0 }; +static UCHAR specified_unicode_file_name_1[] = { 'a', 0, '1', 0, '2', 0, '3', 0, '4', 0, '5', 0, '6', 0, '7', 0, '.', 0, 't', 0, 'x', 0, 't', 0, 0, 0 }; + +#ifndef FX_DISABLE_ERROR_CHECKING +static UCHAR specified_unicode_file_name_2[] = { 'a', 0, '1', 0, '2', 0, '3', 0, '4', 0, '5', 0, '6', 0, '7', 0, '.', 0, 't', 0, 'x', 0, 0, 't', 0, 0 }; +#endif /* FX_DISABLE_ERROR_CHECKING */ + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status, length, count; +#ifndef FX_DISABLE_ERROR_CHECKING +UINT new_length, old_length; +#endif /* FX_DISABLE_ERROR_CHECKING */ +ULONG ulength; +UCHAR destination_name[128]; +UCHAR destination_name_1[128]; +UCHAR temp, temp_array[32]; +FX_LOCAL_PATH local_path; +FX_FILE my_file; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Unicode 4 test........................................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + 128, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + return_value_if_fail(status == FX_SUCCESS, 2); + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_value_if_fail(status == FX_SUCCESS, 3); + + // Create a file whose name's first byte is 0xe5. + ascii_file_name[0] = 0xe5; + status = fx_file_create(&ram_disk, (CHAR *)ascii_file_name); + return_value_if_fail(status == FX_SUCCESS, 4); + + // Attempt to create a unicode file which make fx_unicode_file_create call fx_unicode_directory_search to scan the directory before creation. + // The file we just created will be read during the scannning so that the branch at Line 290 in fx_unicode_directory_search will be covered. + length = fx_unicode_length_get(long_unicode_name1); + status = fx_unicode_file_create(&ram_disk, long_unicode_name1, length, (CHAR *)destination_name); + return_value_if_fail(status == FX_SUCCESS, 5); + + // Attempt to create two 8.3 format file by both fx_file_create and fx_unicode_file_create to cover the branch at Line 324 in fx_unicode_directory_search.c + status = fx_file_create(&ram_disk, (CHAR *)specified_ascii_file_name); + return_value_if_fail(status == FX_SUCCESS, 6); + + length = fx_unicode_length_get(specified_unicode_file_name); + status = fx_unicode_file_create(&ram_disk, specified_unicode_file_name, length, (CHAR *)destination_name); + return_value_if_fail(status == FX_ALREADY_CREATED, 7); + + // Add test code to cover the branch at Line 146 in fx_unicode_directory_search.c + ascii_file_name[0] = 'a'; + status = fx_directory_create(&ram_disk, (CHAR *)ascii_file_name); +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, (CHAR *)ascii_file_name); +#else + status = fx_directory_default_set(&ram_disk, (CHAR *)ascii_file_name); +#endif + return_value_if_fail(status == FX_SUCCESS, 8); + + // Clear the name of current directory. +#ifndef FX_STANDALONE_ENABLE + ((FX_PATH*)_tx_thread_current_ptr->tx_thread_filex_ptr)->fx_path_directory.fx_dir_entry_name[0] = 0; +#else + status = fx_directory_default_set(&ram_disk, "\\"); +#endif + + // The file is attempted to be created in the root directory, so it will fail. + status = fx_unicode_file_create(&ram_disk, specified_unicode_file_name, length, (CHAR *)destination_name); + return_value_if_fail(status == FX_ALREADY_CREATED, 9); + + status = fx_media_close(&ram_disk); + return_value_if_fail(status == FX_SUCCESS, 10); + + /* Open the ram_disk in fx_ram_dirver_test.c which contain a long file name dir_entry associated with a free short name entry. */ + _fx_ram_driver_copy_default_format = 1; + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + return_value_if_fail(status == FX_SUCCESS, 11); + _fx_ram_driver_copy_default_format = 0; + + /* Cover the branch around Line 608 in fx_unicode_directory_entry_read.c. */ + length = fx_unicode_length_get(specified_unicode_file_name); + status = fx_unicode_file_create(&ram_disk, specified_unicode_file_name, length, (CHAR *)destination_name); + return_value_if_fail(status == FX_SUCCESS, 12); + + /* Create a 8.3 format volume dirctory entry. */ + status = fx_media_volume_set(&ram_disk, " . "); + return_value_if_fail(status == FX_SUCCESS, 13); + + /* Access our directory to cover the branch at Line 587 in fx_unicode_direcotry_entry_read. */ + length = fx_unicode_length_get(specified_unicode_file_name); + status = fx_unicode_file_create(&ram_disk, specified_unicode_file_name, length, (CHAR *)destination_name); + return_value_if_fail(status == FX_ALREADY_CREATED, 14); + + /* Create format volume dirctory entry with 11 blanks. */ + status = fx_media_volume_set(&ram_disk, " "); + return_value_if_fail(status == FX_SUCCESS, 15); + + /* Access our directory to cover the branch at Line 619 in fx_unicode_direcotry_entry_read. */ + length = fx_unicode_length_get(specified_unicode_file_name); + status = fx_unicode_file_create(&ram_disk, specified_unicode_file_name, length, (CHAR *)destination_name); + return_value_if_fail(status == FX_ALREADY_CREATED, 16); + + /* Changed first byte as lowercase. */ + specified_unicode_file_name[0] = 'z'; + status = fx_unicode_file_create(&ram_disk, specified_unicode_file_name, length, (CHAR *)destination_name); + return_value_if_fail(status == FX_ALREADY_CREATED, 17); + + /* Create a file whose name is begin with 'a'. */ + length = fx_unicode_length_get(specified_unicode_file_name_1); + status = fx_unicode_file_create(&ram_disk, specified_unicode_file_name_1, length, (CHAR *)destination_name); + return_value_if_fail(status == FX_SUCCESS, 18); + + /* Changed first byte as uppercase. */ + specified_unicode_file_name_1[0] = 'A'; + length = fx_unicode_length_get(specified_unicode_file_name_1); + status = fx_unicode_file_create(&ram_disk, specified_unicode_file_name_1, length, (CHAR *)destination_name); + return_value_if_fail(status == FX_ALREADY_CREATED, 19); + + /* Reuse our disk in fx_ram_driver_test.c to create a set of corrupt dir_entries include a normal dir_entry associated with a specified one whose first byte is 0. */ + status = fx_media_close(&ram_disk); + _fx_ram_driver_copy_default_format = 1; + + /* Modify the first byte at Line 1738 in fx_ram_driver_test.c. */ + temp = large_file_name_format[(1738 - 852) * 16]; + large_file_name_format[(1738 - 852) * 16] = 0x00; + + /* Modify the first dir_entry, which used to be the first short name dir_entry in the directory, to be the long name of "NT-FILE~.TXT". */ + for (count = 0; count < 32; count++) + { + temp_array[count] = large_file_name_format[(1732 - 852) * 16 + count]; + large_file_name_format[(1732 - 852) * 16 + count] = large_file_name_format[(1736 - 852) * 16 + count]; + } + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + return_value_if_fail(status == FX_SUCCESS, 20); + + /* Revert all we done. */ + _fx_ram_driver_copy_default_format = 0; + large_file_name_format[(1738 - 852) * 16] = temp; + for (count = 0; count < 32; count++) + { + large_file_name_format[(1732 - 852) * 16 + count] = temp_array[count]; + } + + /* Access the dir_entry we just modified by fx_unicode_name_get. */ + status = fx_unicode_name_get(&ram_disk, "NT-FILE~.TXTa", destination_name_1, &ulength); + return_value_if_fail(status == FX_NOT_FOUND, 21); + status = fx_unicode_name_get(&ram_disk, "NT-FILE~.TXT", destination_name_1, &ulength); + return_value_if_fail(status == FX_SUCCESS, 31); + status = fx_unicode_name_get(&ram_disk, "NT-FILE~.TX", destination_name_1, &ulength); + return_value_if_fail(status == FX_NOT_FOUND, 32); + + /* Show special performance in our implement. Althought we assign the first byte of some short name dir_entry as 0, fx_unicode_directory_search consider the long file name return by fx_unicode_directory_entry_read as short name so that we will succeed. */ + status = fx_unicode_name_get(&ram_disk, "aBcD~", destination_name_1, &ulength); + return_value_if_fail(status == FX_SUCCESS, 33); + +#ifndef FX_DISABLE_ERROR_CHECKING + /* Test unicode name checking. */ + length = fx_unicode_length_get(specified_unicode_file_name_2); + status = fx_unicode_file_create(&ram_disk, specified_unicode_file_name_2, length, (CHAR *)destination_name); + return_value_if_fail(status == FX_SUCCESS, 21); + length++; + status = fx_unicode_file_create(&ram_disk, specified_unicode_file_name_2, length, (CHAR *)destination_name); + return_value_if_fail(status == FX_INVALID_NAME, 22); + + length = fx_unicode_length_get(specified_unicode_file_name_2); + status = fx_unicode_directory_create(&ram_disk, specified_unicode_file_name_2, length, (CHAR *)destination_name); + return_value_if_fail(status == FX_ALREADY_CREATED, 23); + length++; + status = fx_unicode_file_create(&ram_disk, specified_unicode_file_name_2, length, (CHAR *)destination_name); + return_value_if_fail(status == FX_INVALID_NAME, 24); + + length = fx_unicode_length_get(specified_unicode_file_name_2); + status = fx_unicode_short_name_get(&ram_disk, specified_unicode_file_name_2, length, (CHAR *)destination_name); + return_value_if_fail(status == FX_SUCCESS, 25); + length++; + status = fx_unicode_short_name_get(&ram_disk, specified_unicode_file_name_2, length, (CHAR *)destination_name); + return_value_if_fail(status == FX_INVALID_NAME, 26); + + status = fx_unicode_short_name_get_extended(&ram_disk, specified_unicode_file_name_2, length, (CHAR *)destination_name, sizeof(destination_name)); + return_value_if_fail(status == FX_INVALID_NAME, 26); + + /* Test invalid old file name */ + old_length = fx_unicode_length_get(specified_unicode_file_name_2); + old_length++; + new_length = fx_unicode_length_get(specified_unicode_file_name_1); + status = fx_unicode_file_rename(&ram_disk, specified_unicode_file_name_2, old_length, specified_unicode_file_name_1, new_length, (CHAR *)destination_name); + return_value_if_fail(status == FX_INVALID_NAME, 27); + status = fx_unicode_directory_rename(&ram_disk, specified_unicode_file_name_2, old_length, specified_unicode_file_name_1, new_length, (CHAR *)destination_name); + return_value_if_fail(status == FX_INVALID_NAME, 28); + + /* Test invalid new file name */ + old_length = fx_unicode_length_get(specified_unicode_file_name_2); + new_length = fx_unicode_length_get(specified_unicode_file_name_1); + new_length++; + status = fx_unicode_file_rename(&ram_disk, specified_unicode_file_name_2, old_length, specified_unicode_file_name_1, new_length, (CHAR *)destination_name); + return_value_if_fail(status == FX_INVALID_NAME, 29); + status = fx_unicode_directory_rename(&ram_disk, specified_unicode_file_name_2, old_length, specified_unicode_file_name_1, new_length, (CHAR *)destination_name); + return_value_if_fail(status == FX_INVALID_NAME, 30); + + old_length = fx_unicode_length_get(long_unicode_name1); + new_length = fx_unicode_length_get(specified_unicode_file_name_2); + status = fx_unicode_file_rename(&ram_disk, long_unicode_name1, old_length, specified_unicode_file_name_2, new_length, (CHAR *)destination_name); + return_value_if_fail(status == FX_NOT_FOUND, 34); + + old_length = fx_unicode_length_get(long_unicode_name1); + new_length = fx_unicode_length_get(specified_unicode_file_name_2); + status = fx_unicode_directory_rename(&ram_disk, long_unicode_name1, old_length, specified_unicode_file_name_2, new_length, (CHAR *)destination_name); + return_value_if_fail(status == FX_NOT_FOUND, 35); + +#endif + + status = fx_media_close(&ram_disk); + return_value_if_fail(status == FX_SUCCESS, 36); + + /* Open the ram_disk in fx_ram_dirver_test.c. */ + temp_array[0] = large_file_name_format[(1736 - 852) * 16 + 3]; + temp_array[1] = large_file_name_format[(1736 - 852) * 16 + 4]; + + /* Insert 00FF in a long file dir_entry. */ + large_file_name_format[(1736 - 852) * 16 + 3] = 0; + large_file_name_format[(1736 - 852) * 16 + 4] = 0xFF; + _fx_ram_driver_copy_default_format = 1; + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, 128); + return_value_if_fail(status == FX_SUCCESS, 37); + _fx_ram_driver_copy_default_format = 0; + + /* Revert all we done. */ + large_file_name_format[(1736 - 852) * 16 + 3] = temp_array[0]; + large_file_name_format[(1736 - 852) * 16 + 4] = temp_array[1]; + + /* Attempt to access our directory. */ + length = fx_unicode_length_get(specified_ascii_file_name); + status = fx_unicode_file_create(&ram_disk, specified_ascii_file_name, length, (CHAR *)destination_name); + return_value_if_fail(status == FX_SUCCESS, 38); + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + 128, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Backup the data which will be modified later. */ + temp_array[0] = large_file_name_format[(1734 - 852) * 16]; + temp_array[1] = large_file_name_format[(1734 - 852) * 16 + 11]; + + /* Modfy the first dir_entry's ordinary number as 0x40 to make number of lfns be zero. */ + large_file_name_format[(1734 - 852) * 16] = 0x40; + large_file_name_format[(1734 - 852) * 16 + 11] = (UCHAR)FX_LONG_NAME; + _fx_ram_driver_copy_default_format = 1; + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + _fx_ram_driver_copy_default_format = 0; + + /* Recover the disk. */ + large_file_name_format[(1734 - 852) * 16] = temp_array[0]; + large_file_name_format[(1734 - 852) * 16 + 11] = temp_array[1]; + + /* Access root directory. */ + status = fx_unicode_file_create(&ram_disk, specified_ascii_file_name, length, (CHAR *)destination_name); + return_if_fail( status == FX_SUCCESS); + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + 128, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Backup the data which will be modified later. */ + temp_array[0] = large_file_name_format[(1734 - 852) * 16]; + temp_array[1] = large_file_name_format[(1734 - 852) * 16 + 11]; + temp_array[2] = large_file_name_format[(1736 - 852) * 16]; + temp_array[3] = large_file_name_format[(1736 - 852) * 16 + 11]; + + /* Modfy the second dir_entry's ordinary number as 0x40 to make card number be zero. */ + large_file_name_format[(1734 - 852) * 16] = 0x42; + large_file_name_format[(1734 - 852) * 16 + 11] = (UCHAR)FX_LONG_NAME; + large_file_name_format[(1736 - 852) * 16] = 0x40; + large_file_name_format[(1736 - 852) * 16 + 11] = (UCHAR)FX_LONG_NAME; + + /* Open the disk in fx_ram_driver_test.c */ + _fx_ram_driver_copy_default_format = 1; + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + _fx_ram_driver_copy_default_format = 0; + + /* Recover the disk. */ + large_file_name_format[(1734 - 852) * 16] = temp_array[0]; + large_file_name_format[(1734 - 852) * 16 + 11] = temp_array[1]; + large_file_name_format[(1736 - 852) * 16] = temp_array[2]; + large_file_name_format[(1736 - 852) * 16 + 11] = temp_array[3]; + + /* Access root directory. */ + status = fx_unicode_file_create(&ram_disk, specified_ascii_file_name, length, (CHAR *)destination_name); + return_if_fail( status == FX_SUCCESS); + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + 128, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_if_fail( status == FX_SUCCESS); + + /* Backup the data which will be modified later. */ + temp_array[0] = large_file_name_format[(1736 - 852) * 16]; + temp_array[1] = large_file_name_format[(1736 - 852) * 16 + 11]; + temp_array[2] = large_file_name_format[(1738 - 852) * 16]; + temp_array[3] = large_file_name_format[(1738 - 852) * 16 + 11]; + + /* Modfy the last two dir_entries in root directory, so we have to access the second sector of root directory. */ + large_file_name_format[(1736 - 852) * 16] = 0x43; + large_file_name_format[(1736 - 852) * 16 + 11] = (UCHAR)FX_LONG_NAME; + large_file_name_format[(1738 - 852) * 16] = 0x42; + large_file_name_format[(1738 - 852) * 16 + 11] = (UCHAR)FX_LONG_NAME; + + /* Open the disk in fx_ram_driver_test.c */ + _fx_ram_driver_copy_default_format = 1; + status += fx_media_open( &ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + _fx_ram_driver_copy_default_format = 0; + + /* Recover the disk. */ + large_file_name_format[(1734 - 852) * 16] = temp_array[0]; + large_file_name_format[(1734 - 852) * 16 + 11] = temp_array[1]; + large_file_name_format[(1736 - 852) * 16] = temp_array[2]; + large_file_name_format[(1736 - 852) * 16 + 11] = temp_array[3]; + + /* Access root directory, but set the size of root directory as zero to make a mistake. */ + ram_disk.fx_media_root_sectors = 0; + status = fx_unicode_file_create(&ram_disk, specified_ascii_file_name, length, (CHAR *)destination_name); + return_if_fail( status == FX_FILE_CORRUPT); + + status = fx_media_close( &ram_disk); + return_if_fail( status == FX_SUCCESS); + + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + 128, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open( &ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + status = fx_file_create( &ram_disk, "src"); + status += fx_file_open( &ram_disk, &my_file, "src", FX_OPEN_FOR_WRITE); + status += fx_file_write( &my_file, "hello", 5); + return_if_fail( status == FX_SUCCESS); + + /* IO error in fx_utility_FAT_map_flush.c while calling fx_utility_logical_sector_read. */ + _fx_utility_logical_sector_read_error_request = 2; + status = fx_media_close( &ram_disk); + + printf("SUCCESS!\n"); + test_control_return(0); +} diff --git a/test/regression_test/filex_unicode_directory_entry_2_test.c b/test/regression_test/filex_unicode_directory_entry_2_test.c new file mode 100644 index 0000000..b5db892 --- /dev/null +++ b/test/regression_test/filex_unicode_directory_entry_2_test.c @@ -0,0 +1,144 @@ +/* Test the condition of fat entry broken. */ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include +#include "fx_ram_driver_test.h" + +#define DEMO_STACK_SIZE 8192 +#define CACHE_SIZE 16*128 + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + +/* Define thread prototypes. */ + +void filex_unicode_directory_entry_2_test_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_unicode_directory_entry_2_test_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UCHAR destination_name[100] = {0}; +UCHAR destination_name2[100] = {0}; +UCHAR long_unicode_name[] = {2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 0, 0}; +ULONG length = 14; +FX_LOCAL_PATH local_path; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Unicode directory entry 2 test........................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + /* To cover the branch, which read mulitple sectors in the same cluster. */ + 2, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Open the ram_disk. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *)destination_name); + + /* Chdir to the created directory and create a subdirectory. */ + status += fx_directory_local_path_set(&ram_disk, &local_path, (CHAR *)destination_name); + status += fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *)destination_name2); + + /* Fat entry destory. */ + ram_disk.fx_media_fat_cache[8].fx_fat_cache_entry_value = 0; + ram_disk.fx_media_fat_cache[8].fx_fat_cache_entry_dirty = 1; + + /* Close the media to flush buffer. Now we have a disk with a corrupt dir_entry. */ + status += fx_media_close(&ram_disk); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(19); + } + + status += fx_directory_local_path_set(&ram_disk, &local_path, (CHAR *)destination_name); + status += fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *) destination_name); + + if (status == FX_FILE_CORRUPT) + { + printf("ERROR!\n"); + test_control_return(19); + } + else + { + printf("SUCCESS!\n"); + test_control_return(0); + } +} diff --git a/test/regression_test/filex_unicode_directory_entry_change_test.c b/test/regression_test/filex_unicode_directory_entry_change_test.c new file mode 100644 index 0000000..bc549fb --- /dev/null +++ b/test/regression_test/filex_unicode_directory_entry_change_test.c @@ -0,0 +1,211 @@ +/*This test is determined to cover lines 315 327 352 390 in fx_unicode_directory_entry_change.c. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_ram_driver_test.h" +#include + +#define DEMO_STACK_SIZE 8192 +/* Set the cache size as the size of one sector causing frequently IO operation. */ +#define CACHE_SIZE 128 + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[256]; +#endif + +/* Define thread prototypes. */ + +void filex_unicode_directory_entry_change_test_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + +extern UINT _fx_unicode_directory_entry_change(FX_MEDIA *media_ptr, FX_DIR_ENTRY *entry_ptr, UCHAR *unicode_name, ULONG unicode_name_length); + +static UINT driver_called_counter = 0; +/* Create a terrible driver. */ +static void _fx_terrible_driver(FX_MEDIA *media_ptr) +{ + driver_called_counter++; + // printf("\n_fx_terrible_driver has been called %d times.", driver_called_counter); + if (driver_called_counter == 3) + { + media_ptr->fx_media_driver_status=FX_IO_ERROR; + return; + } + (*_fx_ram_driver)(media_ptr); +} + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_unicode_directory_entry_change_test_application_define(void *first_unused_memory) +#endif +{ +#ifndef FX_STANDALONE_ENABLE + UCHAR *pointer; + + /* Setup the working pointer. */ + pointer = (UCHAR *)first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); + +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + +static UCHAR long_unicode_name1[] = { 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 0, 0, 0, 0, 0,}; + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ +FX_DIR_ENTRY entry; /* set a point to the entry */ +FX_DIR_ENTRY *entry_ptr=&entry; +UINT status, length; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Unicode directory entry change test...................."); + + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + return_value_if_fail(status == FX_SUCCESS, 1); + + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_value_if_fail(status == FX_SUCCESS, 2); + + /* initiallize the entry */ + entry_ptr->fx_dir_entry_name = "Z12345"; + entry_ptr->fx_dir_entry_attributes = 16; + entry_ptr->fx_dir_entry_byte_offset = 0; + entry_ptr->fx_dir_entry_cluster = 3; + entry_ptr->fx_dir_entry_created_date = 18977; + entry_ptr->fx_dir_entry_created_time = 0; + entry_ptr->fx_dir_entry_created_time_ms = 0; + entry_ptr->fx_dir_entry_date = 18977; + entry_ptr->fx_dir_entry_file_size = 0; + entry_ptr->fx_dir_entry_last_accessed_date = 18977; + entry_ptr->fx_dir_entry_last_search_byte_offset = 0; + entry_ptr->fx_dir_entry_last_search_cluster = 0; + entry_ptr->fx_dir_entry_last_search_log_sector = 0; + entry_ptr->fx_dir_entry_last_search_relative_cluster = 0; + entry_ptr->fx_dir_entry_log_sector = 2189; + entry_ptr->fx_dir_entry_long_name_present = 1; + entry_ptr->fx_dir_entry_long_name_shorted = 0; + entry_ptr->fx_dir_entry_next_log_sector = 0; + entry_ptr->fx_dir_entry_reserved = 0; + entry_ptr->fx_dir_entry_time = 0; + + /*This case is determined to cover lines 390 in fx_unicode_directory_entry_change.c. */ + length = fx_unicode_length_get(long_unicode_name1); + status=_fx_unicode_directory_entry_change(&ram_disk, entry_ptr, long_unicode_name1, length); + return_value_if_fail(status == FX_FILE_CORRUPT, 3); + + /*This case is determined to cover lines 327 in fx_unicode_directory_entry_change.c. */ + *(ram_disk.fx_media_driver_buffer) = 90; + status = _fx_unicode_directory_entry_change(&ram_disk, entry_ptr, long_unicode_name1, length); + return_value_if_fail(status == FX_FILE_CORRUPT, 4); + + /*This case is determined to cover lines 352 in fx_unicode_directory_entry_change.c. */ + entry_ptr->fx_dir_entry_byte_offset = CACHE_SIZE + 1; + *(ram_disk.fx_media_driver_buffer+ CACHE_SIZE+1) = 90; + + /*set the the logical sector that is in root directory */ + entry_ptr->fx_dir_entry_log_sector =ram_disk.fx_media_data_sector_start-1; + status = _fx_unicode_directory_entry_change(&ram_disk, entry_ptr, long_unicode_name1, length); + return_value_if_fail(status == FX_FILE_CORRUPT, 5); + + /*This case is determined to cover lines 315 in fx_unicode_directory_entry_change.c. */ + entry_ptr->fx_dir_entry_byte_offset = CACHE_SIZE + 1; + *(ram_disk.fx_media_driver_buffer + CACHE_SIZE + 1) = 90; + entry_ptr->fx_dir_entry_log_sector = ram_disk.fx_media_data_sector_start ; + ram_disk.fx_media_fat_cache[8].fx_fat_cache_entry_cluster = 20; + + /* Register our terrible dirver to make IO ERROR at a particular time. */ + ram_disk.fx_media_driver_entry = _fx_terrible_driver; + status = _fx_unicode_directory_entry_change(&ram_disk, entry_ptr, long_unicode_name1, length); + /* Unregister our terrible driver. */ + ram_disk.fx_media_driver_entry = _fx_ram_driver; + return_value_if_fail(status == FX_IO_ERROR, 6); + + /* initiallize the entry */ + entry_ptr->fx_dir_entry_name = "Z12345"; + entry_ptr->fx_dir_entry_attributes = 16; + entry_ptr->fx_dir_entry_byte_offset = 0; + entry_ptr->fx_dir_entry_cluster = 3; + entry_ptr->fx_dir_entry_created_date = 18977; + entry_ptr->fx_dir_entry_created_time = 0; + entry_ptr->fx_dir_entry_created_time_ms = 0; + entry_ptr->fx_dir_entry_date = 18977; + entry_ptr->fx_dir_entry_file_size = 0; + entry_ptr->fx_dir_entry_last_accessed_date = 18977; + entry_ptr->fx_dir_entry_last_search_byte_offset = 0; + entry_ptr->fx_dir_entry_last_search_cluster = 0; + entry_ptr->fx_dir_entry_last_search_log_sector = 0; + entry_ptr->fx_dir_entry_last_search_relative_cluster = 0; + entry_ptr->fx_dir_entry_log_sector = 2189; + entry_ptr->fx_dir_entry_long_name_present = 1; + entry_ptr->fx_dir_entry_long_name_shorted = 0; + entry_ptr->fx_dir_entry_next_log_sector = 0; + entry_ptr->fx_dir_entry_reserved = 0; + entry_ptr->fx_dir_entry_time = 0; + + /* Covered the last branch at Line 323 in fx_unicode_directory_entry_change.c. */ + *(ram_disk.fx_media_driver_buffer) = 90; + /* Register a wrong information in fat cache. */ + ram_disk.fx_media_fat_cache[8].fx_fat_cache_entry_cluster = 2; + ram_disk.fx_media_fat_cache[8].fx_fat_cache_entry_value = 0; + status = _fx_unicode_directory_entry_change(&ram_disk, entry_ptr, long_unicode_name1, length); + + printf("SUCCESS!\n"); + test_control_return(0); +} diff --git a/test/regression_test/filex_unicode_directory_entry_test.c b/test/regression_test/filex_unicode_directory_entry_test.c new file mode 100644 index 0000000..d6acf9b --- /dev/null +++ b/test/regression_test/filex_unicode_directory_entry_test.c @@ -0,0 +1,352 @@ +/* This test is determined to cover lines in fx_unicode_directory_entry_read.c. */ +/* We need a dir_entry whose first byte, ordinal number byte, is big enoutgh to exceed the the */ +/* limit of FX_MAX_LONG_NAME_LEN, so we created a disk with a corrupt dir_entry. */ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#include "tx_thread.h" +#endif +#include "fx_api.h" +#include "fx_ram_driver_test.h" +#include "fx_utility.h" +#include "fx_unicode.h" +#include + +#define DEMO_STACK_SIZE 8192 +#define CACHE_SIZE 16*128 + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + +/* Define thread prototypes. */ + +void filex_unicode_directory_entry_test_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + +/* Create a terrible driver. */ +static UINT driver_called_counter = 0; +static void _fx_terrible_driver(FX_MEDIA *media_ptr) +{ + driver_called_counter++; +// printf("\n_fx_terrible_driver has been called %d times.", driver_called_counter); + if ( + (driver_called_counter <= 3) + ) + { + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + return; + } + + (* _fx_ram_driver)(media_ptr); +} +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_unicode_directory_entry_test_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UCHAR destination_name[100] = {0}; +UCHAR buffer[512]; +UCHAR long_unicode_name[] = {2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 0, 0}; +ULONG length = 14; +ULONG dir_num1, dir_num2; +FX_LOCAL_PATH local_path; +FX_DIR_ENTRY source_dir, destination_dir; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Unicode directory entry test..........................."); + + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + return_value_if_fail( status == FX_SUCCESS, 2); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_value_if_fail( status == FX_SUCCESS, 3); + + /* For coverage test in fx_directory_free_search.c. + 387 : 4380 : status = _fx_directory_entry_write(media_ptr, entry_ptr); + 388 [ - + ]: 4380 : if(status != FX_SUCCESS) + 389 : : { + 390 : 0 : return(status); + 391 : : } + */ + _fx_directory_entry_write_error_request = 1; + + /* call fx_unicode_directory_create -call-> _fx_unicode_directory_search -call-> _fx_unicode_directory_entry_read */ + status = fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *) destination_name); + return_value_if_fail( status == FX_IO_ERROR, 3); + + /* Close the media to flush buffer. Now we have a disk with a corrupt dir_entry. */ + status = fx_media_close(&ram_disk); + return_value_if_fail( status == FX_SUCCESS, 4); + + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + return_value_if_fail( status == FX_SUCCESS, 2); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_value_if_fail( status == FX_SUCCESS, 3); + + /* Disable write protect */ + ram_disk.fx_media_driver_write_protect = FX_FALSE; + + /* call fx_unicode_directory_create -call-> _fx_unicode_directory_search -call-> _fx_unicode_directory_entry_read */ + status = fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *) destination_name); + return_value_if_fail( status == FX_SUCCESS, 3); + + /* After creating first directory on a new disk, the first dir_entry must be located at the head of the disk memory buffer. */ + /* Modify the first byte, ordinal number byte, to make a mistake. */ + *ram_disk.fx_media_memory_buffer |= 0x1f; + + /* Close the media to flush buffer. Now we have a disk with a corrupt dir_entry. */ + status = fx_media_close(&ram_disk); + return_value_if_fail( status == FX_SUCCESS, 4); + + /* Open the media and try to create the directory again to see what happened. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_value_if_fail( status == FX_SUCCESS, 5); + +#if !defined(FX_ENABLE_FAULT_TOLERANT) && !defined(FX_DISABLE_CACHE) + /* Try to recreate the same directory to see what happened. */ + status = fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *) destination_name); + return_value_if_fail( status == FX_SUCCESS, 6); +#endif + + /* At this point the three dir_entry belongs to original directory is invalid(new dir_entry's byteoffset in memory buffer is 96 bytes). */ + + /* Close the media to flush buffer. Now we have a disk with a corrupt dir_entry. */ + status = fx_media_close(&ram_disk); + return_value_if_fail( status == FX_SUCCESS, 7); + + /* This time, we'll modify the second dir_entry's first byte, ordinal byte, which will make the filesystem keep reading entries until FX_FILE_CORRUPT. */ + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Open the ram_disk. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_value_if_fail( status == FX_SUCCESS, 8); + + /* Disable write protect */ + ram_disk.fx_media_driver_write_protect = FX_FALSE; + + /* call fx_unicode_directory_create -call-> _fx_unicode_directory_search -call-> _fx_unicode_directory_entry_read */ + status = fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *) destination_name); + return_value_if_fail( status == FX_SUCCESS, 9); + + /* Modify second dir_entry this time to make a mistake. */ + ram_disk.fx_media_memory_buffer[32] |= 0x1f; + + /* Close the media to flush buffer. Now we have a disk with a corrupt dir_entry. */ + status = fx_media_close(&ram_disk); + return_value_if_fail( status == FX_SUCCESS, 10); + + /* Open the media and try to create the directory again to see what happened. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_value_if_fail( status == FX_SUCCESS, 11); + +#if !defined(FX_ENABLE_FAULT_TOLERANT) && !defined(FX_DISABLE_CACHE) + /* Try to recreate the same directory to see what happened. */ + status = fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *) destination_name); + + /* Since there are no appropriate long name dir_entry behind, the filesystem will read data continously untill logic sector overflow. */ + return_value_if_fail( status == FX_FILE_CORRUPT, 12); +#endif + + /* Add terrible driver to make IO mistake. */ + status = fx_media_close(&ram_disk); + return_value_if_fail( status == FX_SUCCESS, 13); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + return_value_if_fail( status == FX_SUCCESS, 14); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_value_if_fail( status == FX_SUCCESS, 15); + + /* Register our terrible driver. */ + ram_disk.fx_media_driver_entry = _fx_terrible_driver; + status = fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *) destination_name); + return_value_if_fail( status == FX_IO_ERROR, 16); + + /* Unregister our terrible driver. */ + ram_disk.fx_media_driver_entry = _fx_ram_driver; + + status = fx_media_abort( &ram_disk); + return_value_if_fail( status == FX_SUCCESS, 17); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + 128, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Open the ram_disk. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_value_if_fail( status == FX_SUCCESS, 18); + + /* Set local path at a sub_directory. */ + length = fx_unicode_length_get( long_unicode_name); + status = fx_unicode_directory_create( &ram_disk, long_unicode_name, length, (CHAR *)destination_name); +#ifndef FX_STANDALONE_ENABLE + status += fx_directory_local_path_set(&ram_disk, &local_path, (CHAR *)destination_name); +#else + status += fx_directory_default_set(&ram_disk, (CHAR *)destination_name); +#endif + return_value_if_fail( status == FX_SUCCESS, 19); + + /* Call _fx_unicode_directory_entry_read with uncorrect initial information. */ + destination_dir.fx_dir_entry_name = (CHAR *)buffer; + source_dir.fx_dir_entry_last_search_cluster = 1; + source_dir.fx_dir_entry_last_search_relative_cluster = 1; + dir_num1 = 0; + status = _fx_unicode_directory_entry_read( &ram_disk, &source_dir, &dir_num1, &destination_dir, destination_name, &dir_num2); + + /* Call _fx_unicode_directory_entry_read with uncorrect initial information. */ + source_dir.fx_dir_entry_last_search_cluster = 1; + source_dir.fx_dir_entry_last_search_relative_cluster = 0; + source_dir.fx_dir_entry_last_search_log_sector = 1; + source_dir.fx_dir_entry_log_sector = 0; + status = _fx_unicode_directory_entry_read( &ram_disk, &source_dir, &dir_num1, &destination_dir, destination_name, &dir_num2); + + /* Call _fx_unicode_directory_entry_read with uncorrect initial information. */ + source_dir.fx_dir_entry_last_search_cluster = 1; + source_dir.fx_dir_entry_last_search_relative_cluster = 0; + source_dir.fx_dir_entry_last_search_byte_offset = 0x20; + source_dir.fx_dir_entry_byte_offset = 0; + source_dir.fx_dir_entry_last_search_log_sector = 0; + source_dir.fx_dir_entry_log_sector = 0; + status = _fx_unicode_directory_entry_read( &ram_disk, &source_dir, &dir_num1, &destination_dir, destination_name, &dir_num2); + + /* Attempt to cover the branch at Line 140 in fx_utility_FAT_map_flush.c. */ + for (UINT i = 0; i < 128; i++) + ram_disk.fx_media_fat_secondary_update_map[i] = 0xff; + _fx_utility_FAT_map_flush( &ram_disk); + + printf("SUCCESS!\n"); + test_control_return(0); +} diff --git a/test/regression_test/filex_unicode_directory_rename_test.c b/test/regression_test/filex_unicode_directory_rename_test.c new file mode 100644 index 0000000..1048f15 --- /dev/null +++ b/test/regression_test/filex_unicode_directory_rename_test.c @@ -0,0 +1,444 @@ +/* This FileX test concentrates on the directory rename operations. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_ram_driver_test.h" +#include + +#define DEMO_STACK_SIZE 8192 +#define CACHE_SIZE 16*128 + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + +static UCHAR old_directory_name[] = {3, 0, 4, 0, 5, 0, 6, 0, 0, 0}; +static UCHAR new_directory_name1[] = {3, 0, 4, 0, 5, 0, 6, 0, 0, 0}; +static UCHAR new_directory_name2[] = {3, 0, 4, 0, 5, 0, 0, 0}; +static UCHAR new_directory_name3[] = {3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 0, 0}; +static UCHAR new_directory_name4[] = {2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 0, 0}; +static UCHAR new_file_name5[] = {'a', 0, 'b', 0, 'c', 0, 0, 0}; +static UCHAR new_file_name5_bak[] = {'a', 0, 'b', 0, 'c', 0, 0, 0}; +static UCHAR new_file_name6[] = {'a', 0, 'y', 0, 'c', 0, 0, 0}; +static UCHAR new_file_name7[] = {'a', 1, 'z', 0, 'c', 0, 0, 0}; +static UCHAR new_file_name8[] = {'z' + 1, 0, 'z', 0, 'c', 0, 'd', 0, 0, 0}; +static UCHAR new_file_name9[] = {'z' + 1, 1, 'z', 0, 'c', 0, 'd', 0, 0, 0}; +static UCHAR destination_name[100]; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +void filex_unicode_directory_rename_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_unicode_directory_rename_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT i; +UINT status, count; +ULONG length; +ULONG old_length; +ULONG new_length; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Unicode Directory rename test.........................."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Attempt to rename a directory while the media is not open */ + status = fx_unicode_directory_rename(&ram_disk, (UCHAR *)"name", 1, (UCHAR *)"name", 1, (CHAR *) destination_name); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(2); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(3); + } + + /* Attempt to rename a directory that does not exist */ + status = fx_unicode_directory_rename(&ram_disk, (UCHAR *)"does_not_exist", 1, (UCHAR *)"does_not_exist", 1, (CHAR *) destination_name); + if (status == FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(4); + } + + /* Create the unicode directory name to create the same unicode file names in the sub directory. */ + length = fx_unicode_length_get(old_directory_name); + status = fx_unicode_directory_create(&ram_disk, old_directory_name, length, (CHAR *) destination_name); + + /* Check for expected error. */ + if (status) + { + + /* Error creating unicode file names. Return to caller. */ + printf("ERROR!\n"); + test_control_return(5); + } + +/* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + /* Send null pointer to generate an error. */ + new_length = 0; + old_length = 0; + status = fx_unicode_directory_rename(FX_NULL, old_directory_name, old_length, new_directory_name1, new_length, (CHAR *) destination_name); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(6); + } +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Rename the old directory name to new_directory_name1. */ + old_length = fx_unicode_length_get(old_directory_name); + new_length = fx_unicode_length_get(new_directory_name1); + status = fx_unicode_directory_rename(&ram_disk, old_directory_name, old_length, new_directory_name1, new_length, (CHAR *) destination_name); + + /* Check for expected error. */ + if (status) + { + + /* Error creating unicode file names. Return to caller. */ + printf("ERROR!\n"); + test_control_return(7); + } + + /* Rename the new_directory_name1 to new_directory_name2. */ + old_length = fx_unicode_length_get(new_directory_name1); + new_length = fx_unicode_length_get(new_directory_name2); + status = fx_unicode_directory_rename(&ram_disk, new_directory_name1, old_length, new_directory_name2, new_length, (CHAR *) destination_name); + + /* Check for expected error. */ + if (status) + { + + /* Error creating unicode file names. Return to caller. */ + printf("ERROR!\n"); + test_control_return(8); + } + + /* Rename the new_directory_name2 to new_directory_name3. */ + old_length = fx_unicode_length_get(new_directory_name2); + new_length = fx_unicode_length_get(new_directory_name3); + status = fx_unicode_directory_rename(&ram_disk, new_directory_name2, old_length, new_directory_name3, new_length, (CHAR *) destination_name); + + /* Check for expected error. */ + if (status) + { + + /* Error creating unicode file names. Return to caller. */ + printf("ERROR!\n"); + test_control_return(9); + } + + /* Rename the new_directory_name3 to new_directory_name4. */ + old_length = fx_unicode_length_get(new_directory_name3); + new_length = fx_unicode_length_get(new_directory_name4); + status = fx_unicode_directory_rename(&ram_disk, new_directory_name3, old_length, new_directory_name4, new_length, (CHAR *) destination_name); + + /* Check for expected error. */ + if (status) + { + + /* Error creating unicode file names. Return to caller. */ + printf("ERROR!\n"); + test_control_return(10); + } + + /* Attempt to create the old_directory_name again. */ + length = fx_unicode_length_get(old_directory_name); + status = fx_unicode_directory_create(&ram_disk, old_directory_name, length, (CHAR *) destination_name); + + /* Check for expected error. */ + if (status) + { + + /* Error creating unicode directory name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(11); + } + + /* Attempt to create the new_directory_name1 that same as old_directory_name again. */ + length = fx_unicode_length_get(new_directory_name1); + status = fx_unicode_directory_create(&ram_disk, new_directory_name1, length, (CHAR *) destination_name); + + /* Check for expected error. */ + if (status != FX_ALREADY_CREATED) + { + + /* Error creating unicode directory name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(12); + } + + + /* Attempt to create the new_directory_name2 again. */ + length = fx_unicode_length_get(new_directory_name2); + status = fx_unicode_directory_create(&ram_disk, new_directory_name2, length, (CHAR *) destination_name); + + /* Check for expected error. */ + if (status) + { + + /* Error creating unicode directory name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(13); + } + + /* Attempt to create the new_directory_name1 again. */ + length = fx_unicode_length_get(new_directory_name3); + status = fx_unicode_directory_create(&ram_disk, new_directory_name3, length, (CHAR *) destination_name); + + /* Check for expected error. */ + if (status) + { + + /* Error creating unicode directory name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(14); + } + + /* Attempt to create the new_directory_name4 again. */ + length = fx_unicode_length_get(new_directory_name4); + status = fx_unicode_directory_create(&ram_disk, new_directory_name4, length, (CHAR *) destination_name); + + /* Check for expected error. */ + if (status != FX_ALREADY_CREATED) + { + + /* Error creating unicode directory name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(15); + } + + /* Create a directory whose name is consists of lowcase characters. */ + length = fx_unicode_length_get(new_file_name5); + status = fx_unicode_directory_create(&ram_disk, new_file_name5, length, (CHAR *)destination_name); + + /* Check for expected error. */ + return_value_if_fail( status == FX_SUCCESS, 16); + + /* Rename as the same directory name. */ + length = fx_unicode_length_get(new_file_name5); + status = fx_unicode_directory_rename(&ram_disk, new_file_name5, length, new_file_name5, length, (CHAR *)destination_name); + + /* Check for expected error. */ + return_value_if_fail( status == FX_SUCCESS, 17); + + length = fx_unicode_length_get(new_file_name6); + status = fx_unicode_directory_create(&ram_disk, new_file_name6, length, (CHAR *)destination_name); + + /* Check for expected error. */ + return_value_if_fail( status == FX_SUCCESS, 18); + + /* Attempt to rename new_file_name6 as existed new_file_name5 */ + old_length = fx_unicode_length_get(new_file_name6); + new_length = fx_unicode_length_get(new_file_name5); + status = fx_unicode_directory_rename(&ram_disk, new_file_name6, old_length, new_file_name5, new_length, (CHAR *)destination_name); + + /* Check for expected error. */ + return_value_if_fail( status == FX_ALREADY_CREATED, 19); + + /* Attempt to rename new_file_name6 as new_file_name7 */ + old_length = fx_unicode_length_get(new_file_name6); + new_length = fx_unicode_length_get(new_file_name7); + status = fx_unicode_directory_rename(&ram_disk, new_file_name6, old_length, new_file_name7, new_length, (CHAR *)destination_name); + + /* Check for expected error. */ + return_value_if_fail( status == FX_SUCCESS, 20); + + /* Attempt to rename new_file_name5 as exised new_file_name7 */ + old_length = fx_unicode_length_get(new_file_name5); + new_length = fx_unicode_length_get(new_file_name7); + status = fx_unicode_directory_rename(&ram_disk, new_file_name5, old_length, new_file_name7, new_length, (CHAR *)destination_name); + + /* Check for expected error. */ + return_value_if_fail( status == FX_ALREADY_CREATED, 21); + + /* Attempt to rename new_file_name7 as itself. */ + old_length = fx_unicode_length_get(new_file_name7); + new_length = fx_unicode_length_get(new_file_name7); + status = fx_unicode_directory_rename(&ram_disk, new_file_name7, old_length, new_file_name7, new_length, (CHAR *)destination_name); + + /* Check for expected error. */ + return_value_if_fail( status == FX_SUCCESS, 22); + + /* Created two directories whose name's first byte is 'z'+1. */ + length = fx_unicode_length_get(new_file_name8); + status = fx_unicode_directory_create(&ram_disk, new_file_name8, length, (CHAR *)destination_name); + length = fx_unicode_length_get(new_file_name9); + status += fx_unicode_directory_create(&ram_disk, new_file_name9, length, (CHAR *)destination_name); + return_value_if_fail( status == FX_SUCCESS, 23); + + /* Attempt to rename new_file_name9 as existed new_file_name8 */ + old_length = fx_unicode_length_get(new_file_name9); + new_length = fx_unicode_length_get(new_file_name8); + status = fx_unicode_directory_rename(&ram_disk, new_file_name9, old_length, new_file_name8, new_length, (CHAR *)destination_name); + return_value_if_fail( status == FX_ALREADY_CREATED, 24); + + /* Attempt to rename new_file_name9 as existed new_file_name7 */ + old_length = fx_unicode_length_get(new_file_name9); + new_length = fx_unicode_length_get(new_file_name7); + status = fx_unicode_directory_rename(&ram_disk, new_file_name9, old_length, new_file_name7, new_length, (CHAR *)destination_name); + return_value_if_fail( status == FX_ALREADY_CREATED, 25); + + /* Attempt to create a lot of directories with similar names to overflow. */ + length = fx_unicode_length_get(new_file_name5); + for ( count = 0; count < 27; count++) + { + new_file_name5[0]++; + status = fx_unicode_directory_create(&ram_disk, new_file_name5, length, (CHAR *)destination_name); + return_value_if_fail( (status == FX_SUCCESS) || (status == FX_ALREADY_CREATED) || (status == FX_NO_MORE_SPACE), 24 + count); + } + old_length = fx_unicode_length_get(new_file_name5_bak); + new_length = fx_unicode_length_get(new_file_name5); + status = fx_unicode_directory_rename(&ram_disk, new_file_name5_bak, old_length, new_file_name5, new_length, (CHAR *)destination_name); + return_value_if_fail( (status == FX_NO_MORE_SPACE) || ( status == FX_NOT_FOUND), 53); + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + return_value_if_fail( status == FX_SUCCESS, 51); + } + + printf("SUCCESS!\n"); + test_control_return(0); +} diff --git a/test/regression_test/filex_unicode_fat_entry_1_test.c b/test/regression_test/filex_unicode_fat_entry_1_test.c new file mode 100644 index 0000000..e69e47a --- /dev/null +++ b/test/regression_test/filex_unicode_fat_entry_1_test.c @@ -0,0 +1,141 @@ +/* Test the condition of fat entry broken. */ +#include "fx_api.h" +#include "fx_ram_driver_test.h" +#include "fx_utility.h" +#include + +#define DEMO_STACK_SIZE 8192 +#define CACHE_SIZE 16*128 + +#ifndef FX_STANDALONE_ENABLE + +/* Define the ThreadX and FileX object control blocks... */ + +static TX_THREAD ftest_0; +static FX_MEDIA ram_disk; + +/* Define the counters used in the test application... */ + +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; + +/* Define thread prototypes. */ + +void filex_unicode_fat_entry_1_test_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_unicode_fat_entry_1_test_application_define(void *first_unused_memory) +#endif +{ + +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + + /* Initialize the FileX system. */ + fx_system_initialize(); +} + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UCHAR destination_name[100] = {0}; +UCHAR destination_name2[100] = {0}; +UCHAR long_unicode_name[] = {2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 0, 0}; +ULONG length = 14; +FX_LOCAL_PATH local_path; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Unicode fat entry 1 test..............................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Open the ram_disk. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *)destination_name); + + /* Chdir to the created directory and create a subdirectory. */ + status += fx_directory_local_path_set(&ram_disk, &local_path, (CHAR *)destination_name); + status += fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *)destination_name2); + return_value_if_fail( status == FX_SUCCESS, 1); + + /* Fat entry destory. */ + ram_disk.fx_media_fat_cache[8].fx_fat_cache_entry_value = 0xffff; + ram_disk.fx_media_fat_cache[8].fx_fat_cache_entry_dirty = 1; + + /* Close the media to flush buffer. Now we have a disk with a corrupt dir_entry. */ + status = fx_media_close(&ram_disk); + + /* Open the ram_disk. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_value_if_fail( status == FX_SUCCESS, 2); + + /* Attempt to access our broken directory. */ + status = fx_directory_local_path_set(&ram_disk, &local_path, (CHAR *)destination_name); + return_value_if_fail( status == FX_INVALID_PATH, 3); + status = fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *) destination_name); + return_value_if_fail( status == FX_FILE_CORRUPT, 4); + + printf("SUCCESS!\n"); + test_control_return(0); +} + +#else + +extern void test_control_return(UINT status); +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_enable_4_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Unicode fat entry 1 test...............................N/A\n"); + + test_control_return(255); +} +#endif diff --git a/test/regression_test/filex_unicode_fat_entry_2_test.c b/test/regression_test/filex_unicode_fat_entry_2_test.c new file mode 100644 index 0000000..b2f259c --- /dev/null +++ b/test/regression_test/filex_unicode_fat_entry_2_test.c @@ -0,0 +1,154 @@ +/* This test is determined to cover lines in fx_unicode_directory_entry_read.c. */ +/* We need a dir_entry whose first byte, ordinal number byte, is big enoutgh to exceed the the */ +/* limit of FX_MAX_LONG_NAME_LEN, so we created a disk with a corrupt dir_entry. */ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include +#include +#include "fx_ram_driver_test.h" + +#define DEMO_STACK_SIZE 8192 +#define CACHE_SIZE 16*128 + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + +/* Define thread prototypes. */ + +void filex_unicode_fat_entry_2_test_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_unicode_fat_entry_2_test_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UCHAR destination_name[100] = {0}; +UCHAR destination_name2[100] = {0}; +UCHAR long_unicode_name[] = {2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 0, 0}; +ULONG length = 14; +FX_LOCAL_PATH local_path; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Unicode fat entry 2 test..............................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Open the ram_disk. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *)destination_name); + /* Chdir to the created directory and create a subdirectory. */ +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, (CHAR *)destination_name); +#else + status = fx_directory_default_set(&ram_disk, (CHAR *)destination_name); +#endif + + status += fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *)destination_name2); + + /* Fat entry destory. */ + ram_disk.fx_media_fat_cache[8].fx_fat_cache_entry_value = 0x1ff; + ram_disk.fx_media_fat_cache[8].fx_fat_cache_entry_dirty = 1; + + /* Close the media to flush buffer. Now we have a disk with a corrupt dir_entry. */ + status += fx_media_close(&ram_disk); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + if (status != FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(19); + } + +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, (CHAR *)destination_name); +#else + status = fx_directory_default_set(&ram_disk, (CHAR *)destination_name); +#endif + status += fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *)destination_name); + + if (status == FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(19); + } + else + { + printf("SUCCESS!\n"); + test_control_return(0); + } +} diff --git a/test/regression_test/filex_unicode_fat_entry_3_test.c b/test/regression_test/filex_unicode_fat_entry_3_test.c new file mode 100644 index 0000000..746cd84 --- /dev/null +++ b/test/regression_test/filex_unicode_fat_entry_3_test.c @@ -0,0 +1,233 @@ +/* Test the condition of fat entry broken. */ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_ram_driver_test.h" +#include + +#define DEMO_STACK_SIZE 8192 +#define CACHE_SIZE 16*128 + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + +/* Define thread prototypes. */ + +void filex_unicode_fat_entry_3_test_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_unicode_fat_entry_3_test_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UCHAR destination_name[100] = {0}; +UCHAR destination_name1[20] = {0}; +UCHAR destination_name2[20] = {0}; +UCHAR destination_name3[20] = {0}; +UCHAR destination_name4[20] = {0}; +UCHAR destination_name5[20] = {0}; +UCHAR destination_name6[20] = {0}; +UCHAR destination_name7[20] = {0}; +UCHAR destination_name8[20] = {0}; +UCHAR long_unicode_name[] = {2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 0, 0}; +ULONG length = 14; +UCHAR fat_buffer[128] = {0}; +FX_LOCAL_PATH local_path; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Unicode fat entry 3 test..............................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 7000, // Total sectors - FAT16 + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Create a directory and many subdirectories to build fat chain. */ + status = fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *)destination_name); +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, (CHAR *)destination_name); +#else + status = fx_directory_default_set(&ram_disk, (CHAR *)destination_name); +#endif + + long_unicode_name[0]++; + status += fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *)destination_name1); + long_unicode_name[0]++; + status += fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *)destination_name2); + long_unicode_name[0]++; + status += fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *)destination_name3); + long_unicode_name[0]++; + status += fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *)destination_name4); + long_unicode_name[0]++; + status += fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *)destination_name5); + long_unicode_name[0]++; + status += fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *)destination_name6); + return_if_fail( status == FX_SUCCESS); + + /* Flush everything out. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (int i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 1, (VOID *) fat_buffer); + + /* Add a FAT entry randomly in the FAT table. */ + fat_buffer[6] = 0x32; + fat_buffer[7] = 0x00; + + /* Write the FAT corruption out. */ + status += fx_media_write(&ram_disk, 1, (VOID *) fat_buffer); + return_if_fail( status == FX_SUCCESS); + + long_unicode_name[0]++; + status = fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *)destination_name7); + return_if_fail( status == FX_FAT_READ_ERROR); + + /* Flush everything out. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (int i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 1, (VOID *) fat_buffer); + + /* Add a FAT entry randomly in the FAT table. */ + fat_buffer[6] = 0x03; + fat_buffer[7] = 0x00; + + /* Write the FAT corruption out. */ + status += fx_media_write(&ram_disk, 1, (VOID *) fat_buffer); + status += fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *)destination_name7); + return_if_fail( status == FX_FAT_READ_ERROR); + + /* Flush everything out. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (int i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 1, (VOID *) fat_buffer); + + /* Recovery FAT chain. */ + fat_buffer[6] = 0x06; + fat_buffer[7] = 0x00; + + /* Write the FAT corruption out. */ + status += fx_media_write(&ram_disk, 1, (VOID *) fat_buffer); + status += fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *)destination_name7); + return_if_fail( status == FX_SUCCESS); + + /* Flush everything out. */ + fx_media_flush(&ram_disk); + _fx_utility_FAT_flush(&ram_disk); + _fx_utility_logical_sector_flush(&ram_disk, 1, 60000, FX_TRUE); + for (int i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + + /* Read the first FAT sector. */ + status = fx_media_read(&ram_disk, 1, (VOID *) fat_buffer); + + /* Make FAT chain a circle. */ + fat_buffer[12] = 0x03; + fat_buffer[13] = 0x00; + + /* Write the FAT corruption out. */ + status += fx_media_write(&ram_disk, 1, (VOID *) fat_buffer); + status += fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *)destination_name8); + return_if_fail( status == FX_FAT_READ_ERROR); + + printf("SUCCESS!\n"); + test_control_return(0); +} diff --git a/test/regression_test/filex_unicode_fat_entry_test.c b/test/regression_test/filex_unicode_fat_entry_test.c new file mode 100644 index 0000000..aa30447 --- /dev/null +++ b/test/regression_test/filex_unicode_fat_entry_test.c @@ -0,0 +1,139 @@ +/* Test the condition of fat entry broken. */ +#include "fx_api.h" +#include "fx_ram_driver_test.h" +#include + +#define DEMO_STACK_SIZE 8192 +#define CACHE_SIZE 16*128 + +#ifndef FX_STANDALONE_ENABLE + +/* Define the ThreadX and FileX object control blocks... */ + +static TX_THREAD ftest_0; +static FX_MEDIA ram_disk; + +/* Define the counters used in the test application... */ + +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; + +/* Define thread prototypes. */ + +void filex_unicode_fat_entry_test_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_unicode_fat_entry_test_application_define(void *first_unused_memory) +#endif +{ + +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + + /* Initialize the FileX system. */ + fx_system_initialize(); +} + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UCHAR destination_name[100] = {0}; +UCHAR destination_name2[100] = {0}; +UCHAR long_unicode_name[] = {2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 0, 0}; +ULONG length = 14; +FX_LOCAL_PATH local_path; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Unicode fat entry test................................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Open the ram_disk. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + status += fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *) destination_name); + + /* Chdir to the created directory and create a subdirectory. */ + status += fx_directory_local_path_set(&ram_disk, &local_path, (CHAR *)destination_name); + status += fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *) destination_name2); + + /* Fat entry destory. */ + ram_disk.fx_media_fat_cache[8].fx_fat_cache_entry_value = 0; + ram_disk.fx_media_fat_cache[8].fx_fat_cache_entry_dirty = 1; + + /* Close the media to flush buffer. Now we have a disk with a corrupt dir_entry. */ + status += fx_media_close(&ram_disk); + + /* Open the ram_disk. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_value_if_fail( status == FX_SUCCESS, 2); + + /* Attempt to access our broken directory. */ + status = fx_directory_local_path_set(&ram_disk, &local_path, (CHAR *)destination_name); + return_value_if_fail( status == FX_INVALID_PATH, 3); + status = fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *) destination_name); + return_value_if_fail( status == FX_LOST_CLUSTER_ERROR, 4); + + printf("SUCCESS!\n"); + test_control_return(0); +} + +#else + +extern void test_control_return(UINT status); +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_fault_tolerant_enable_4_test_application_define(void *first_unused_memory) +#endif +{ + + FX_PARAMETER_NOT_USED(first_unused_memory); + + /* Print out some test information banners. */ + printf("FileX Test: Unicode fat entry test.................................N/A\n"); + + test_control_return(255); +} +#endif diff --git a/test/regression_test/filex_unicode_file_directory_rename_extra_2_test.c b/test/regression_test/filex_unicode_file_directory_rename_extra_2_test.c new file mode 100644 index 0000000..3d337ee --- /dev/null +++ b/test/regression_test/filex_unicode_file_directory_rename_extra_2_test.c @@ -0,0 +1,530 @@ +/* Test the condition of fat entry broken. */ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_ram_driver_test.h" +#include + +#define DEMO_STACK_SIZE 8192 +/* Set the cache size as the size of one sector causing frequently IO operation. */ +#define CACHE_SIZE 128 + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + +/* Define thread prototypes. */ + +void filex_unicode_file_directory_rename_extra_2_test_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_unicode_file_directory_rename_extra_2_test_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + +static UCHAR new_file_name[] = {'a', 0, 'b', 0, 'c', 0, 0, 0}; +static UCHAR long_unicode_name1[] = {1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 0, 0}; +static UCHAR long_unicode_name2[] = {2, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 0, 0}; +static UCHAR destination_name[100]; + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status, length, new_length, old_length; +UCHAR temp; +FX_LOCAL_PATH local_path; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Unicode file directory rename extra 2 test............."); + + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + length = fx_unicode_length_get(new_file_name); + status = fx_unicode_file_create( &ram_disk, new_file_name, length, (CHAR *)destination_name); + return_if_fail( status == FX_SUCCESS); + +#if !defined(FX_ENABLE_FAULT_TOLERANT) && !defined(FX_DISABLE_CACHE) + /* Make an IO error when fx_unicode_file_create call _fx_directory_search. */ + length = fx_unicode_length_get(long_unicode_name1); + _fx_ram_driver_io_error_request = 10; + status = fx_unicode_file_create( &ram_disk, long_unicode_name1, length, (CHAR *)destination_name); + return_if_fail( status == FX_IO_ERROR); +#endif + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + length = fx_unicode_length_get(new_file_name); + status = fx_unicode_file_create( &ram_disk, new_file_name, length, (CHAR *)destination_name); + return_if_fail( status == FX_SUCCESS); + +#if !defined(FX_ENABLE_FAULT_TOLERANT) && !defined(FX_DISABLE_CACHE) + /* Make an IO error when fx_unicode_file_create call _fx_unicode_directory_entry_change. */ + length = fx_unicode_length_get(long_unicode_name1); + _fx_ram_driver_io_error_request = 11; + status = fx_unicode_file_create( &ram_disk, long_unicode_name1, length, (CHAR *)destination_name); + return_if_fail( status == FX_IO_ERROR); +#endif + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + fx_media_abort( &ram_disk); + + /* Try fx_unicode_direcotry_create this time. */ + /* Format the media with FAT32. This needs to be done before opening it! */ + status += fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + length = fx_unicode_length_get(new_file_name); + status = fx_unicode_directory_create( &ram_disk, new_file_name, length, (CHAR *)destination_name); + return_if_fail( status == FX_SUCCESS); + + /* Make an IO error when fx_unicode_file_create call _fx_unicode_directory_entry_change. */ + length = fx_unicode_length_get(long_unicode_name1); + _fx_ram_driver_io_error_request = 14; + status = fx_unicode_directory_create( &ram_disk, long_unicode_name1, length, (CHAR *)destination_name); + return_if_fail( status == FX_IO_ERROR); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + fx_media_abort( &ram_disk); + + /* Try fx_unicode_file_rename this time. */ + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + length = fx_unicode_length_get(new_file_name); + status = fx_unicode_file_create( &ram_disk, new_file_name, length, (CHAR *)destination_name); + return_if_fail( status == FX_SUCCESS); + + length = fx_unicode_length_get(long_unicode_name1); + status = fx_unicode_file_create( &ram_disk, long_unicode_name1, length, (CHAR *)destination_name); + return_if_fail( status == FX_SUCCESS); + + /* Make an IO ERROR when fx_unicode_file_rename call _fx_unicode_directory_entry_change. */ + old_length = fx_unicode_length_get( long_unicode_name1); + new_length = fx_unicode_length_get( long_unicode_name2); + _fx_utility_logical_sector_read_error_request = 27; + status = fx_unicode_file_rename( &ram_disk, long_unicode_name1, old_length, long_unicode_name2, new_length, (CHAR *)destination_name); + return_if_fail( status == FX_IO_ERROR); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + fx_media_abort( &ram_disk); + + /* Try fx_unicode_file_rename this time. */ + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + length = fx_unicode_length_get(new_file_name); + status = fx_unicode_file_create( &ram_disk, new_file_name, length, (CHAR *)destination_name); + return_if_fail( status == FX_SUCCESS); + + length = fx_unicode_length_get(long_unicode_name1); + status = fx_unicode_file_create( &ram_disk, long_unicode_name1, length, (CHAR *)destination_name); + return_if_fail( status == FX_SUCCESS); + + /* Make an IO ERROR when fx_unicode_file_rename call _fx_directory_search. */ + old_length = fx_unicode_length_get( long_unicode_name1); + new_length = fx_unicode_length_get( long_unicode_name2); + _fx_utility_logical_sector_read_error_request = 32; + status = fx_unicode_file_rename( &ram_disk, long_unicode_name1, old_length, long_unicode_name2, new_length, (CHAR *)destination_name); + return_if_fail( status == FX_IO_ERROR); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + fx_media_abort( &ram_disk); + + /* Try fx_unicode_directory_rename this time. */ + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + length = fx_unicode_length_get(new_file_name); + status = fx_unicode_directory_create( &ram_disk, new_file_name, length, (CHAR *)destination_name); + return_if_fail( status == FX_SUCCESS); + + length = fx_unicode_length_get(long_unicode_name1); + status = fx_unicode_directory_create( &ram_disk, long_unicode_name1, length, (CHAR *)destination_name); + return_if_fail( status == FX_SUCCESS); + + /* Make an IO ERROR when fx_unicode_file_rename call _fx_directory_search. */ + old_length = fx_unicode_length_get( long_unicode_name1); + new_length = fx_unicode_length_get( long_unicode_name2); + _fx_utility_logical_sector_read_error_request = 27; + status = fx_unicode_directory_rename( &ram_disk, long_unicode_name1, old_length, long_unicode_name2, new_length, (CHAR *)destination_name); + return_if_fail( status == FX_IO_ERROR); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + fx_media_abort( &ram_disk); + + /* Try fx_unicode_directory_rename this time. */ + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + length = fx_unicode_length_get(new_file_name); + status = fx_unicode_directory_create( &ram_disk, new_file_name, length, (CHAR *)destination_name); + return_if_fail( status == FX_SUCCESS); + + length = fx_unicode_length_get(long_unicode_name1); + status = fx_unicode_directory_create( &ram_disk, long_unicode_name1, length, (CHAR *)destination_name); + return_if_fail( status == FX_SUCCESS); + + /* Make an IO ERROR when fx_unicode_file_rename call _fx_unicode_directory_entry_change. */ + old_length = fx_unicode_length_get( long_unicode_name1); + new_length = fx_unicode_length_get( long_unicode_name2); + _fx_utility_logical_sector_read_error_request = 32; + status = fx_unicode_directory_rename( &ram_disk, long_unicode_name1, old_length, long_unicode_name2, new_length, (CHAR *)destination_name); + return_if_fail( status == FX_IO_ERROR); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + fx_media_abort( &ram_disk); + + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + /* Make an IO error when fx_unicode_directory_rename call _fx_directory_search. */ + _fx_ram_driver_io_error_request = 1; + length = fx_unicode_length_get(new_file_name); + status = fx_unicode_directory_create( &ram_disk, new_file_name, length, (CHAR *)destination_name); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + fx_media_abort( &ram_disk); + + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + length = fx_unicode_length_get(new_file_name); + status = fx_unicode_directory_create( &ram_disk, new_file_name, length, (CHAR *)destination_name); + return_if_fail( status == FX_SUCCESS); + + length = fx_unicode_length_get(long_unicode_name1); + status = fx_unicode_directory_create( &ram_disk, long_unicode_name1, length, (CHAR *)destination_name); + return_if_fail( status == FX_SUCCESS); + + /* Flush everything out. */ + fx_media_flush( &ram_disk); + _fx_utility_FAT_flush( &ram_disk); + _fx_utility_logical_sector_flush( &ram_disk, 1, 60000, FX_TRUE); + for (UINT i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 0; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 0; + } + +#ifndef FX_ENABLE_FAULT_TOLERANT + + /* Make an IO ERROR in fx_unicode_directory_entry_read while calling _fx_utility_FAT_entry_read. */ + old_length = fx_unicode_length_get( long_unicode_name1); + new_length = fx_unicode_length_get( long_unicode_name2); + _fx_ram_driver_io_error_request = 2; + status = fx_unicode_directory_rename( &ram_disk, long_unicode_name1, old_length, long_unicode_name2, new_length, (CHAR *)destination_name); + return_if_fail( status == FX_IO_ERROR); + +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + fx_media_abort( &ram_disk); + + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + 0, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + length = fx_unicode_length_get(new_file_name); + status = fx_unicode_directory_create( &ram_disk, new_file_name, length, (CHAR *)destination_name); + return_if_fail( status == FX_SUCCESS); + +#ifndef FX_ENABLE_FAULT_TOLERANT + + /* Make an IO error in _fx_unicode_directory_entry_change while calling _fx_utility_logical_sector_read at Line 358. */ + _fx_utility_logical_sector_read_error_request = 24; + length = fx_unicode_length_get(long_unicode_name1); + status = fx_unicode_directory_create( &ram_disk, long_unicode_name1, length, (CHAR *)destination_name); + return_if_fail( status == FX_IO_ERROR); + +#endif + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + fx_media_abort( &ram_disk); + + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + 0, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000, // Total sectors s + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + + length = fx_unicode_length_get(new_file_name); + status = fx_unicode_directory_create( &ram_disk, new_file_name, length, (CHAR *)destination_name); +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, (CHAR *)destination_name); +#else + status = fx_directory_default_set(&ram_disk, (CHAR *)destination_name); +#endif + return_if_fail( status == FX_SUCCESS); + + /* Create enough files to enforce cache flush. */ + temp = long_unicode_name1[0]; + for (UINT i = 0; i < 76; i++) + { + long_unicode_name1[0]++; + length = fx_unicode_length_get(long_unicode_name1); + status = fx_unicode_directory_create( &ram_disk, long_unicode_name1, length, (CHAR *)destination_name); + } + return_if_fail( status == FX_SUCCESS); + + /* fx_unicode_directory_create will determine whether the file to be created is already existed by calling fx_unicode_directory_search. */ + /* The search will fail because of IO_ERROR rather than FILE_ALREADY_CREATED, but the creation of file will still succeeded, since we recover the driver asap. :) */ + long_unicode_name1[0]++; + length = fx_unicode_length_get(long_unicode_name1); + _fx_ram_driver_io_error_request = 22; + status = fx_unicode_directory_create( &ram_disk, long_unicode_name1, length, (CHAR *)destination_name); + long_unicode_name1[0] = temp; + return_if_fail( status == FX_SUCCESS); + + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + printf("SUCCESS!\n"); + test_control_return(0); +} diff --git a/test/regression_test/filex_unicode_file_directory_rename_extra_test.c b/test/regression_test/filex_unicode_file_directory_rename_extra_test.c new file mode 100644 index 0000000..0c25cbe --- /dev/null +++ b/test/regression_test/filex_unicode_file_directory_rename_extra_test.c @@ -0,0 +1,223 @@ +/* Test the condition of fat entry broken. */ +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_utility.h" +#include "fx_ram_driver_test.h" +#include + +#define DEMO_STACK_SIZE 8192 +#define CACHE_SIZE 16*128 + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + +/* Define thread prototypes. */ + +void filex_unicode_file_directory_rename_extra_test_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_unicode_file_directory_rename_extra_test_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + +static UCHAR new_file_name5[] = {'a', 0, 'b', 0, 'c', 0, 0, 0}; +static UCHAR new_file_name5_bak[] = {'a', 0, 'b', 0, 'c', 0, 0, 0}; +static UCHAR new_file_name6[] = {'1', 0, 'b', 0, 'c', 0, 'd', 0, 0, 0}; +static UCHAR destination_name[100]; + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status, length, count, new_length, old_length; +UCHAR temp; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Unicode file directory rename extra test..............."); + + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + return_value_if_fail( status == FX_SUCCESS, 1); + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_value_if_fail( status == FX_SUCCESS, 2); + + length = fx_unicode_length_get(new_file_name5); + status = fx_unicode_file_create( &ram_disk, new_file_name5, length, (CHAR *)destination_name); + return_value_if_fail( status == FX_SUCCESS, 3); + + temp = new_file_name5[0]; + + /* Attempt to create more than 26 files with unicode names with the same length. */ + for ( count = 0; count < 28; count++) + { + new_file_name5[0]++; + length = fx_unicode_length_get(new_file_name5); + status = fx_unicode_file_create( &ram_disk, new_file_name5, length, (CHAR *)destination_name); + if ( count <=24) + { + + /* Succeed while less than 26 files is created. */ + return_value_if_fail( status == FX_SUCCESS, 4 + count); + } + else + { + + /* Exceeded the limitation. */ + return_value_if_fail( status == FX_ALREADY_CREATED, 4 + count); + } + } + + length = fx_unicode_length_get(new_file_name6); + status = fx_unicode_file_create( &ram_disk, new_file_name6, length, (CHAR *)destination_name); + return_value_if_fail( status == FX_SUCCESS, 32); + + /* Touch the limitation by renaming. */ + new_length = fx_unicode_length_get(new_file_name5); + old_length = fx_unicode_length_get(new_file_name6); + status = fx_unicode_file_rename( &ram_disk, new_file_name6, old_length, new_file_name5, new_length, (CHAR *)destination_name); + return_value_if_fail( status == FX_ALREADY_CREATED, 33); + + /* Renaming a file with the same length is premitted. */ + new_length = fx_unicode_length_get(new_file_name5); + old_length = fx_unicode_length_get(new_file_name5_bak); + status = fx_unicode_file_rename( &ram_disk, new_file_name5_bak, old_length, new_file_name5, new_length, (CHAR *)destination_name); + return_value_if_fail( status == FX_SUCCESS, 34); + + /* Recover new_file_name5 for the test for renaming directory. */ + new_file_name5[0] = temp; + + /* Abort the disk to reuse memory. */ + fx_media_abort( &ram_disk); + + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + return_value_if_fail( status == FX_SUCCESS, 1); + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + return_value_if_fail( status == FX_SUCCESS, 2); + + length = fx_unicode_length_get(new_file_name5); + status = fx_unicode_directory_create( &ram_disk, new_file_name5, length, (CHAR *)destination_name); + return_value_if_fail( status == FX_SUCCESS, 3); + + temp = new_file_name5[0]; + + /* Attempt to create more than 26 files with unicode names with the same length. */ + for ( count = 0; count < 28; count++) + { + new_file_name5[0]++; + length = fx_unicode_length_get(new_file_name5); + status = fx_unicode_directory_create( &ram_disk, new_file_name5, length, (CHAR *)destination_name); + if ( count <=24) + { + + /* Succeed while less than 26 files is created. */ + return_value_if_fail( status == FX_SUCCESS, 4 + count); + } + else + { + + /* Exceeded the limitation. */ + return_value_if_fail( status == FX_ALREADY_CREATED, 4 + count); + } + } + + length = fx_unicode_length_get(new_file_name6); + status = fx_unicode_directory_create( &ram_disk, new_file_name6, length, (CHAR *)destination_name); + return_value_if_fail( status == FX_SUCCESS, 32); + + /* Touch the limitation by renaming. */ + new_length = fx_unicode_length_get(new_file_name5); + old_length = fx_unicode_length_get(new_file_name6); + status = fx_unicode_directory_rename( &ram_disk, new_file_name6, old_length, new_file_name5, new_length, (CHAR *)destination_name); + return_value_if_fail( status == FX_ALREADY_CREATED, 33); + + /* Renaming a file with the same length is premitted. */ + new_length = fx_unicode_length_get(new_file_name5); + old_length = fx_unicode_length_get(new_file_name5_bak); + status = fx_unicode_directory_rename( &ram_disk, new_file_name5_bak, old_length, new_file_name5, new_length, (CHAR *)destination_name); + return_value_if_fail( status == FX_SUCCESS, 34); + printf("SUCCESS!\n"); + test_control_return(0); +} diff --git a/test/regression_test/filex_unicode_file_rename_test.c b/test/regression_test/filex_unicode_file_rename_test.c new file mode 100644 index 0000000..313cd15 --- /dev/null +++ b/test/regression_test/filex_unicode_file_rename_test.c @@ -0,0 +1,442 @@ +/* This FileX test concentrates on the unicode file rename operations. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_ram_driver_test.h" +#include + +#define DEMO_STACK_SIZE 8192 +#define CACHE_SIZE 16*128 + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif +/* Notice that old_file_name is the same as new_file_name1. */ +static UCHAR old_file_name[] = {3, 0, 4, 0, 5, 0, 6, 0, 0, 0}; +static UCHAR new_file_name1[] = {3, 0, 4, 0, 5, 0, 6, 0, 0, 0}; +static UCHAR new_file_name2[] = {3, 0, 4, 0, 5, 0, 0, 0}; +static UCHAR new_file_name3[] = {3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 0, 0}; +static UCHAR new_file_name4[] = {2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 0, 0}; +static UCHAR new_file_name5[] = {'a', 0, 'b', 0, 'c', 0, 0, 0}; +static UCHAR new_file_name5_bak[] = {'a', 0, 'b', 0, 'c', 0, 0, 0}; +static UCHAR new_file_name6[] = {'a', 0, 'y', 0, 'c', 0, 0, 0}; +static UCHAR new_file_name7[] = {'a', 1, 'z', 0, 'c', 0, 0, 0}; +static UCHAR new_file_name8[] = {'z' + 1, 0, 'z', 0, 'c', 0, 'd', 0, 0, 0}; +static UCHAR new_file_name9[] = {'z' + 1, 1, 'z', 0, 'c', 0, 'd', 0, 0, 0}; +static UCHAR destination_name[100]; + +#define TEST_COUNT 3 + +/* Define thread prototypes. */ + +void filex_unicode_file_rename_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_unicode_file_rename_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT i; +UINT status, count; +ULONG length; +ULONG old_length; +ULONG new_length; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Unicode File Rename Test..............................."); + + /* Loop to test FAT 12, 16, 32. */ + for (i = 0; i < TEST_COUNT; i ++) + { + if (i == 0) + { + /* Format the media with FAT12. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 256, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 1) + { + /* Format the media with FAT16. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 4200 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + else if (i == 2) + { + /* Format the media with FAT32. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory_large, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 70000 * 8, // Total sectors + 256, // Sector size + 8, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + } + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* Attempt to rename a file before the media has been opened */ + status = fx_unicode_file_rename(&ram_disk, (UCHAR *)"name", 1, (UCHAR *)"name", 1, (CHAR *) destination_name); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(2); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory_large, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(3); + } + + /* Attempt to rename a file that does not exist */ + status = fx_unicode_file_rename(&ram_disk, (UCHAR *)"does_not_exist", 1, (UCHAR *)"does_not_exist", 1, (CHAR *) destination_name); + if (status == FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(4); + } + + /* Create the unicode file name to create the same unicode file names in the sub directory. */ + length = fx_unicode_length_get(old_file_name); + status = fx_unicode_file_create(&ram_disk, old_file_name, length, (CHAR *) destination_name); + + /* Check for expected error. */ + if (status) + { + + /* Error creating unicode file names. Return to caller. */ + printf("ERROR!\n"); + test_control_return(5); + } + + /* Rename the old file name to new_file_name1. */ + old_length = fx_unicode_length_get(old_file_name); + new_length = fx_unicode_length_get(new_file_name1); + status = fx_unicode_file_rename(&ram_disk, old_file_name, old_length, new_file_name1, new_length, (CHAR *) destination_name); + + /* Check for expected error. */ + if (status) + { + + /* Error creating unicode file names. Return to caller. */ + printf("ERROR!\n"); + test_control_return(6); + } + + /* Rename the new_file_name1 to new_file_name2. */ + old_length = fx_unicode_length_get(new_file_name1); + new_length = fx_unicode_length_get(new_file_name2); + status = fx_unicode_file_rename(&ram_disk, new_file_name1, old_length, new_file_name2, new_length, (CHAR *) destination_name); + + /* Check for expected error. */ + if (status) + { + + /* Error creating unicode file names. Return to caller. */ + printf("ERROR!\n"); + test_control_return(7); + } + + /* Rename the new_file_name2 to new_file_name3. */ + old_length = fx_unicode_length_get(new_file_name2); + new_length = fx_unicode_length_get(new_file_name3); + status = fx_unicode_file_rename(&ram_disk, new_file_name2, old_length, new_file_name3, new_length, (CHAR *) destination_name); + + /* Check for expected error. */ + if (status) + { + + /* Error creating unicode file names. Return to caller. */ + printf("ERROR!\n"); + test_control_return(8); + } + + /* Rename the new_file_name3 to new_file_name4. */ + old_length = fx_unicode_length_get(new_file_name3); + new_length = fx_unicode_length_get(new_file_name4); + status = fx_unicode_file_rename(&ram_disk, new_file_name3, old_length, new_file_name4, new_length, (CHAR *) destination_name); + + /* Check for expected error. */ + if (status) + { + + /* Error creating unicode file names. Return to caller. */ + printf("ERROR!\n"); + test_control_return(9); + } + + /* Attempt to create the old_file_name again. */ + length = fx_unicode_length_get(old_file_name); + status = fx_unicode_file_create(&ram_disk, old_file_name, length, (CHAR *) destination_name); + + /* Check for expected error. */ + if (status) + { + + /* Error creating unicode file name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(10); + } + + /* Attempt to create the new_file_name1 same as old_file_name again. */ + length = fx_unicode_length_get(new_file_name1); + status = fx_unicode_file_create(&ram_disk, new_file_name1, length, (CHAR *) destination_name); + + /* Check for expected error. */ + if (status != FX_ALREADY_CREATED) + { + + /* Error creating unicode directory name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(11); + } + + + /* Attempt to create the new_file_name2 again. */ + length = fx_unicode_length_get(new_file_name2); + status = fx_unicode_file_create(&ram_disk, new_file_name2, length, (CHAR *) destination_name); + + /* Check for expected error. */ + if (status) + { + + /* Error creating unicode directory name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(12); + } + + /* Attempt to create the new_file_name1 again. */ + length = fx_unicode_length_get(new_file_name3); + status = fx_unicode_file_create(&ram_disk, new_file_name3, length, (CHAR *) destination_name); + + /* Check for expected error. */ + if (status) + { + + /* Error creating unicode directory name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(13); + } + + /* Attempt to create the new_file_name4 again. */ + length = fx_unicode_length_get(new_file_name4); + status = fx_unicode_file_create(&ram_disk, new_file_name4, length, (CHAR *) destination_name); + + /* Check for expected error. */ + if (status != FX_ALREADY_CREATED) + { + + /* Error creating unicode directory name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(14); + } + +#ifndef FX_DISABLE_ERROR_CHECKING + /* send a null pointer to generate an error */ + status = fx_unicode_file_rename(FX_NULL, old_file_name, old_length, new_file_name1, new_length, (CHAR *) destination_name); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(15); + } +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Create a file whose name is consists of lowcase characters. */ + length = fx_unicode_length_get(new_file_name5); + status = fx_unicode_file_create(&ram_disk, new_file_name5, length, (CHAR *)destination_name); + + /* Check for expected error. */ + return_value_if_fail( status == FX_SUCCESS, 16); + + /* Rename as the same file name. */ + length = fx_unicode_length_get(new_file_name5); + status = fx_unicode_file_rename(&ram_disk, new_file_name5, length, new_file_name5, length, (CHAR *)destination_name); + + /* Check for expected error. */ + return_value_if_fail( status == FX_SUCCESS, 17); + + length = fx_unicode_length_get(new_file_name6); + status = fx_unicode_file_create(&ram_disk, new_file_name6, length, (CHAR *)destination_name); + + /* Check for expected error. */ + return_value_if_fail( status == FX_SUCCESS, 18); + + /* Attempt to rename new_file_name6 as existed new_file_name5 */ + old_length = fx_unicode_length_get(new_file_name6); + new_length = fx_unicode_length_get(new_file_name5); + status = fx_unicode_file_rename(&ram_disk, new_file_name6, old_length, new_file_name5, new_length, (CHAR *)destination_name); + + /* Check for expected error. */ + return_value_if_fail( status == FX_ALREADY_CREATED, 19); + + /* Attempt to rename new_file_name6 as new_file_name7 */ + old_length = fx_unicode_length_get(new_file_name6); + new_length = fx_unicode_length_get(new_file_name7); + status = fx_unicode_file_rename(&ram_disk, new_file_name6, old_length, new_file_name7, new_length, (CHAR *)destination_name); + + /* Check for expected error. */ + return_value_if_fail( status == FX_SUCCESS, 20); + + /* Attempt to rename new_file_name5 as exised new_file_name7 */ + old_length = fx_unicode_length_get(new_file_name5); + new_length = fx_unicode_length_get(new_file_name7); + status = fx_unicode_file_rename(&ram_disk, new_file_name5, old_length, new_file_name7, new_length, (CHAR *)destination_name); + + /* Check for expected error. */ + return_value_if_fail( status == FX_ALREADY_CREATED, 21); + + /* Attempt to rename new_file_name7 as itself. */ + old_length = fx_unicode_length_get(new_file_name7); + new_length = fx_unicode_length_get(new_file_name7); + status = fx_unicode_file_rename(&ram_disk, new_file_name7, old_length, new_file_name7, new_length, (CHAR *)destination_name); + + /* Check for expected error. */ + return_value_if_fail( status == FX_SUCCESS, 22); + + /* Created two files whose name's first byte is 'z'+1. */ + length = fx_unicode_length_get(new_file_name8); + status = fx_unicode_file_create(&ram_disk, new_file_name8, length, (CHAR *)destination_name); + length = fx_unicode_length_get(new_file_name9); + status += fx_unicode_file_create(&ram_disk, new_file_name9, length, (CHAR *)destination_name); + return_value_if_fail( status == FX_SUCCESS, 23); + + /* Attempt to rename new_file_name9 as existed new_file_name8 */ + old_length = fx_unicode_length_get(new_file_name9); + new_length = fx_unicode_length_get(new_file_name8); + status = fx_unicode_file_rename(&ram_disk, new_file_name9, old_length, new_file_name8, new_length, (CHAR *)destination_name); + return_value_if_fail( status == FX_ALREADY_CREATED, 24); + + /* Attempt to rename new_file_name9 as existed new_file_name7 */ + old_length = fx_unicode_length_get(new_file_name9); + new_length = fx_unicode_length_get(new_file_name7); + status = fx_unicode_file_rename(&ram_disk, new_file_name9, old_length, new_file_name7, new_length, (CHAR *)destination_name); + return_value_if_fail( status == FX_ALREADY_CREATED, 25); + + /* Attempt to create a lot of directories with similar names to overflow. */ + length = fx_unicode_length_get(new_file_name5); + for ( count = 0; count < 27; count++) + { + new_file_name5[0]++; + status = fx_unicode_directory_create(&ram_disk, new_file_name5, length, (CHAR *)destination_name); + return_value_if_fail( (status == FX_SUCCESS) || (status == FX_ALREADY_CREATED) || (status == FX_NO_MORE_SPACE), 24 + count); + } + old_length = fx_unicode_length_get(new_file_name5_bak); + new_length = fx_unicode_length_get(new_file_name5); + status = fx_unicode_directory_rename(&ram_disk, new_file_name5_bak, old_length, new_file_name5, new_length, (CHAR *)destination_name); + return_value_if_fail( (status == FX_NOT_FOUND) || ( status == FX_NOT_DIRECTORY), 53); + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + return_value_if_fail( status == FX_SUCCESS, 51); + } + + printf("SUCCESS!\n"); + test_control_return(0); +} diff --git a/test/regression_test/filex_unicode_name_string_test.c b/test/regression_test/filex_unicode_name_string_test.c new file mode 100644 index 0000000..5c9100e --- /dev/null +++ b/test/regression_test/filex_unicode_name_string_test.c @@ -0,0 +1,175 @@ +/* This FileX test concentrates on the unicode operations. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_ram_driver_test.h" +#include + +#define DEMO_STACK_SIZE 8192 +#define CACHE_SIZE 16*128 + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +extern TX_THREAD *_tx_thread_current_ptr; +#endif +static FX_MEDIA ram_disk; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + +/* Define thread prototypes. */ + +void filex_unicode_name_string_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_unicode_name_string_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; + +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +UCHAR buffer[512]; +#ifndef FX_DISABLE_ERROR_CHECKING +UCHAR destination_name[300] = {0}; +UCHAR long_unicode_name[] = {2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 0, 0}; +#endif /* FX_DISABLE_ERROR_CHECKING */ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *path_ptr; +#else +UCHAR path_ptr[1024]; +#endif +FX_LOCAL_PATH local_path; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Unicode name string test..............................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Open the ram_disk. */ + status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_if_fail( status == FX_SUCCESS); + +#ifndef FX_DISABLE_ERROR_CHECKING + + /* Disable write protect */ + ram_disk.fx_media_driver_write_protect = FX_FALSE; + + /* lengthen the unicode name to include the last 0 */ + ULONG length = 15; + + /* Test creating directory. */ + status = fx_unicode_directory_create(&ram_disk, long_unicode_name, length, (CHAR *) destination_name); + return_if_fail( status == FX_INVALID_NAME); + + /* Test creating file. */ + status = fx_unicode_file_create(&ram_disk, long_unicode_name, length, (CHAR *) destination_name); + return_if_fail( status == FX_INVALID_NAME); + +#endif + + /* Create a directory named a...(254 times). */ + buffer[0] = '/'; + + for (UINT i = 1; i < 300; i++) + buffer[i] = 'a'; + + buffer[255] = 0; + + /* Chenged to the specified directory and ensure the string related to current position is end as zero. */ + status = fx_directory_create( &ram_disk, (CHAR *)buffer); +#ifndef FX_STANDALONE_ENABLE + status = fx_directory_local_path_set(&ram_disk, &local_path, (CHAR *)buffer); +#else + status = fx_directory_default_set(&ram_disk, (CHAR *)buffer); +#endif +#ifndef FX_STANDALONE_ENABLE + path_ptr = (UCHAR *)((FX_PATH *)_tx_thread_current_ptr -> tx_thread_filex_ptr) -> fx_path_string; +#endif + path_ptr[255] = 0; + return_if_fail( status == FX_SUCCESS); + + /* Specify last found name as the directory we just created. */ + buffer[255] = '/'; + buffer[256] = 0; + + for (UINT i = 0; i <= 256; i++) + ram_disk.fx_media_last_found_name[i] = (CHAR)buffer[i]; + + /* Access the directory and see what will happen. */ + status = fx_directory_create( &ram_disk, "a"); + return_if_fail( status == FX_SUCCESS); + + status = fx_media_close(&ram_disk); + return_if_fail( status == FX_SUCCESS); + + printf("SUCCESS!\n"); + test_control_return(0); +} diff --git a/test/regression_test/filex_unicode_test.c b/test/regression_test/filex_unicode_test.c new file mode 100644 index 0000000..94b1ec9 --- /dev/null +++ b/test/regression_test/filex_unicode_test.c @@ -0,0 +1,640 @@ +/* This FileX test concentrates on the unicode operations. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_utility.h" +#include +#include "fx_ram_driver_test.h" + +#define DEMO_STACK_SIZE 8192 +#define CACHE_SIZE 16*128 + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; + + +static UCHAR short_unicode_name[] = {1, 0, 0, 0}; +static UCHAR directory_name[] = {3, 0, 4, 0, 5, 0, 6, 0, 0, 0}; +static UCHAR long_unicode_name[] = {2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 0, 0}; +static UCHAR destination_name[100]; +static UCHAR does_not_exist[] = {1, 1, 1, 1}; + +static UCHAR unicode_temp_long_file_name[FX_MAX_LONG_NAME_LEN]; + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + +/* Define thread prototypes. */ + +void filex_unicode_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + +#ifndef FX_ENABLE_FAULT_TOLERANT +/* Calculate the times driver was called to raise error at a particular time. */ +static INT driver_called_counter = 0; + +/* Create a terrible driver. */ +static void _fx_terrible_driver(FX_MEDIA *media_ptr) +{ + driver_called_counter++; + + /* Make IO error to cover the branch at Line 247 in fx_unicode_file_create.c */ + if ( driver_called_counter != 61) + (* _fx_ram_driver)(media_ptr); + else + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + return; +} +#endif /* FX_ENABLE_FAULT_TOLERANT */ + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_unicode_application_define(void *first_unused_memory) +#endif +{ +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG length; +ULONG ul_temp; +UCHAR buffer[512]; +UINT i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Unicode test..........................................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 100, // Directory Entries + 0, // Hidden sectors + 512, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(1); + } + + /* try to do all the unicode commands before the media is opened to generate an error */ + length = 1; + + /* short name get */ + status = fx_unicode_short_name_get(&ram_disk, directory_name, length, (CHAR *) destination_name); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(2); + } + + /* Call extended version of short name get. */ + status = fx_unicode_short_name_get_extended(&ram_disk, directory_name, length, (CHAR*)destination_name, sizeof(destination_name)); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(2); + } + + /* name get */ + status = fx_unicode_name_get(&ram_disk, (CHAR *) destination_name, destination_name, &length); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(3); + } + + /* Call extended version of name get. */ + status = fx_unicode_name_get_extended(&ram_disk, (CHAR*)destination_name, destination_name, &length, sizeof(destination_name)); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(3); + } + + /* create */ + status = fx_unicode_directory_create(&ram_disk, directory_name, length, (CHAR *) destination_name); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(4); + } + + /* file create */ + status = fx_unicode_file_create(&ram_disk, short_unicode_name, length, (CHAR *) destination_name); + if (status != FX_MEDIA_NOT_OPEN) + { + printf("ERROR!\n"); + test_control_return(5); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(6); + } + + /* try to create a directory while the media is write protected */ + ram_disk.fx_media_driver_write_protect = FX_TRUE; + status = fx_unicode_directory_create(&ram_disk, directory_name, length, (CHAR *) destination_name); + if (status != FX_WRITE_PROTECT) + { + printf("ERROR!\n"); + test_control_return(7); + } + + /* try to create a file while the media is write protected */ + status = fx_unicode_file_create(&ram_disk, short_unicode_name, length, (CHAR *) destination_name); + if (status != FX_WRITE_PROTECT) + { + printf("ERROR!\n"); + test_control_return(8); + } + ram_disk.fx_media_driver_write_protect = FX_FALSE; + + /* only test this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + /* send a null pointer to generate an error */ + /* short name get */ + status = fx_unicode_short_name_get(FX_NULL, directory_name, length, (CHAR *) destination_name); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(9); + } + + /* name get */ + status = fx_unicode_name_get(FX_NULL, (CHAR *) destination_name, destination_name, &length); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(10); + } + + /* file create */ + status = fx_unicode_file_create(FX_NULL, short_unicode_name, length, (CHAR *) destination_name); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(11); + } +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Create the a short and long unicode file name. */ + length = fx_unicode_length_get(short_unicode_name); + status = fx_unicode_file_create(&ram_disk, short_unicode_name, length, (CHAR *) destination_name); + length = fx_unicode_length_get(long_unicode_name); + status += fx_unicode_file_create(&ram_disk, long_unicode_name, length, (CHAR *) destination_name); + status += fx_file_create(&ram_disk, "abcdefghijklmnop"); + status += fx_directory_short_name_get(&ram_disk, "abcdefghijklmnop", (CHAR *) destination_name); + status += fx_media_flush(&ram_disk); + + /* Check for erros. */ + if (status != FX_SUCCESS) + { + + /* Error creating unicode file names. Return to caller. */ + printf("ERROR!\n"); + test_control_return(13); + } + + /* Try to create the same name again - this should result in an error! */ + length = fx_unicode_length_get(long_unicode_name); + status += fx_unicode_file_create(&ram_disk, long_unicode_name, length, (CHAR *) destination_name); + + /* Check for expected error. */ + if (status != FX_ALREADY_CREATED) + { + + /* Error creating unicode file names. Return to caller. */ + printf("ERROR!\n"); + test_control_return(14); + } + + /* Try creating a unicode name twice... this should result in an error as well. */ + length = fx_unicode_length_get(short_unicode_name); + status = fx_unicode_file_create(&ram_disk, short_unicode_name, length, (CHAR *) destination_name); + + /* Check for expected error. */ + if (status != FX_ALREADY_CREATED) + { + + /* Error creating unicode file names. Return to caller. */ + printf("ERROR!\n"); + test_control_return(15); + } + + /* Only run this if error checking is enabled */ +#ifndef FX_DISABLE_ERROR_CHECKING + /* send null pointer to generate an error */ + status = fx_unicode_directory_create(FX_NULL, directory_name, length, (CHAR *) destination_name); + if (status != FX_PTR_ERROR) + { + printf("ERROR!\n"); + test_control_return(16); + } +#endif /* FX_DISABLE_ERROR_CHECKING */ + + /* Create the unicode directory name to create the same unicode file names in the sub directory. */ + length = fx_unicode_length_get(directory_name); + status = fx_unicode_directory_create(&ram_disk, directory_name, length, (CHAR *) destination_name); + status += fx_file_create(&ram_disk, "qrstuvwxyz"); + + /* Check for erros. */ + if (status != FX_SUCCESS) + { + + /* Error creating unicode directory. Return to caller. */ + printf("ERROR!\n"); + test_control_return(17); + } + + /* Attempt to create the unicode sub-directory again. */ + status = fx_unicode_directory_create(&ram_disk, directory_name, length, (CHAR *) destination_name); + + /* Check for expected error. */ + if (status != FX_ALREADY_CREATED) + { + + /* Error creating unicode directory name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(18); + } + + /* Ask for the name of something that does not exist to generate an error. */ + ul_temp = length; + status = fx_unicode_short_name_get(&ram_disk, does_not_exist, length, (CHAR *) destination_name); + if (status == FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(19); + } + status = fx_unicode_name_get(&ram_disk, (CHAR *) does_not_exist, destination_name, &length); + if (status == FX_SUCCESS) + { + printf("ERROR!\n"); + test_control_return(20); + } + length = ul_temp; + + /* Now, pickup the short name for the unicode directory so we can set the default path there and + do the same thing from a sub-directory. */ + status = fx_unicode_short_name_get(&ram_disk, directory_name, length, (CHAR *) destination_name); + status += fx_directory_default_set(&ram_disk, (CHAR *) destination_name); + length = fx_unicode_length_get(short_unicode_name); + status = fx_unicode_file_create(&ram_disk, short_unicode_name, length, (CHAR *) destination_name); + length = fx_unicode_length_get(long_unicode_name); + status += fx_unicode_file_create(&ram_disk, long_unicode_name, length, (CHAR *) destination_name); + status += fx_file_create(&ram_disk, "abcdefghijklmnop"); + length = fx_unicode_length_get(directory_name); + status += fx_unicode_directory_create(&ram_disk, directory_name, length, (CHAR *) destination_name); + status += fx_file_create(&ram_disk, "qrstuvwxyz"); + status += fx_media_flush(&ram_disk); + + /* Check for erros. */ + if (status != FX_SUCCESS) + { + + /* Error creating unicode file and directory names in a sub-directory. Return to caller. */ + printf("ERROR!\n"); + test_control_return(21); + } + + + /* Test the short/long name get routines with the short unicode name. */ + length = fx_unicode_length_get(short_unicode_name); + status += fx_unicode_short_name_get(&ram_disk, short_unicode_name, length, (CHAR *) destination_name); + status += fx_unicode_name_get(&ram_disk, (CHAR *) destination_name, destination_name, &length); + + /* Check for errors. */ + if ((status) || (length != fx_unicode_length_get(short_unicode_name))) + { + + /* Error getting unicode file and short names in a sub-directory. Return to caller. */ + printf("ERROR!\n"); + test_control_return(22); + } + + /* Test the short/long name get routines with the long unicode name. */ + length = fx_unicode_length_get(long_unicode_name); + status += fx_unicode_short_name_get(&ram_disk, long_unicode_name, length, (CHAR *) destination_name); + status += fx_unicode_name_get(&ram_disk, (CHAR *) destination_name, destination_name, &length); + + /* Check for errors. */ + if ((status) || (length != fx_unicode_length_get(long_unicode_name))) + { + + /* Error getting unicode file and short names in a sub-directory. Return to caller. */ + printf("ERROR!\n"); + test_control_return(23); + } + + /* Test the short/long name get routines with the directory unicode name. */ + length = fx_unicode_length_get(directory_name); + status += fx_unicode_short_name_get(&ram_disk, directory_name, length, (CHAR *) destination_name); + status += fx_unicode_name_get(&ram_disk, (CHAR *) destination_name, destination_name, &length); + + /* Check for errors. */ + if ((status) || (length != fx_unicode_length_get(directory_name))) + { + + /* Error getting unicode file and short names in a sub-directory. Return to caller. */ + printf("ERROR!\n"); + test_control_return(24); + } + + /* Now delete everything in the sub-directory. */ + status += fx_unicode_short_name_get(&ram_disk, directory_name, length, (CHAR *) destination_name); + status += fx_directory_delete(&ram_disk, (CHAR *) destination_name); + status += fx_file_delete(&ram_disk, "qrstuvwxyz"); + status += fx_file_delete(&ram_disk, "abcdefghijklmnop"); + length = fx_unicode_length_get(long_unicode_name); + status += fx_unicode_short_name_get(&ram_disk, long_unicode_name, length, (CHAR *) destination_name); + status += fx_file_delete(&ram_disk, (CHAR *) destination_name); + length = fx_unicode_length_get(short_unicode_name); + status += fx_unicode_short_name_get(&ram_disk, short_unicode_name, length, (CHAR *) destination_name); + status += fx_file_delete(&ram_disk, (CHAR *) destination_name); + + /* Check for erros. */ + if (status != FX_SUCCESS) + { + + /* Error deleting unicode file and directory names in a sub-directory. Return to caller. */ + printf("ERROR!\n"); + test_control_return(25); + } + + + /* Move the directory default back to the root. */ + status += fx_directory_default_set(&ram_disk, "/"); + + /* Test the short/long name get routines with the short unicode name. */ + length = fx_unicode_length_get(short_unicode_name); + status += fx_unicode_short_name_get(&ram_disk, short_unicode_name, length, (CHAR *) destination_name); + status += fx_unicode_name_get(&ram_disk, (CHAR *) destination_name, destination_name, &length); + + /* Check for errors. */ + if ((status) || (length != fx_unicode_length_get(short_unicode_name))) + { + + /* Error getting unicode file and short names in the root directory. Return to caller. */ + printf("ERROR!\n"); + test_control_return(26); + } + + /* Test the short/long name get routines with the long unicode name. */ + length = fx_unicode_length_get(long_unicode_name); + status += fx_unicode_short_name_get(&ram_disk, long_unicode_name, length, (CHAR *) destination_name); + status += fx_unicode_name_get(&ram_disk, (CHAR *) destination_name, destination_name, &length); + + /* Check for errors. */ + if ((status) || (length != fx_unicode_length_get(long_unicode_name))) + { + + /* Error getting unicode file and short names in the root directory. Return to caller. */ + printf("ERROR!\n"); + test_control_return(27); + } + + /* Test the short/long name get routines with the directory unicode name. */ + length = fx_unicode_length_get(directory_name); + status += fx_unicode_short_name_get(&ram_disk, directory_name, length, (CHAR *) destination_name); + status += fx_unicode_name_get(&ram_disk, (CHAR *) destination_name, destination_name, &length); + + /* Check for errors. */ + if ((status) || (length != fx_unicode_length_get(directory_name))) + { + + /* Error getting unicode file and short names in the root directory. Return to caller. */ + printf("ERROR!\n"); + test_control_return(28); + } + + /* Delete the root directory contents. */ + status += fx_unicode_short_name_get(&ram_disk, directory_name, length, (CHAR *) destination_name); + status += fx_directory_delete(&ram_disk, (CHAR *) destination_name); + status += fx_file_delete(&ram_disk, "qrstuvwxyz"); + status += fx_file_delete(&ram_disk, "abcdefghijklmnop"); + length = fx_unicode_length_get(long_unicode_name); + status += fx_unicode_short_name_get(&ram_disk, long_unicode_name, length, (CHAR *) destination_name); + status += fx_file_delete(&ram_disk, (CHAR *) destination_name); + length = fx_unicode_length_get(short_unicode_name); + status += fx_unicode_short_name_get(&ram_disk, short_unicode_name, length, (CHAR *) destination_name); + status += fx_file_delete(&ram_disk, (CHAR *) destination_name); + + /* Check for errors. */ + if (status != FX_SUCCESS) + { + + /* Error getting unicode file and short names in the root directory. Return to caller. */ + printf("ERROR!\n"); + test_control_return(29); + } + + length = fx_unicode_length_get(short_unicode_name); + status = fx_unicode_file_create(&ram_disk, short_unicode_name, length, (CHAR*)destination_name); + length = fx_unicode_length_get(long_unicode_name); + status += fx_unicode_file_create(&ram_disk, long_unicode_name, length, (CHAR*)destination_name); + + length = fx_unicode_length_get((UCHAR*)"Z\01\02\03\0\0"); + status += fx_file_create(&ram_disk, (CHAR*)"test"); + + /* Check for errors. */ + if (status != FX_SUCCESS) + { + + /* Error creating unicode files. Return to caller. */ + printf("ERROR!\n"); + test_control_return(29); + } + + length = fx_unicode_length_get(long_unicode_name); + status = fx_unicode_short_name_get_extended(&ram_disk, long_unicode_name, length, (CHAR*)destination_name, 14); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + /* Error getting short name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(29); + } + + /* Set unicode_temp_long_file_name and length to known state. */ + memcpy(unicode_temp_long_file_name, "ORIGINALSTRING", 15); + length = 0; + status = fx_unicode_name_get_extended(&ram_disk, (CHAR*)destination_name, (UCHAR*)unicode_temp_long_file_name, &length, 4); + + /* Check for error. */ + if ((status != FX_SUCCESS) || (length != 14) || (memcmp(unicode_temp_long_file_name, "\2\0\0\0INALSTRING\0", 15))) + { + + /* Error getting short name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(29); + } + + status = fx_unicode_short_name_get_extended(&ram_disk, (UCHAR*)"t\0e\0s\0t\0\0", 4, (CHAR*)destination_name, 14); + + /* Check for error. */ + if (status != FX_SUCCESS) + { + + /* Error getting short name. Return to caller. */ + printf("ERROR!\n"); + test_control_return(29); + } + + /* At the circumstance of the same lenth of unicode name,there are no more than 26 unicode names + with the first character ranging from 'a'to 'z'.If you create one more unicode name, an error will + occur.Now test this case */ + length = fx_unicode_length_get(long_unicode_name); + unicode_temp_long_file_name[0] = 'z'; + + for (i = 1; i < length; i++) + { + + /* Build temporary long file name. */ + unicode_temp_long_file_name[i] = (UCHAR)('0' + (i % 9)); + } + unicode_temp_long_file_name[i] = FX_NULL; + for (i = 1; i < 27; i++) + { + + /* creat the same lenth files but Them differ in the first character of file name. */ + status = fx_file_create(&ram_disk, (CHAR *)unicode_temp_long_file_name); + if (status) + break; + unicode_temp_long_file_name[0] = (UCHAR)('z' - i); + } + status = fx_unicode_file_create(&ram_disk, long_unicode_name, length, (CHAR *)destination_name); + if (status != FX_ALREADY_CREATED) + { + /* No spare name for unicode file names. Return to caller. */ + printf("ERROR!\n"); + test_control_return(30); + } + + /* Test the unicode length get that exceeds the maximum value. */ + for (i = 0; i < 512; i++) + { + buffer[i] = 'A'; + } + length = fx_unicode_length_get(buffer); + if (length != 128) + { + + printf("ERROR!\n"); + test_control_return(31); + } + + /* Now test the NULL bytes being in different locations. */ + buffer[0] = 0; + buffer[3] = 0; + buffer[8] = 0; + buffer[9] = 0; + + length = fx_unicode_length_get(buffer); + if (length != 4) + { + + printf("ERROR!\n"); + test_control_return(32); + } + + /* Close the media. */ + status = fx_media_close(&ram_disk); + return_value_if_fail( status == FX_SUCCESS, 33); + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + return_value_if_fail( status == FX_SUCCESS, 34); + +#ifndef FX_ENABLE_FAULT_TOLERANT + /* Register our terrible driver. */ + ram_disk.fx_media_driver_entry = _fx_terrible_driver; + short_unicode_name[0]++; + length = fx_unicode_length_get(short_unicode_name); + status = fx_unicode_file_create(&ram_disk, long_unicode_name, length, (CHAR *)destination_name); + ram_disk.fx_media_driver_entry = _fx_ram_driver; + return_value_if_fail( status == FX_IO_ERROR, 35); +#endif /* FX_ENABLE_FAULT_TOLERANT */ + + /* Close the media. */ + status = fx_media_close(&ram_disk); + + /* Determine if the test was successful. */ + return_value_if_fail( status == FX_SUCCESS, 36); + + printf("SUCCESS!\n"); + test_control_return(0); +} diff --git a/test/regression_test/filex_utility_fat_flush_test.c b/test/regression_test/filex_utility_fat_flush_test.c new file mode 100644 index 0000000..f35573f --- /dev/null +++ b/test/regression_test/filex_utility_fat_flush_test.c @@ -0,0 +1,430 @@ +/* This FileX test concentrates on the utility FAT flush operation. */ + +#ifndef FX_STANDALONE_ENABLE +#include "tx_api.h" +#endif +#include "fx_api.h" +#include "fx_utility.h" +#include +#include "fx_ram_driver_test.h" + +#define DEMO_STACK_SIZE 4096 +#define CACHE_SIZE 16*128 + + +/* Define the ThreadX and FileX object control blocks... */ + +#ifndef FX_STANDALONE_ENABLE +static TX_THREAD ftest_0; +#endif +static FX_MEDIA ram_disk; +static FX_FILE my_file; + + +/* Define the counters used in the test application... */ + +#ifndef FX_STANDALONE_ENABLE +static UCHAR *ram_disk_memory; +static UCHAR *cache_buffer; +#else +static UCHAR cache_buffer[CACHE_SIZE]; +#endif + + +/* Define thread prototypes. */ + +void filex_utility_fat_flush_application_define(void *first_unused_memory); +static void ftest_0_entry(ULONG thread_input); + +VOID _fx_ram_driver(FX_MEDIA *media_ptr); +void test_control_return(UINT status); + + + +/* Define what the initial system looks like. */ + +#ifdef CTEST +void test_application_define(void *first_unused_memory) +#else +void filex_utility_fat_flush_application_define(void *first_unused_memory) +#endif +{ + +#ifndef FX_STANDALONE_ENABLE +UCHAR *pointer; + + + /* Setup the working pointer. */ + pointer = (UCHAR *) first_unused_memory; + + /* Create the main thread. */ + tx_thread_create(&ftest_0, "thread 0", ftest_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; + + /* Setup memory for the RAM disk and the sector cache. */ + cache_buffer = pointer; + pointer = pointer + CACHE_SIZE; + ram_disk_memory = pointer; +#endif + + /* Initialize the FileX system. */ + fx_system_initialize(); + +#ifdef FX_STANDALONE_ENABLE + ftest_0_entry(0); +#endif +} + + + +/* Define the test threads. */ + +static void ftest_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +ULONG read_value; +ULONG write_value; +ULONG available_bytes; +ULONG i; + + FX_PARAMETER_NOT_USED(thread_input); + + /* Print out some test information banners. */ + printf("FileX Test: Utility FAT flush test......................................."); + + /* Format the media. This needs to be done before opening it! */ + status = fx_media_format(&ram_disk, + _fx_ram_driver, // Driver entry + ram_disk_memory, // RAM disk memory pointer + cache_buffer, // Media buffer pointer + CACHE_SIZE, // Media buffer size + "MY_RAM_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 511, // Total sectors + 128, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Determine if the format had an error. */ + if (status) + { + + printf("ERROR!\n"); + test_control_return(2); + } + + /* Open the ram_disk. */ + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE); + + /* Check the status. */ + if (status != FX_SUCCESS) + { + + /* Error, return error code. */ + printf("ERROR!\n"); + test_control_return(21); + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&ram_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(3); + } + /* Open the test file. */ + status = fx_file_open(&ram_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(4); + } + + /* Pickup the available bytes in the media. */ + status = fx_media_space_available(&ram_disk, &available_bytes); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (available_bytes < sizeof(ULONG))) + { + + printf("ERROR!\n"); + test_control_return(5); + } + + /* Loop to write successive bytes out to the file.... to fill the media! */ + i = 0; + write_value = 0; + while (i < available_bytes) + { + + /* Write 4 bytes to the file. */ + status = fx_file_write(&my_file, (void *) &write_value, sizeof(ULONG)); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + printf("ERROR!\n"); + test_control_return(6); + } + + /* Increment byte count. */ + i = i + sizeof(ULONG); + + /* Increment write value. */ + write_value++; + } + + /* Pickup the available bytes in the media again. */ + status = fx_media_space_available(&ram_disk, &i); + + /* Check for available bytes error. */ + if ((status != FX_SUCCESS) || (i != 0)) + { + + printf("ERROR!\n"); + test_control_return(7); + } + + for (i = 0; i < FX_MAX_FAT_CACHE; i++) + { + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_cluster = 496; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_value = 1; + ram_disk.fx_media_fat_cache[i].fx_fat_cache_entry_dirty = 1; + } + + _fx_utility_FAT_flush(&ram_disk); + + /* Note: If definition of FX_FAT_MAP_SIZE is changed in future, the value checked in + return_value_if_fail will change for individual array element */ + for(i=0;i