0a6a4f
commit 61d7788b83b302207a67b82786f4fd79e3538f30
0a6a4f
Author: Andreas Gruenbacher <agruen@gnu.org>
0a6a4f
Date:   Thu Jun 27 11:10:43 2019 +0200
0a6a4f
0a6a4f
    Don't crash when RLIMIT_NOFILE is set to RLIM_INFINITY
0a6a4f
    
0a6a4f
    * src/safe.c (min_cached_fds): Define minimum number of cached dir file
0a6a4f
    descriptors.
0a6a4f
    (max_cached_fds): Change type to rlim_t to allow storing RLIM_INFINITY.
0a6a4f
    (init_dirfd_cache): Set max_cached_fds to RLIM_INFINITY when RLIMIT_NOFILE is
0a6a4f
    RLIM_INFINITY.  Set the initial hash table size to min_cached_fds, independent
0a6a4f
    of RLIMIT_NOFILE: patches commonly only affect one or a few files, so a small
0a6a4f
    hash table will usually suffice; if needed, the hash table will grow.
0a6a4f
    (insert_cached_dirfd): Don't shrink the cache when max_cached_fds is
0a6a4f
    RLIM_INFINITY.
0a6a4f
0a6a4f
diff --git a/src/safe.c b/src/safe.c
0a6a4f
index 5a7202f..f147b0e 100644
0a6a4f
--- a/src/safe.c
0a6a4f
+++ b/src/safe.c
0a6a4f
@@ -67,7 +67,8 @@ struct cached_dirfd {
0a6a4f
 };
0a6a4f
 
0a6a4f
 static Hash_table *cached_dirfds = NULL;
0a6a4f
-static size_t max_cached_fds;
0a6a4f
+static rlim_t min_cached_fds = 8;
0a6a4f
+static rlim_t max_cached_fds;
0a6a4f
 LIST_HEAD (lru_list);
0a6a4f
 
0a6a4f
 static size_t hash_cached_dirfd (const void *entry, size_t table_size)
0a6a4f
@@ -98,11 +99,17 @@ static void init_dirfd_cache (void)
0a6a4f
 {
0a6a4f
   struct rlimit nofile;
0a6a4f
 
0a6a4f
-  max_cached_fds = 8;
0a6a4f
   if (getrlimit (RLIMIT_NOFILE, &nofile) == 0)
0a6a4f
-    max_cached_fds = MAX (nofile.rlim_cur / 4, max_cached_fds);
0a6a4f
+    {
0a6a4f
+      if (nofile.rlim_cur == RLIM_INFINITY)
0a6a4f
+        max_cached_fds = RLIM_INFINITY;
0a6a4f
+      else
0a6a4f
+	max_cached_fds = MAX (nofile.rlim_cur / 4, min_cached_fds);
0a6a4f
+    }
0a6a4f
+  else
0a6a4f
+    max_cached_fds = min_cached_fds;
0a6a4f
 
0a6a4f
-  cached_dirfds = hash_initialize (max_cached_fds,
0a6a4f
+  cached_dirfds = hash_initialize (min_cached_fds,
0a6a4f
 				   NULL,
0a6a4f
 				   hash_cached_dirfd,
0a6a4f
 				   compare_cached_dirfds,
0a6a4f
@@ -148,20 +155,23 @@ static void insert_cached_dirfd (struct cached_dirfd *entry, int keepfd)
0a6a4f
   if (cached_dirfds == NULL)
0a6a4f
     init_dirfd_cache ();
0a6a4f
 
0a6a4f
-  /* Trim off the least recently used entries */
0a6a4f
-  while (hash_get_n_entries (cached_dirfds) >= max_cached_fds)
0a6a4f
+  if (max_cached_fds != RLIM_INFINITY)
0a6a4f
     {
0a6a4f
-      struct cached_dirfd *last =
0a6a4f
-	list_entry (lru_list.prev, struct cached_dirfd, lru_link);
0a6a4f
-      if (&last->lru_link == &lru_list)
0a6a4f
-	break;
0a6a4f
-      if (last->fd == keepfd)
0a6a4f
+      /* Trim off the least recently used entries */
0a6a4f
+      while (hash_get_n_entries (cached_dirfds) >= max_cached_fds)
0a6a4f
 	{
0a6a4f
-	  last = list_entry (last->lru_link.prev, struct cached_dirfd, lru_link);
0a6a4f
+	  struct cached_dirfd *last =
0a6a4f
+	    list_entry (lru_list.prev, struct cached_dirfd, lru_link);
0a6a4f
 	  if (&last->lru_link == &lru_list)
0a6a4f
 	    break;
0a6a4f
+	  if (last->fd == keepfd)
0a6a4f
+	    {
0a6a4f
+	      last = list_entry (last->lru_link.prev, struct cached_dirfd, lru_link);
0a6a4f
+	      if (&last->lru_link == &lru_list)
0a6a4f
+		break;
0a6a4f
+	    }
0a6a4f
+	  remove_cached_dirfd (last);
0a6a4f
 	}
0a6a4f
-      remove_cached_dirfd (last);
0a6a4f
     }
0a6a4f
 
0a6a4f
   /* Only insert if the parent still exists. */