Validate logical disk names in configuration parsing to ensure compatibility with snapraid-daemon

This commit is contained in:
Andrea Mazzoleni 2026-09-17 17:32:19 +02:00
parent bdc2da9a94
commit 3f3e7d4483
3 changed files with 129 additions and 0 deletions

View file

@ -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();

View file

@ -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;

View file

@ -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 */