stasis_recording/stored: remove calls to deprecated readdir_r function.

The readdir_r function has been deprecated and should no longer be used. This
patch removes the readdir_r dependency (replaced it with readdir) and also moves
the directory search code to a more centralized spot (file.c)

Also removed a strict dependency on the dirent structure's d_type field as it
is not portable. The code now checks to see if the value is available. If so,
it tries to use it, but defaults back to using the stats function if necessary.

Lastly, for most implementations of readdir it *should* be thread-safe to make
concurrent calls to it as long as different directory streams are specified.
glibc falls into this category. However, since it is possible that there exist
some implementations that are not safe, locking has been added for those other
than glibc.

ASTERISK-26412
ASTERISK-26509 #close

Change-Id: Id8f54689b1e2873e82a09d0d0d2faf41964e80ba
This commit is contained in:
Kevin Harwell
2016-10-28 15:11:35 -05:00
parent 57a9797e0a
commit bd4d7d8ad0
4 changed files with 421 additions and 151 deletions

View File

@@ -1095,6 +1095,150 @@ int ast_filecopy(const char *filename, const char *filename2, const char *fmt)
return filehelper(filename, filename2, fmt, ACTION_COPY);
}
static int __ast_file_read_dirs(struct ast_str **path, ast_file_on_file on_file,
void *obj, int max_depth)
{
DIR *dir;
struct dirent *entry;
size_t size;
int res;
if (!(dir = opendir(ast_str_buffer(*path)))) {
ast_log(LOG_ERROR, "Error opening directory - %s: %s\n",
ast_str_buffer(*path), strerror(errno));
return -1;
}
size = ast_str_strlen(*path);
--max_depth;
res = 0;
while ((entry = readdir(dir)) != NULL && !errno) {
int is_file, is_dir, used_stat = 0;
if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) {
continue;
}
/*
* If the dirent structure has a d_type use it to determine if we are dealing with
* a file or directory. Unfortunately if it doesn't have it, or if the type is
* unknown, or a link then we'll need to use the stat function instead.
*/
#ifdef _DIRENT_HAVE_D_TYPE
if (entry->d_type != DT_UNKNOWN && entry->d_type != DT_LNK) {
is_file = entry->d_type == DT_REG;
is_dir = entry->d_type == DT_DIR;
} else
#endif
{
struct stat statbuf;
/*
* If using the stat function the file needs to be appended to the
* path so it can be found. However, before appending make sure the
* path contains only the directory for this depth level.
*/
ast_str_truncate(*path, size);
ast_str_append(path, 0, "/%s", entry->d_name);
if (stat(ast_str_buffer(*path), &statbuf)) {
ast_log(LOG_ERROR, "Error reading path stats - %s: %s\n",
ast_str_buffer(*path), strerror(errno));
/*
* Output an error, but keep going. It could just be
* a broken link and other files could be fine.
*/
continue;
}
is_file = S_ISREG(statbuf.st_mode);
is_dir = S_ISDIR(statbuf.st_mode);
used_stat = 1;
}
if (is_file) {
/* If the handler returns non-zero then stop */
if ((res = on_file(ast_str_buffer(*path), entry->d_name, obj))) {
break;
}
/* Otherwise move on to next item in directory */
continue;
}
if (!is_dir) {
ast_debug(5, "Skipping %s: not a regular file or directory\n",
ast_str_buffer(*path));
continue;
}
/* Only re-curse into sub-directories if not at the max depth */
if (max_depth != 0) {
/*
* If the stat function was used then the sub-directory has
* already been appended, otherwise append it.
*/
if (!used_stat) {
ast_str_truncate(*path, size);
ast_str_append(path, 0, "/%s", entry->d_name);
}
if ((res = __ast_file_read_dirs(path, on_file, obj, max_depth))) {
break;
}
}
}
closedir(dir);
if (!res && errno) {
ast_log(LOG_ERROR, "Error while reading directories - %s: %s\n",
ast_str_buffer(*path), strerror(errno));
res = -1;
}
return res;
}
#if !defined(__GLIBC__)
/*!
* \brief Lock to hold when iterating over directories.
*
* Currently, 'readdir' is not required to be thread-safe. In most modern implementations
* it should be safe to make concurrent calls into 'readdir' that specify different directory
* streams (glibc would be one of these). However, since it is potentially unsafe for some
* implementations we'll use our own locking in order to achieve synchronization for those.
*/
AST_MUTEX_DEFINE_STATIC(read_dirs_lock);
#endif
int ast_file_read_dirs(const char *dir_name, ast_file_on_file on_file, void *obj, int max_depth)
{
struct ast_str *path;
int res;
if (!(path = ast_str_create(256))) {
return -1;
}
ast_str_set(&path, 0, "%s", dir_name);
errno = 0;
#if !defined(__GLIBC__)
ast_mutex_lock(&read_dirs_lock);
#endif
res = __ast_file_read_dirs(&path, on_file, obj, max_depth);
#if !defined(__GLIBC__)
ast_mutex_unlock(&read_dirs_lock);
#endif
ast_free(path);
return res;
}
int ast_streamfile(struct ast_channel *chan, const char *filename, const char *preflang)
{
struct ast_filestream *fs;