From 3f3e7d44830dc53edba5d9ee0da62742b1721e56 Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Thu, 17 Sep 2026 17:32:19 +0200 Subject: [PATCH] Validate logical disk names in configuration parsing to ensure compatibility with snapraid-daemon --- cmdline/selftest.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++ cmdline/state.c | 58 +++++++++++++++++++++++++++++++++++++++++++++ cmdline/state.h | 12 ++++++++++ 3 files changed, 129 insertions(+) diff --git a/cmdline/selftest.c b/cmdline/selftest.c index bd9341a9..bbb0764e 100644 --- a/cmdline/selftest.c +++ b/cmdline/selftest.c @@ -1883,6 +1883,62 @@ static void test_smart_ignore(void) } } +static void test_disk_name(void) +{ + char name_127[128]; + char name_128[129]; + + /* valid normal names */ + assert(disk_name_invalid_reason("d1") == 0); + assert(disk_name_invalid_reason("data_01") == 0); + assert(disk_name_invalid_reason("disk-A") == 0); + + /* 127-byte name is valid */ + memset(name_127, 'a', 127); + name_127[127] = 0; + assert(disk_name_invalid_reason(name_127) == 0); + + /* 128-byte name is rejected */ + memset(name_128, 'a', 128); + name_128[128] = 0; + assert(disk_name_invalid_reason(name_128) != 0); + + /* slash delimiter rejected */ + assert(disk_name_invalid_reason("data/1") != 0); + assert(disk_name_invalid_reason("/disk") != 0); + assert(disk_name_invalid_reason("disk/") != 0); + + /* backslash delimiter rejected */ + assert(disk_name_invalid_reason("data\\1") != 0); + assert(disk_name_invalid_reason("\\disk") != 0); + assert(disk_name_invalid_reason("disk\\") != 0); + + /* colon delimiter rejected */ + assert(disk_name_invalid_reason("data:1") != 0); + assert(disk_name_invalid_reason(":disk") != 0); + assert(disk_name_invalid_reason("disk:") != 0); + + /* reserved parity names and aliases rejected */ + assert(disk_name_invalid_reason("parity") != 0); + assert(disk_name_invalid_reason("1-parity") != 0); + assert(disk_name_invalid_reason("2-parity") != 0); + assert(disk_name_invalid_reason("3-parity") != 0); + assert(disk_name_invalid_reason("4-parity") != 0); + assert(disk_name_invalid_reason("5-parity") != 0); + assert(disk_name_invalid_reason("6-parity") != 0); + assert(disk_name_invalid_reason("q-parity") != 0); + assert(disk_name_invalid_reason("r-parity") != 0); + assert(disk_name_invalid_reason("z-parity") != 0); + + /* valid names containing parity as substring */ + assert(disk_name_invalid_reason("parity1") == 0); + assert(disk_name_invalid_reason("parity_data") == 0); + assert(disk_name_invalid_reason("my_parity") == 0); + assert(disk_name_invalid_reason("disparity") == 0); + assert(disk_name_invalid_reason("2-parity-extra") == 0); + assert(disk_name_invalid_reason("q-parity-disk") == 0); +} + static void test_stream(void) { STREAM f; @@ -2733,6 +2789,9 @@ void test(int argc, char* argv[]) /* smart attribute ignore rules */ test_smart_ignore(); + /* logical disk name validation */ + test_disk_name(); + t_selftest_start = os_tick_ms(); selftest(); t_selftest_end = os_tick_ms(); diff --git a/cmdline/state.c b/cmdline/state.c index 1cb1c9bc..568a235f 100644 --- a/cmdline/state.c +++ b/cmdline/state.c @@ -114,6 +114,24 @@ static int lev_config_scan(const char* s, unsigned* level, unsigned* mode) return -1; } +const char* disk_name_invalid_reason(const char* name) +{ + unsigned dummy_level; + + if (strlen(name) > DISK_NAME_MAX) + return "Name is too long (maximum 127 bytes)"; + if (strchr(name, '/') != 0) + return "Name contains '/'"; + if (strchr(name, '\\') != 0) + return "Name contains '\\'"; + if (strchr(name, ':') != 0) + return "Name contains ':'"; + if (lev_config_scan(name, &dummy_level, 0) == 0) + return "Name is a reserved parity name"; + + return 0; +} + const char* lev_raid_name(unsigned mode, unsigned n) { switch (n) { @@ -1032,6 +1050,14 @@ void state_config(struct snapraid_state* state, const char* path, const char* co /* LCOV_EXCL_STOP */ } + const char* reason = disk_name_invalid_reason(buffer); + if (reason) { + /* LCOV_EXCL_START */ + log_fatal(EUSER, "Invalid 'data' name '%s' in '%s' at line %u. %s.\n", buffer, path, line, reason); + exit(EXIT_FAILURE); + /* LCOV_EXCL_STOP */ + } + sgetspace(f); ret = sgetlasttok(f, dir, sizeof(dir)); @@ -1065,6 +1091,18 @@ void state_config(struct snapraid_state* state, const char* path, const char* co /* LCOV_EXCL_STOP */ } + for (i = state->extralist; i != 0; i = i->next) { + struct snapraid_extra* found_extra = i->data; + if (strcmp(found_extra->name, buffer) == 0) + break; + } + if (i) { + /* LCOV_EXCL_START */ + log_fatal(EUSER, "Duplicate 'data' name '%s' already used in 'extra' at line %u\n", buffer, line); + exit(EXIT_FAILURE); + /* LCOV_EXCL_STOP */ + } + /* if the disk has to be present */ skip_access = 0; if (!state->opt.skip_disk_access) { @@ -1134,6 +1172,14 @@ void state_config(struct snapraid_state* state, const char* path, const char* co /* LCOV_EXCL_STOP */ } + const char* reason = disk_name_invalid_reason(buffer); + if (reason) { + /* LCOV_EXCL_START */ + log_fatal(EUSER, "Invalid 'extra' name '%s' in '%s' at line %u. %s.\n", buffer, path, line, reason); + exit(EXIT_FAILURE); + /* LCOV_EXCL_STOP */ + } + sgetspace(f); ret = sgetlasttok(f, dir, sizeof(dir)); @@ -1167,6 +1213,18 @@ void state_config(struct snapraid_state* state, const char* path, const char* co /* LCOV_EXCL_STOP */ } + for (i = state->disklist; i != 0; i = i->next) { + struct snapraid_disk* found_disk = i->data; + if (strcmp(found_disk->name, buffer) == 0) + break; + } + if (i) { + /* LCOV_EXCL_START */ + log_fatal(EUSER, "Duplicate 'extra' name '%s' already used in 'data' at line %u\n", buffer, line); + exit(EXIT_FAILURE); + /* LCOV_EXCL_STOP */ + } + /* if the disk has to be present */ if (!state->opt.skip_disk_access) { struct stat st; diff --git a/cmdline/state.h b/cmdline/state.h index 30fdf93f..44d89573 100644 --- a/cmdline/state.h +++ b/cmdline/state.h @@ -27,6 +27,18 @@ const char* lev_name(unsigned level); */ const char* lev_config_name(unsigned level); +/** + * Maximum byte length of a logical disk name. + */ +#define DISK_NAME_MAX 127 + +/** + * Validate a logical disk name. + * + * Returns 0 if valid, or a static string error reason if invalid. + */ +const char* disk_name_invalid_reason(const char* name); + /****************************************************************************/ /* state */