Switch to readdir from readdir_r

It's deprecated in glibc, and does not work properly on Solaris.

Fixes #5458.
This commit is contained in:
Fabian Homborg 2019-01-02 14:16:39 +01:00
parent f3e87b7996
commit 12d7c7feb6

View File

@ -37,27 +37,27 @@ const file_id_t kInvalidFileID = {(dev_t)-1LL, (ino_t)-1LL, (uint64_t)-1LL, -1,
static owning_lock<std::unordered_map<wcstring, wcstring>> wgettext_map; static owning_lock<std::unordered_map<wcstring, wcstring>> wgettext_map;
bool wreaddir_resolving(DIR *dir, const wcstring &dir_path, wcstring &out_name, bool *out_is_dir) { bool wreaddir_resolving(DIR *dir, const wcstring &dir_path, wcstring &out_name, bool *out_is_dir) {
struct dirent d; struct dirent *result = readdir(dir);
struct dirent *result = NULL; if (!result) {
int retval = readdir_r(dir, &d, &result);
if (retval || !result) {
out_name = L""; out_name = L"";
return false; return false;
} }
out_name = str2wcstring(d.d_name); out_name = str2wcstring(result->d_name);
if (!out_is_dir) return true; if (!out_is_dir) {
return true;
}
// The caller cares if this is a directory, so check. // The caller cares if this is a directory, so check.
bool is_dir = false; bool is_dir = false;
// We may be able to skip stat, if the readdir can tell us the file type directly. // We may be able to skip stat, if the readdir can tell us the file type directly.
bool check_with_stat = true; bool check_with_stat = true;
#ifdef HAVE_STRUCT_DIRENT_D_TYPE #ifdef HAVE_STRUCT_DIRENT_D_TYPE
if (d.d_type == DT_DIR) { if (result->d_type == DT_DIR) {
// Known directory. // Known directory.
is_dir = true; is_dir = true;
check_with_stat = false; check_with_stat = false;
} else if (d.d_type == DT_LNK || d.d_type == DT_UNKNOWN) { } else if (result->d_type == DT_LNK || result->d_type == DT_UNKNOWN) {
// We want to treat symlinks to directories as directories. Use stat to resolve it. // We want to treat symlinks to directories as directories. Use stat to resolve it.
check_with_stat = true; check_with_stat = true;
} else { } else {
@ -70,7 +70,7 @@ bool wreaddir_resolving(DIR *dir, const wcstring &dir_path, wcstring &out_name,
// We couldn't determine the file type from the dirent; check by stat'ing it. // We couldn't determine the file type from the dirent; check by stat'ing it.
cstring fullpath = wcs2string(dir_path); cstring fullpath = wcs2string(dir_path);
fullpath.push_back('/'); fullpath.push_back('/');
fullpath.append(d.d_name); fullpath.append(result->d_name);
struct stat buf; struct stat buf;
if (stat(fullpath.c_str(), &buf) != 0) { if (stat(fullpath.c_str(), &buf) != 0) {
is_dir = false; is_dir = false;
@ -83,34 +83,24 @@ bool wreaddir_resolving(DIR *dir, const wcstring &dir_path, wcstring &out_name,
} }
bool wreaddir(DIR *dir, wcstring &out_name) { bool wreaddir(DIR *dir, wcstring &out_name) {
// We need to use a union to ensure that the dirent struct is large enough to avoid stomping on struct dirent *result = readdir(dir);
// the stack. Some platforms incorrectly defined the `d_name[]` member as being one element if (!result) {
// long when it should be at least NAME_MAX + 1.
union {
struct dirent d;
char c[offsetof(struct dirent, d_name) + NAME_MAX + 1];
} d_u;
struct dirent *result = NULL;
int retval = readdir_r(dir, &d_u.d, &result);
if (retval || !result) {
out_name = L""; out_name = L"";
return false; return false;
} }
out_name = str2wcstring(d_u.d.d_name); out_name = str2wcstring(result->d_name);
return true; return true;
} }
bool wreaddir_for_dirs(DIR *dir, wcstring *out_name) { bool wreaddir_for_dirs(DIR *dir, wcstring *out_name) {
struct dirent d;
struct dirent *result = NULL; struct dirent *result = NULL;
while (!result) { while (!result) {
int retval = readdir_r(dir, &d, &result); result = readdir(dir);
if (retval || !result) break; if (!result) break;
#if HAVE_STRUCT_DIRENT_D_TYPE #if HAVE_STRUCT_DIRENT_D_TYPE
switch (d.d_type) { switch (result->d_type) {
case DT_DIR: case DT_DIR:
case DT_LNK: case DT_LNK:
case DT_UNKNOWN: { case DT_UNKNOWN: {
@ -129,7 +119,8 @@ bool wreaddir_for_dirs(DIR *dir, wcstring *out_name) {
if (result && out_name) { if (result && out_name) {
*out_name = str2wcstring(result->d_name); *out_name = str2wcstring(result->d_name);
} }
return result != NULL; if (!result) return false;
return true;
} }
const wcstring wgetcwd() { const wcstring wgetcwd() {