diff options
| author | Colin Watson <[email protected]> | 2017-09-16 03:16:45 +0000 | 
|---|---|---|
| committer | Werner Koch <[email protected]> | 2017-10-04 15:26:39 +0000 | 
| commit | b5b996b1a142abb90296f5feadf0b5b19c59f738 (patch) | |
| tree | bae96d5b1fea3c7f799bd36dd4d7a4c850a0abdd | |
| parent | Register DCO for Colin Watson. (diff) | |
| download | gpgme-b5b996b1a142abb90296f5feadf0b5b19c59f738.tar.gz gpgme-b5b996b1a142abb90296f5feadf0b5b19c59f738.zip | |
core: Restore get_max_fds optimization on Linux
* src/posix-io.c (get_max_fds): Restore Linux optimization, this time
using open/getdents/close rather than opendir/readdir/closedir.
--
opendir/readdir/closedir may allocate/free memory, and aren't required
to do so in an async-signal-safe way.  On the other hand, opening
/proc/self/fd directly and iterating over it using getdents is safe.
(getdents is not strictly speaking documented to be async-signal-safe
because it's not in POSIX.  However, the Linux implementation is
essentially just a souped-up read.  Python >= 3.2.3 makes the same
assumption.)
Signed-off-by: Colin Watson <[email protected]>
| -rw-r--r-- | src/posix-io.c | 99 | 
1 files changed, 66 insertions, 33 deletions
| diff --git a/src/posix-io.c b/src/posix-io.c index 14856df1..42677138 100644 --- a/src/posix-io.c +++ b/src/posix-io.c @@ -48,6 +48,7 @@  #include <sys/resource.h>  #if __linux__ +# include <sys/syscall.h>  # include <sys/types.h>  # include <dirent.h>  #endif /*__linux__ */ @@ -279,6 +280,20 @@ _gpgme_io_set_nonblocking (int fd)  } +#ifdef __linux__ +/* This is not declared in public headers; getdents(2) says that we must + * define it ourselves.  */ +struct linux_dirent +{ +  unsigned long d_ino; +  unsigned long d_off; +  unsigned short d_reclen; +  char d_name[]; +}; + +# define DIR_BUF_SIZE 1024 +#endif /* __linux__ */ +  static long int  get_max_fds (void)  { @@ -291,39 +306,57 @@ get_max_fds (void)     * than for example doing 4096 close calls where almost all of them     * will fail.     * -   * Unfortunately we can't call opendir between fork and exec in a -   * multi-threaded process because opendir uses malloc and thus a -   * mutex which may deadlock with a malloc in another thread.  Thus -   * the code is not used until we can have a opendir variant which -   * does not use malloc.  */ -/* #ifdef __linux__ */ -/*   { */ -/*     DIR *dir = NULL; */ -/*     struct dirent *dir_entry; */ -/*     const char *s; */ -/*     int x; */ - -/*     dir = opendir ("/proc/self/fd"); */ -/*     if (dir) */ -/*       { */ -/*         while ((dir_entry = readdir (dir))) */ -/*           { */ -/*             s = dir_entry->d_name; */ -/*             if ( *s < '0' || *s > '9') */ -/*               continue; */ -/*             x = atoi (s); */ -/*             if (x > fds) */ -/*               fds = x; */ -/*           } */ -/*         closedir (dir); */ -/*       } */ -/*     if (fds != -1) */ -/*       { */ -/*         fds++; */ -/*         source = "/proc"; */ -/*       } */ -/*     } */ -/* #endif /\* __linux__ *\/ */ +   * We can't use the normal opendir/readdir/closedir interface between +   * fork and exec in a multi-threaded process because opendir uses +   * malloc and thus a mutex which may deadlock with a malloc in another +   * thread.  However, the underlying getdents system call is safe.  */ +#ifdef __linux__ +  { +    int dir_fd; +    char dir_buf[DIR_BUF_SIZE]; +    struct linux_dirent *dir_entry; +    int r, pos; +    const char *s; +    int x; + +    dir_fd = open ("/proc/self/fd", O_RDONLY | O_DIRECTORY); +    if (dir_fd != -1) +      { +        for (;;) +          { +            r = syscall(SYS_getdents, dir_fd, dir_buf, DIR_BUF_SIZE); +            if (r == -1) +              { +                /* Fall back to other methods.  */ +                fds = -1; +                break; +              } +            if (r == 0) +              break; + +            for (pos = 0; pos < r; pos += dir_entry->d_reclen) +              { +                dir_entry = (struct linux_dirent *) (dir_buf + pos); +                s = dir_entry->d_name; +                if (*s < '0' || *s > '9') +                  continue; +                /* atoi is not guaranteed to be async-signal-safe.  */ +                for (x = 0; *s >= '0' && *s <= '9'; s++) +                  x = x * 10 + (*s - '0'); +                if (!*s && x > fds && x != dir_fd) +                  fds = x; +              } +          } + +        close (dir_fd); +      } +    if (fds != -1) +      { +        fds++; +        source = "/proc"; +      } +    } +#endif /* __linux__ */  #ifdef RLIMIT_NOFILE    if (fds == -1) | 
