diff --git a/glib/gspawn.c b/glib/gspawn.c index 23ade06ae..69d3fec10 100644 --- a/glib/gspawn.c +++ b/glib/gspawn.c @@ -47,6 +47,10 @@ #include #endif /* HAVE_SYS_RESOURCE_H */ +#ifdef __linux__ +#include /* for syscall and SYS_getdents64 */ +#endif + #include "gspawn.h" #include "gspawn-private.h" #include "gthread.h" @@ -1125,6 +1129,44 @@ set_cloexec (void *data, gint fd) } #ifndef HAVE_FDWALK +#ifdef __linux__ +struct linux_dirent64 +{ + guint64 d_ino; /* 64-bit inode number */ + guint64 d_off; /* 64-bit offset to next structure */ + unsigned short d_reclen; /* Size of this dirent */ + unsigned char d_type; /* File type */ + char d_name[]; /* Filename (null-terminated) */ +}; + +static gint +filename_to_fd (const char *p) +{ + char c; + int fd = 0; + const int cutoff = G_MAXINT / 10; + const int cutlim = G_MAXINT % 10; + + if (*p == '\0') + return -1; + + while ((c = *p++) != '\0') + { + if (!g_ascii_isdigit (c)) + return -1; + c -= '0'; + + /* Check for overflow. */ + if (fd > cutoff || (fd == cutoff && c > cutlim)) + return -1; + + fd = fd * 10 + c; + } + + return fd; +} +#endif + static int fdwalk (int (*cb)(void *data, int fd), void *data) { @@ -1136,45 +1178,39 @@ fdwalk (int (*cb)(void *data, int fd), void *data) struct rlimit rl; #endif -#ifdef __linux__ - DIR *d; +#ifdef __linux__ + /* Avoid use of opendir/closedir since these are not async-signal-safe. */ + int dir_fd = open ("/proc/self/fd", O_RDONLY | O_DIRECTORY); + if (dir_fd >= 0) + { + char buf[4096]; + int pos, nread; + struct linux_dirent64 *de; - if ((d = opendir("/proc/self/fd"))) { - struct dirent *de; + while ((nread = syscall (SYS_getdents64, dir_fd, buf, sizeof(buf))) > 0) + { + for (pos = 0; pos < nread; pos += de->d_reclen) + { + de = (struct linux_dirent64 *)(buf + pos); - while ((de = readdir(d))) { - glong l; - gchar *e = NULL; + fd = filename_to_fd (de->d_name); + if (fd < 0 || fd == dir_fd) + continue; - if (de->d_name[0] == '.') - continue; - - errno = 0; - l = strtol(de->d_name, &e, 10); - if (errno != 0 || !e || *e) - continue; - - fd = (gint) l; - - if ((glong) fd != l) - continue; - - if (fd == dirfd(d)) - continue; - - if ((res = cb (data, fd)) != 0) - break; + if ((res = cb (data, fd)) != 0) + break; + } } - - closedir(d); + + close (dir_fd); return res; - } + } /* If /proc is not mounted or not accessible we fall back to the old * rlimit trick */ #endif - + #ifdef HAVE_SYS_RESOURCE_H if (getrlimit(RLIMIT_NOFILE, &rl) == 0 && rl.rlim_max != RLIM_INFINITY)