diff --git a/cmdline/snapraid.c b/cmdline/snapraid.c index c3c2f258..598a90fe 100644 --- a/cmdline/snapraid.c +++ b/cmdline/snapraid.c @@ -1530,7 +1530,8 @@ int snapraid_main(int argc, char* argv[]) if (state.opt.force_realloc) state_locate_mark_tail_blocks_for_resync(&state, opt.parity_tail); - if (opt.gui_touch_before) { + /* skip touch itself until content is loaded and sync has initialized parity sizes */ + if (opt.gui_touch_before && state_is_write_ready(&state)) { ret = state_touch(&state); /* save the new state if required */ @@ -1627,6 +1628,9 @@ int snapraid_main(int argc, char* argv[]) } else if (operation == OPERATION_REHASH) { state_read(&state); + /* prevent saving content with uninitialized parity split sizes */ + state_require_write_ready(&state); + state_rehash(&state); /* @@ -1645,6 +1649,9 @@ int snapraid_main(int argc, char* argv[]) } else if (operation == OPERATION_SCRUB) { state_read(&state); + /* prevent saving content with uninitialized parity split sizes */ + state_require_write_ready(&state); + /* refresh the size info before the content write */ state_refresh(&state); @@ -1667,6 +1674,9 @@ int snapraid_main(int argc, char* argv[]) } else if (operation == OPERATION_REWRITE) { state_read(&state); + /* prevent saving content with uninitialized parity split sizes */ + state_require_write_ready(&state); + /* intercept signals while operating */ os_signal_init(app_signal_handler, app_signal_handler); @@ -1682,6 +1692,9 @@ int snapraid_main(int argc, char* argv[]) } else if (operation == OPERATION_TOUCH) { state_read(&state); + /* prevent saving content with uninitialized parity split sizes */ + state_require_write_ready(&state); + memory(); ret = state_touch(&state); @@ -1696,6 +1709,7 @@ int snapraid_main(int argc, char* argv[]) */ os_signal_init(app_signal_handler, app_signal_handler); + /* save the new state if required */ if (state.need_write) state_write(&state); } else if (operation == OPERATION_SPINUP) { diff --git a/cmdline/state.c b/cmdline/state.c index c7be0926..0d44fa28 100644 --- a/cmdline/state.c +++ b/cmdline/state.c @@ -5428,6 +5428,37 @@ void state_read(struct snapraid_state* state) state->checked_read = 1; } +/** + * Check if all configured parity splits have persistent sizes. + */ +int state_is_write_ready(struct snapraid_state* state) +{ + unsigned l, s; + + for (l = 0; l < state->level; ++l) { + for (s = 0; s < state->parity[l].split_mac; ++s) { + if (state->parity[l].split_map[s].size == PARITY_SIZE_INVALID) + return 0; + } + } + + return 1; +} + +/** + * Require sync to initialize parity sizes before commands that can save content. + * Check before modifying timestamps or metadata, as a later write could fail + * even if the command has no blocks to process (e.g. a missing content replica). + */ +void state_require_write_ready(struct snapraid_state* state) +{ + if (!state_is_write_ready(state)) { + log_fatal(EUSER, "The array has not been synced yet, or its parity configuration has changed.\n"); + log_fatal(EUSER, "Run 'snapraid sync' before '%s'.\n", state->command); + exit(EXIT_FAILURE); + } +} + struct state_verify_thread_context { struct snapraid_state* state; struct snapraid_content* content; diff --git a/cmdline/state.h b/cmdline/state.h index 31215e86..b914b782 100644 --- a/cmdline/state.h +++ b/cmdline/state.h @@ -240,6 +240,16 @@ void state_probe(struct snapraid_state* state); */ void state_read(struct snapraid_state* state); +/** + * Check if all configured parity splits have persistent sizes. + */ +int state_is_write_ready(struct snapraid_state* state); + +/** + * Require initialized parity split sizes before commands that can save content. + */ +void state_require_write_ready(struct snapraid_state* state); + /** * Count the number of unsynced parity positions using an event sweep across extents. * Counts positions in [blockmin, blockmax). The caller must ensure that the disk list,