asterisk/tests/test_file.c
George Joseph 3017f09f22 file.c/__ast_file_read_dirs: Fix issues on filesystems without d_type
One of the code paths in __ast_file_read_dirs will only get executed if
the OS doesn't support dirent->d_type OR if the filesystem the
particular file is on doesn't support it.  So, while standard Linux
systems support the field, some filesystems like XFS do not.  In this
case, we need to call stat() to determine whether the directory entry
is a file or directory so we append the filename to the supplied
directory path and call stat.  We forgot to truncate path back to just
the directory afterwards though so we were passing a complete file name
to the callback in the dir_name parameter instead of just the directory
name.

The logic has been re-written to only create a full_path if we need to
call stat() or if we need to descend into another directory.

Change-Id: I54e4228bd8355fad65200c6df3ec4c9c8a98dfba
2016-11-15 21:21:59 -05:00

196 lines
4.6 KiB
C

/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2016, Digium, Inc.
*
* Kevin Harwell <kharwell@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*** MODULEINFO
<depend>TEST_FRAMEWORK</depend>
<support_level>core</support_level>
***/
#include "asterisk.h"
#include <sys/stat.h>
#include <stdio.h>
#include "asterisk/file.h"
#include "asterisk/paths.h"
#include "asterisk/test.h"
#include "asterisk/module.h"
#include "asterisk/strings.h"
#include "asterisk/vector.h"
#define FOUND -7
AST_VECTOR(_filenames, struct ast_str *);
static void rm_file(struct ast_str *filename)
{
if (unlink(ast_str_buffer(filename))) {
ast_log(LOG_ERROR, "Unable to remove file: %s\n", ast_str_buffer(filename));
}
ast_free(filename);
}
static int test_files_destroy(struct ast_test *test, char *dir_name,
struct _filenames *filenames)
{
int res;
if (filenames) {
AST_VECTOR_CALLBACK_VOID(filenames, rm_file);
AST_VECTOR_FREE(filenames);
}
if ((res = rmdir(dir_name)) < 0) {
ast_test_status_update(test, "Failed to remove directory: %s\n", dir_name);
}
return res;
}
static int test_files_create(struct ast_test *test, char *dir_name,
struct _filenames *filenames, int num)
{
int i;
if (!(mkdtemp(dir_name))) {
ast_test_status_update(test, "Failed to create directory: %s\n", dir_name);
return -1;
}
AST_VECTOR_INIT(filenames, num);
/*
* Create "num" files under the specified directory
*/
for (i = 0; i < num; ++i) {
int fd;
struct ast_str *filename = ast_str_create(32);
if (!filename) {
break;
}
ast_str_set(&filename, 0, "%s/XXXXXX", dir_name);
fd = mkstemp(ast_str_buffer(filename));
if (fd < 0) {
ast_test_status_update(test, "Failed to create file: %s\n",
ast_str_buffer(filename));
ast_free(filename);
break;
}
close(fd);
AST_VECTOR_APPEND(filenames, filename);
}
if (i != num) {
test_files_destroy(test, dir_name, filenames);
return -1;
}
return 0;
}
static char *test_files_get_one(struct _filenames *filenames, int num)
{
/* Every file is in a directory and contains a '/' so okay to do this */
return strrchr(ast_str_buffer(
AST_VECTOR_GET(filenames, ast_random() % (num - 1))), '/') + 1;
}
static int handle_find_file(const char *dir_name, const char *filename, void *obj)
{
struct stat statbuf;
char *full_path = ast_alloca(strlen(dir_name) + strlen(filename) + 2);
sprintf(full_path, "%s/%s", dir_name, filename);
errno = 0;
if (stat(full_path, &statbuf)) {
ast_log(LOG_ERROR, "Error reading path stats - %s: %s\n",
full_path, strerror(errno));
return 0;
}
/* obj contains the name of the file we are looking for */
return strcmp(obj, filename) ? 0 : FOUND;
}
AST_TEST_DEFINE(read_dirs_test)
{
char tmp_dir[] = "/tmp/tmpdir.XXXXXX";
struct ast_str *tmp_sub_dir;
struct _filenames filenames;
enum ast_test_result_state res;
const int num_files = 10 + (ast_random() % 10); /* 10-19 random files */
switch (cmd) {
case TEST_INIT:
info->name = "read_dir_test";
info->category = "/main/file/";
info->summary = "Read a directory's content";
info->description = "Iterate over directories looking for a file.";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
/*
* We want to test recursively searching into a subdirectory, so
* create a top level tmp directory where we will start the search.
*/
if (!(mkdtemp(tmp_dir))) {
ast_test_status_update(test, "Failed to create directory: %s\n", tmp_dir);
return AST_TEST_FAIL;
}
tmp_sub_dir = ast_str_alloca(32);
ast_str_set(&tmp_sub_dir, 0, "%s/XXXXXX", tmp_dir);
if (test_files_create(test, ast_str_buffer(tmp_sub_dir), &filenames, num_files)) {
test_files_destroy(test, tmp_dir, NULL);
return AST_TEST_FAIL;
}
res = ast_file_read_dirs(tmp_dir, handle_find_file, test_files_get_one(
&filenames, num_files), 2) == FOUND ? AST_TEST_PASS : AST_TEST_FAIL;
if (test_files_destroy(test, ast_str_buffer(tmp_sub_dir), &filenames) ||
test_files_destroy(test, tmp_dir, NULL)) {
res = AST_TEST_FAIL;
}
return res;
}
static int unload_module(void)
{
AST_TEST_UNREGISTER(read_dirs_test);
return 0;
}
static int load_module(void)
{
AST_TEST_REGISTER(read_dirs_test);
return AST_MODULE_LOAD_SUCCESS;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "File test module");