Browse Source

[core] use readdir instead of readdir_r

readdir_r is deprecated in glibc due to serious memory handling issues
in the API: one cannot pass the size of the allocated dirent.

glibc authors claims readdir is thread-safe in modern implementations,
and expect POSIX to require it in a future version.

No way to check whether readdir is thread-safe though :(

("thread-safe" in this context means different directory streams, which
is good enough.)

Also remove li_dirent_buf_size.

Change-Id: Ia5eae3327e97dc4b0751fb2604ea21c0ce09a5f9
personal/stbuehler/wip
Stefan Bühler 4 years ago
parent
commit
bc6b256c34
  1. 2
      include/lighttpd/utils.h
  2. 30
      src/common/utils.c
  3. 21
      src/main/stat_cache.c

2
include/lighttpd/utils.h

@ -81,8 +81,6 @@ LI_API gboolean li_string_suffix(const GString *str, const gchar *s, gsize len);
LI_API void li_string_append_int(GString *dest, gint64 val);
LI_API gsize li_dirent_buf_size(DIR * dirp);
LI_API void li_apr_sha1_base64(GString *dest, const GString *passwd);
LI_API void li_apr_md5_crypt(GString *dest, const GString *password, const GString *salt);

30
src/common/utils.c

@ -851,36 +851,6 @@ void li_string_append_int(GString *dest, gint64 v) {
dest->len = len;
}
/* http://womble.decadentplace.org.uk/readdir_r-advisory.html */
gsize li_dirent_buf_size(DIR * dirp) {
glong name_max;
gsize name_end;
# if !defined(HAVE_DIRFD)
UNUSED(dirp);
# endif
# if defined(HAVE_FPATHCONF) && defined(HAVE_DIRFD) && defined(_PC_NAME_MAX)
name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
if (name_max == -1)
# if defined(NAME_MAX)
name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
# else
return (gsize)(-1);
# endif
# else
# if defined(NAME_MAX)
name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
# else
# error "buffer size for readdir_r cannot be determined"
# endif
# endif
name_end = (gsize)offsetof(struct dirent, d_name) + name_max + 1;
return (name_end > sizeof(struct dirent) ? name_end : sizeof(struct dirent));
}
const char *li_remove_path(const char *path) {
char *p = strrchr(path, G_DIR_SEPARATOR);
if (NULL != p && *(p) != '\0') {

21
src/main/stat_cache.c

@ -108,10 +108,7 @@ static void stat_cache_run(gpointer data) {
if (!sce->data.failed && sce->type == STAT_CACHE_ENTRY_DIR) {
/* dirlisting */
DIR *dirp;
gsize size;
struct dirent *entry;
struct dirent *result;
gint error;
liStatCacheEntryData sced;
GString *str;
@ -120,16 +117,19 @@ static void stat_cache_run(gpointer data) {
sce->data.failed = TRUE;
sce->data.err = errno;
} else {
size = li_dirent_buf_size(dirp);
LI_FORCE_ASSERT(size != (gsize)-1);
entry = g_slice_alloc(size);
sce->dirlist = g_array_sized_new(FALSE, FALSE, sizeof(liStatCacheEntryData), 32);
str = g_string_sized_new(sce->data.path->len + 64);
g_string_append_len(str, GSTR_LEN(sce->data.path));
while ((error = readdir_r(dirp, entry, &result)) == 0 && result != NULL) {
/* glibc claims modern readdir are thread-safe, and
* readdir_r has issues. no way to check readdir is actually
* safe, hope for the best.
*/
for (;;) {
errno = 0; /* readdir may not reset errno */
if (NULL == (result = readdir(dirp))) break;
/* hide "." and ".." */
if (result->d_name[0] == '.' && (result->d_name[1] == '\0' ||
(result->d_name[1] == '.' && result->d_name[2] == '\0'))) {
@ -155,13 +155,12 @@ static void stat_cache_run(gpointer data) {
g_array_append_val(sce->dirlist, sced);
}
if (error) {
if (errno) {
sce->data.failed = TRUE;
sce->data.err = error;
sce->data.err = errno;
}
g_string_free(str, TRUE);
g_slice_free1(size, entry);
closedir(dirp);
}
}

Loading…
Cancel
Save