LCOV - code coverage report
Current view: top level - common - closefrom.cc (source / functions) Hit Total Coverage
Test: Test Coverage for xapian-core 2463e5ebed6d Lines: 24 40 60.0 %
Date: 2019-03-18 07:48:51 Functions: 1 2 50.0 %
Branches: 17 50 34.0 %

           Branch data     Line data    Source code
       1                 :            : /** @file closefrom.cc
       2                 :            :  * @brief Implementation of closefrom() function.
       3                 :            :  */
       4                 :            : /* Copyright (C) 2010,2011,2012,2016,2018 Olly Betts
       5                 :            :  *
       6                 :            :  * This program is free software; you can redistribute it and/or modify
       7                 :            :  * it under the terms of the GNU General Public License as published by
       8                 :            :  * the Free Software Foundation; either version 2 of the License, or
       9                 :            :  * (at your option) any later version.
      10                 :            :  *
      11                 :            :  * This program is distributed in the hope that it will be useful,
      12                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14                 :            :  * GNU General Public License for more details.
      15                 :            :  *
      16                 :            :  * You should have received a copy of the GNU General Public License
      17                 :            :  * along with this program; if not, write to the Free Software
      18                 :            :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
      19                 :            :  */
      20                 :            : 
      21                 :            : #include <config.h>
      22                 :            : 
      23                 :            : // We don't currently need closefrom() on __WIN32__.
      24                 :            : #if !defined HAVE_CLOSEFROM && !defined __WIN32__
      25                 :            : 
      26                 :            : #include "closefrom.h"
      27                 :            : 
      28                 :            : #include <cerrno>
      29                 :            : #include "safefcntl.h"
      30                 :            : #include "safeunistd.h"
      31                 :            : 
      32                 :            : #ifdef HAVE_SYS_RESOURCE_H
      33                 :            : # include <sys/types.h>
      34                 :            : # include <sys/resource.h>
      35                 :            : #endif
      36                 :            : 
      37                 :            : #if defined __linux__
      38                 :            : # include "alignment_cast.h"
      39                 :            : # include "safedirent.h"
      40                 :            : # include <cstdlib>
      41                 :            : #elif defined __APPLE__
      42                 :            : # include <sys/attr.h>
      43                 :            : # include <cstdlib>
      44                 :            : # include <cstring>
      45                 :            : #endif
      46                 :            : 
      47                 :            : using namespace std;
      48                 :            : 
      49                 :            : static int
      50                 :          0 : get_maxfd() {
      51                 :            : #ifdef F_MAXFD
      52                 :            :     // May only be supported by NetBSD, modern versions of which implement
      53                 :            :     // closefrom().  Leave this in so that if other platforms have it or add
      54                 :            :     // it they will benefit.
      55                 :            :     int maxfd = fcntl(0, F_MAXFD);
      56                 :            :     if (maxfd >= 0) return maxfd;
      57                 :            : #endif
      58                 :            : #ifdef HAVE_GETRLIMIT
      59                 :            :     struct rlimit rl;
      60 [ #  # ][ #  # ]:          0 :     if (getrlimit(RLIMIT_NOFILE, &rl) == 0 &&
                 [ #  # ]
      61                 :          0 :         rl.rlim_max != RLIM_INFINITY) {
      62                 :          0 :         return static_cast<int>(rl.rlim_max) - 1;
      63                 :            :     }
      64                 :            : #endif
      65                 :          0 :     return static_cast<int>(sysconf(_SC_OPEN_MAX)) - 1;
      66                 :            : }
      67                 :            : 
      68                 :            : // These platforms are known to provide closefrom():
      69                 :            : // FreeBSD >= 8.0, NetBSD >= 3.0, OpenBSD >= 3.5, Solaris >= 9
      70                 :            : //
      71                 :            : // These platforms are known to support fcntl() with F_CLOSEM:
      72                 :            : // AIX, IRIX, NetBSD >= 2.0
      73                 :            : //
      74                 :            : // These platforms have getdirentries() and a "magic" directory with an entry
      75                 :            : // for each FD open in the current process:
      76                 :            : // Linux (at least with glibc)
      77                 :            : //
      78                 :            : // These platforms have getdirentriesattr() and a "magic" directory with an
      79                 :            : // entry for each FD open in the current process:
      80                 :            : // OS X
      81                 :            : //
      82                 :            : // Other platforms just use a loop up to a limit obtained from
      83                 :            : // fcntl(0, F_MAXFD), getrlimit(RLIMIT_NOFILE, ...), or sysconf(_SC_OPEN_MAX)
      84                 :            : // - known examples:
      85                 :            : // Android (bionic libc doesn't provide getdirentries())
      86                 :            : 
      87                 :            : void
      88                 :        699 : Xapian::Internal::closefrom(int fd)
      89                 :            : {
      90                 :        699 :     int maxfd = -1;
      91                 :            : #ifdef F_CLOSEM
      92                 :            :     if (fcntl(fd, F_CLOSEM, 0) >= 0)
      93                 :            :         return;
      94                 :            : #elif defined HAVE_GETDIRENTRIES && defined __linux__
      95                 :        699 :     const char * path = "/proc/self/fd";
      96                 :        699 :     int dir = open(path, O_RDONLY|O_DIRECTORY);
      97         [ +  - ]:        699 :     if (dir >= 0) {
      98                 :        699 :         off_t base = 0;
      99                 :            :         while (true) {
     100                 :            :             char buf[1024];
     101                 :       1398 :             errno = 0;
     102                 :            :             // We use getdirentries() instead of opendir()/readdir() here
     103                 :            :             // because the latter can call malloc(), which isn't safe to do
     104                 :            :             // between fork() and exec() in a multi-threaded program.
     105                 :       1398 :             ssize_t c = getdirentries(dir, buf, sizeof(buf), &base);
     106         [ +  + ]:       1398 :             if (c == 0) {
     107         [ +  - ]:        699 :                 close(dir);
     108                 :        699 :                 return;
     109                 :            :             }
     110         [ -  + ]:        699 :             if (c < 0) {
     111                 :            :                 // Fallback if getdirentries() fails.
     112                 :          0 :                 break;
     113                 :            :             }
     114                 :            :             struct dirent *d;
     115         [ +  + ]:       5976 :             for (ssize_t pos = 0; pos < c; pos += d->d_reclen) {
     116                 :       5277 :                 d = alignment_cast<struct dirent*>(buf + pos);
     117                 :       5277 :                 const char * leaf = d->d_name;
     118 [ +  + ][ -  + ]:       5277 :                 if (leaf[0] < '0' || leaf[0] > '9') {
     119                 :            :                     // Skip '.' and '..'.
     120                 :       1398 :                     continue;
     121                 :            :                 }
     122                 :       3879 :                 int n = atoi(leaf);
     123         [ +  + ]:       3879 :                 if (n < fd) {
     124                 :            :                     // FD below threshold.
     125                 :       2801 :                     continue;
     126                 :            :                 }
     127         [ -  + ]:       1078 :                 if (n == dir) {
     128                 :            :                     // Don't close the fd open on the directory.
     129                 :          0 :                     continue;
     130                 :            :                 }
     131                 :            : 
     132                 :            :                 // Running under valgrind causes some entries above the
     133                 :            :                 // reported RLIMIT_NOFILE value to appear in
     134                 :            :                 // /proc/self/fd - see:
     135                 :            :                 // https://bugs.kde.org/show_bug.cgi?id=191758
     136                 :            :                 //
     137                 :            :                 // If we try to close these, valgrind issues a warning about
     138                 :            :                 // trying to close an invalid file descriptor.  These entries
     139                 :            :                 // start at 1024, so we check that value first so we can
     140                 :            :                 // usually avoid having to read the fd limit when we're not
     141                 :            :                 // running under valgrind.
     142         [ -  + ]:       1078 :                 if (n >= 1024) {
     143         [ #  # ]:          0 :                     if (maxfd < 0)
     144                 :          0 :                         maxfd = get_maxfd();
     145         [ #  # ]:          0 :                     if (n > maxfd)
     146                 :          0 :                         continue;
     147                 :            :                 }
     148                 :            : 
     149                 :            :                 // Retry on EINTR.
     150 [ +  - ][ -  + ]:       1078 :                 while (close(n) < 0 && errno == EINTR) { }
         [ #  # ][ -  + ]
     151                 :            :             }
     152                 :            :         }
     153         [ #  # ]:        699 :         close(dir);
     154                 :            :     }
     155                 :            : #elif defined __APPLE__ // Mac OS X
     156                 :            :     const char * path = "/dev/fd";
     157                 :            : #ifdef __LP64__
     158                 :            :     typedef unsigned int gdea_type;
     159                 :            : #else
     160                 :            :     typedef unsigned long gdea_type;
     161                 :            : #endif
     162                 :            :     int dir = open(path, O_RDONLY|O_DIRECTORY);
     163                 :            :     if (dir >= 0) {
     164                 :            :         gdea_type base = 0;
     165                 :            :         struct attrlist alist;
     166                 :            :         memset(&alist, 0, sizeof(alist));
     167                 :            :         alist.bitmapcount = ATTR_BIT_MAP_COUNT;
     168                 :            :         alist.commonattr = ATTR_CMN_NAME;
     169                 :            :         while (true) {
     170                 :            :             char buf[1024];
     171                 :            :             errno = 0;
     172                 :            :             // We use getdirentriesattr() instead of opendir()/readdir() here
     173                 :            :             // because the latter can call malloc(), which isn't safe to do
     174                 :            :             // between fork() and exec() in a multi-threaded program.  We only
     175                 :            :             // want filename, but can't use getdirentries() because it's not
     176                 :            :             // available with 64-bit inode_t, which seems to be tied to LFS.
     177                 :            :             gdea_type count = sizeof(buf);
     178                 :            :             gdea_type new_state;
     179                 :            :             int r = getdirentriesattr(dir, &alist, buf, sizeof(buf),
     180                 :            :                                       &count, &base, &new_state, 0);
     181                 :            :             (void)new_state;
     182                 :            :             if (r < 0) {
     183                 :            :                 // Fallback if getdirentriesattr() fails.
     184                 :            :                 break;
     185                 :            :             }
     186                 :            :             char * p = buf;
     187                 :            :             while (count-- > 0) {
     188                 :            :                 const char * leaf = p + sizeof(u_int32_t);
     189                 :            :                 p += *static_cast<u_int32_t*>(static_cast<void*>(p));
     190                 :            : 
     191                 :            :                 if (leaf[0] < '0' || leaf[0] > '9') {
     192                 :            :                     // Skip '.' and '..'.
     193                 :            :                     continue;
     194                 :            :                 }
     195                 :            :                 int n = atoi(leaf);
     196                 :            :                 if (n < fd) {
     197                 :            :                     // FD below threshold.
     198                 :            :                     continue;
     199                 :            :                 }
     200                 :            :                 if (n == dir) {
     201                 :            :                     // Don't close the fd open on the directory.
     202                 :            :                     continue;
     203                 :            :                 }
     204                 :            : 
     205                 :            :                 // Retry on EINTR.
     206                 :            :                 while (close(n) < 0 && errno == EINTR) { }
     207                 :            :             }
     208                 :            :             if (r == 1) {
     209                 :            :                 // We've had the last entry.
     210                 :            :                 close(dir);
     211                 :            :                 return;
     212                 :            :             }
     213                 :            :         }
     214                 :            :         close(dir);
     215                 :            :     }
     216                 :            : #elif 0
     217                 :            :     // Some platforms have /proc/<pid>/fd but not /proc/self - if any such
     218                 :            :     // platforms don't have either closefrom() or F_CLOSEM but do have
     219                 :            :     // getdirentries() then this code can be used.  AIX is an example of
     220                 :            :     // a platform of the former, but apparently has F_CLOSEM.
     221                 :            :     char path[6 + sizeof(pid_t) * 3 + 4];
     222                 :            :     sprintf(path, "/proc/%ld/fd", long(getpid()));
     223                 :            : #endif
     224         [ #  # ]:          0 :     if (maxfd < 0)
     225                 :          0 :         maxfd = get_maxfd();
     226         [ #  # ]:          0 :     while (fd <= maxfd) {
     227                 :            :         // Retry on EINTR; just ignore other errors (we'll get EBADF if fd
     228                 :            :         // isn't open so that's OK).
     229 [ #  # ][ #  # ]:          0 :         while (close(fd) < 0 && errno == EINTR) { }
                 [ #  # ]
     230                 :          0 :         ++fd;
     231                 :            :     }
     232                 :            : }
     233                 :            : 
     234                 :            : #endif

Generated by: LCOV version 1.11