f3read/f3write: avoid the execution stack to list files

For large number of files, the original, recursive version of
__ls_my_files() triggers a stack overflow; see issue #153 for
an example.

This patch replaces __ls_my_files() with a version that replaces
the recursion with two scans of the target folder.
This commit is contained in:
Michel Machado 2020-12-10 19:38:18 -05:00
parent 46a509578f
commit 7c5d6b7894

103
utils.c
View File

@ -10,6 +10,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <assert.h> #include <assert.h>
@ -76,40 +77,77 @@ static long number_from_filename(const char *filename)
return num - 1; return num - 1;
} }
/* Don't call this function directly, use ls_my_files() instead. */ static inline bool include_this_file(const char *filename,
static long *__ls_my_files(DIR *dir, long start_at, long end_at, long start_at, long end_at, long *number)
int *pcount, int *pindex)
{ {
if (!is_my_file(filename))
return false;
*number = number_from_filename(filename);
return start_at <= *number && *number <= end_at;
}
static long count_files(const char *path, long start_at, long end_at)
{
DIR *dir = opendir(path);
struct dirent *entry; struct dirent *entry;
const char *filename; long dummy, total = 0;
long number, *ret;
int my_index; if (!dir)
err(errno, "Can't open path %s at %s()", path, __func__);
entry = readdir(dir); entry = readdir(dir);
if (!entry) { while (entry) {
ret = malloc(sizeof(long) * (*pcount + 1)); if (include_this_file(entry->d_name, start_at, end_at, &dummy))
assert(ret); total++;
*pindex = *pcount - 1; entry = readdir(dir);
ret[*pcount] = -1; }
closedir(dir); closedir(dir);
return ret;
return total;
}
/* Don't call this function directly, use ls_my_files() instead. */
static long *__ls_my_files(const char *path, long start_at, long end_at,
long *pcount)
{
long total_files = count_files(path, start_at, end_at);
DIR *dir;
struct dirent *entry;
long *ret, index;
assert(total_files >= 0);
ret = malloc(sizeof(*ret) * (total_files + 1));
assert(ret);
dir = opendir(path);
if (!dir)
err(errno, "Can't open path %s at %s()", path, __func__);
entry = readdir(dir);
index = 0;
while (entry) {
long number;
if (include_this_file(entry->d_name, start_at, end_at,
&number)) {
if (index >= total_files) {
/* The folder @path received more files
* before we finished scanning it.
*/
closedir(dir);
free(ret);
return NULL;
}
ret[index++] = number;
} }
filename = entry->d_name; entry = readdir(dir);
if (!is_my_file(filename)) }
return __ls_my_files(dir, start_at, end_at, pcount, pindex); closedir(dir);
/* Cache @number because @entry may go away. */ ret[index] = -1;
number = number_from_filename(filename); *pcount = index;
/* Ignore files before @start_at and after @end_at. */
if (number < start_at || end_at < number)
return __ls_my_files(dir, start_at, end_at, pcount, pindex);
(*pcount)++;
ret = __ls_my_files(dir, start_at, end_at, pcount, &my_index);
ret[my_index] = number;
*pindex = my_index - 1;
return ret; return ret;
} }
@ -121,17 +159,12 @@ static int cmpintp(const void *p1, const void *p2)
const long *ls_my_files(const char *path, long start_at, long end_at) const long *ls_my_files(const char *path, long start_at, long end_at)
{ {
DIR *dir = opendir(path); long *ret, my_count;
int my_count;
int my_index;
long *ret;
if (!dir) do {
err(errno, "Can't open path %s", path); ret = __ls_my_files(path, start_at, end_at, &my_count);
} while (!ret);
my_count = 0;
ret = __ls_my_files(dir, start_at, end_at, &my_count, &my_index);
assert(my_index == -1);
qsort(ret, my_count, sizeof(*ret), cmpintp); qsort(ret, my_count, sizeof(*ret), cmpintp);
return ret; return ret;
} }