diff --git a/libdm/libdm-targets.c b/libdm/libdm-targets.c index 8a8a328d1..b91c7e9af 100644 --- a/libdm/libdm-targets.c +++ b/libdm/libdm-targets.c @@ -66,6 +66,23 @@ static const char *_skip_fields(const char *p, unsigned nr) return p; } +/* + * Count number of single-space delimited fields. + * Number of fields is number of spaces plus one. + */ +static unsigned _count_fields(const char *p) +{ + unsigned nr = 1; + + if (!p || !*p) + return 0; + + while ((p = _skip_fields(p, 1))) + nr++; + + return nr; +} + /* * Various RAID status versions include: * Versions < 1.5.0 (4 fields): @@ -77,69 +94,71 @@ int dm_get_status_raid(struct dm_pool *mem, const char *params, struct dm_status_raid **status) { int i; - const char *pp, *p; - struct dm_status_raid *s; + unsigned num_fields; + const char *p, *pp, *msg_fields = ""; + struct dm_status_raid *s = NULL; - if (!params || !(p = strchr(params, ' '))) { - log_error("Failed to parse invalid raid params."); - return 0; - } - p++; + if ((num_fields = _count_fields(params)) < 4) + goto_bad; - /* second field holds the device count */ - if (sscanf(p, "%d", &i) != 1) - return_0; + /* Second field holds the device count */ + msg_fields = "<#devs> "; + if (!(p = _skip_fields(params, 1)) || (sscanf(p, "%d", &i) != 1)) + goto_bad; + msg_fields = ""; if (!(s = dm_pool_zalloc(mem, sizeof(struct dm_status_raid)))) - return_0; + goto_bad; if (!(s->raid_type = dm_pool_zalloc(mem, p - params))) goto_bad; /* memory is freed when pool is destroyed */ - if (!(s->dev_health = dm_pool_zalloc(mem, i + 1))) + if (!(s->dev_health = dm_pool_zalloc(mem, i + 1))) /* Space for health chars */ goto_bad; + msg_fields = " <#devices> and "; if (sscanf(params, "%s %u %s %" PRIu64 "/%" PRIu64, s->raid_type, &s->dev_count, s->dev_health, &s->insync_regions, - &s->total_regions) != 5) { - log_error("Failed to parse raid params: %s", params); - goto bad; - } - - *status = s; + &s->total_regions) != 5) + goto_bad; /* * All pre-1.5.0 version parameters are read. Now we check - * for additional 1.5.0+ parameters. + * for additional 1.5.0+ parameters (i.e. num_fields at least 6). * * Note that 'sync_action' will be NULL (and mismatch_count * will be 0) if the kernel returns a pre-1.5.0 status. */ - for (p = params, i = 0; i < 4; i++, p++) - if (!(p = strchr(p, ' '))) - return 1; /* return pre-1.5.0 status */ + if (num_fields < 6) + goto out; - pp = p; - if (!(p = strchr(p, ' '))) { - log_error(INTERNAL_ERROR "Bad RAID status received."); - goto bad; - } - p++; + msg_fields = " and "; - if (!(s->sync_action = dm_pool_zalloc(mem, p - pp))) + /* Skip pre-1.5.0 params */ + if (!(p = _skip_fields(params, 4)) || !(pp = _skip_fields(p, 1))) goto_bad; - if (sscanf(pp, "%s %" PRIu64, s->sync_action, &s->mismatch_count) != 2) { - log_error("Failed to parse raid params: %s", params); - goto bad; - } + if (!(s->sync_action = dm_pool_zalloc(mem, pp - p))) + goto_bad; + + if (sscanf(p, "%s %" PRIu64, s->sync_action, &s->mismatch_count) != 2) + goto_bad; + +out: + *status = s; return 1; + bad: - dm_pool_free(mem, s); + log_error("Failed to parse %sraid params: %s", msg_fields, params); + + if (s) + dm_pool_free(mem, s); + + *status = NULL; return 0; }