Skip to content

Commit

Permalink
f3read/f3write: avoid the execution stack to list files
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
AltraMayor committed Dec 11, 2020
1 parent 46a5095 commit 7c5d6b7
Showing 1 changed file with 67 additions and 34 deletions.
101 changes: 67 additions & 34 deletions utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
Expand Down Expand Up @@ -76,40 +77,77 @@ static long number_from_filename(const char *filename)
return num - 1;
}

/* Don't call this function directly, use ls_my_files() instead. */
static long *__ls_my_files(DIR *dir, long start_at, long end_at,
int *pcount, int *pindex)
static inline bool include_this_file(const char *filename,
long start_at, long end_at, long *number)
{
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;
const char *filename;
long number, *ret;
int my_index;
long dummy, total = 0;

if (!dir)
err(errno, "Can't open path %s at %s()", path, __func__);

entry = readdir(dir);
if (!entry) {
ret = malloc(sizeof(long) * (*pcount + 1));
assert(ret);
*pindex = *pcount - 1;
ret[*pcount] = -1;
closedir(dir);
return ret;
while (entry) {
if (include_this_file(entry->d_name, start_at, end_at, &dummy))
total++;
entry = readdir(dir);
}
closedir(dir);

filename = entry->d_name;
if (!is_my_file(filename))
return __ls_my_files(dir, start_at, end_at, pcount, pindex);
return total;
}

/* Cache @number because @entry may go away. */
number = number_from_filename(filename);
/* 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;

/* 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);
assert(total_files >= 0);
ret = malloc(sizeof(*ret) * (total_files + 1));
assert(ret);

(*pcount)++;
ret = __ls_my_files(dir, start_at, end_at, pcount, &my_index);
ret[my_index] = number;
*pindex = my_index - 1;
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;
}

entry = readdir(dir);
}
closedir(dir);

ret[index] = -1;
*pcount = index;
return ret;
}

Expand All @@ -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)
{
DIR *dir = opendir(path);
int my_count;
int my_index;
long *ret;
long *ret, my_count;

if (!dir)
err(errno, "Can't open path %s", path);
do {
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);
return ret;
}
Expand Down

0 comments on commit 7c5d6b7

Please sign in to comment.