Files
s390-tools/libutil/util_scandir.c
Peter Oberparleiter 75ab455cf6 libutil: Avoid realloc() with zero size
According to the valgrind man-page, "the behaviour of realloc() with a
size of zero is implementation defined in C17 and undefined in C23."

The current glibc implementation frees the specified buffer, returns
NULL and doesn't set errno. While this behavior is unlikely to change
in the near future, code relying on it may not be compatible with other
libc implementations. Also this realloc() use is flagged as an error in
valgrind runs, making valgrind output less usable.

Fix this by explicitly adding code to cover the realloc(buffer, 0) case
in util_realloc(). Also change libutil users of realloc() to use
util_realloc() instead.

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-01-19 13:03:19 +01:00

177 lines
4.5 KiB
C

/*
* util - Utility function library
*
* Scan a directory for matching entries
*
* Copyright IBM Corp. 2016, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <dirent.h>
#include <errno.h>
#include <libgen.h>
#include <regex.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "lib/util_base.h"
#include "lib/util_libc.h"
#include "lib/util_panic.h"
#include "lib/util_scandir.h"
#include "lib/zt_common.h"
/// @cond
struct util_scandir_filter {
regex_t reg_buf;
};
/// @endcond
/*
* Check directory entry
*/
static int filter_regexp(const struct dirent *de, void *data)
{
struct util_scandir_filter *filter = data;
regmatch_t pmatch[1];
if (regexec(&filter->reg_buf, de->d_name, (size_t) 1, pmatch, 0) == 0)
return 1;
return 0;
}
typedef int (*__compar_fn_t) (const void *, const void *);
/*
* Return sorted "struct dirent" array for entries that match "filter_fn"
*/
static int __scandir(struct dirent ***de_vec, const char *path,
int (*filter_fn)(const struct dirent *, void *),
void *filter_data,
int (*compar_fn)(const struct dirent **,
const struct dirent **))
{
struct dirent *de, *de_new, **de_vec_new = NULL;
int count = 0;
DIR *dirp;
*de_vec = NULL;
dirp = opendir(path);
if (!dirp)
return -1;
while ((de = readdir(dirp))) {
if (filter_fn(de, filter_data) == 0)
continue;
de_new = util_malloc(sizeof(*de_new));
*de_new = *de;
de_vec_new = util_realloc(de_vec_new, sizeof(void *) * (count + 1));
de_vec_new[count++] = de_new;
}
closedir(dirp);
if (compar_fn)
qsort(de_vec_new, count, sizeof(void *),
(__compar_fn_t) compar_fn);
*de_vec = de_vec_new;
return count;
}
/*
* Return sorted "struct dirent" array for entries that match "pattern"
*/
static int scandir_regexp(struct dirent ***de_vec, const char *path,
const char *pattern,
int compar_fn(const struct dirent **,
const struct dirent **))
{
struct util_scandir_filter filter;
char err_buf[256];
int count, rc;
rc = regcomp(&filter.reg_buf, pattern, REG_EXTENDED);
if (rc) {
regerror(rc, &filter.reg_buf, err_buf, sizeof(err_buf));
util_panic("Function regcomp(%s) failed: %s\n", pattern,
err_buf);
}
count = __scandir(de_vec, path, filter_regexp, &filter, compar_fn);
regfree(&filter.reg_buf);
return count;
}
/**
* Compare two hexadecimal string dirents numerically
*
* @param[in] de1 First directory entry
* @param[in] de2 Second directory entry
*
* @retval -1 de1 < de2
* @retval 0 de1 = de2
* @retval 1 de1 > de2
*/
int util_scandir_hexsort(const struct dirent **de1, const struct dirent **de2)
{
unsigned long val1 = strtoul((*de1)->d_name, NULL, 16);
unsigned long val2 = strtoul((*de2)->d_name, NULL, 16);
if (val1 < val2)
return -1;
if (val1 == val2)
return 0;
return 1;
}
/**
* Construct a list of direcotry entries using POSIX regular expressions
*
* A desired directory in sysfs is scanned for entries of a given name
* pattern. The name pattern is constructed with sprintf() using a format
* string and variable argument list. After constructing the pattern it
* is used with regcomp() and regexec() to find matching entries.
* The returned list of matches consist of an array of pointers to
* directory entries. The entries as well as the pointer array itself are
* allocated by the function and has to be released by the user via free.
*
* @param[out] de_vec Vector of matched directory entries, or NULL
* @param[in] compar_fn Callback function for sorting the entry list
* @param[in] path Path to the directory to scan
* @param[in] fmt Format string, describes the search pattern as POSIX regex
* @param[in] ... Values for format string
*
* @returns Number of returned directory entries, or -1 on error
*/
int util_scandir(struct dirent ***de_vec,
int compar_fn(const struct dirent **first,
const struct dirent **second),
const char *path,
const char *fmt, ...)
{
char *pattern;
va_list ap;
int rc;
va_start(ap, fmt);
rc = vasprintf(&pattern, fmt, ap);
va_end(ap);
if (rc < 0)
return -1;
rc = scandir_regexp(de_vec, path, pattern, compar_fn);
free(pattern);
return rc;
}
/**
* Free list of directory entries
*
* @param[in] de_vec Vector of directory entries
* @param[in] count Count of directory entries
*/
void util_scandir_free(struct dirent **de_vec, int count)
{
util_ptr_vec_free((void **) de_vec, count);
}