I did this:
> 2017-09-26  Bruno Haible  <br...@clisp.org>
> 
>       vma-iter: Improvements for Linux and BSD platforms.
>       - Add support for DragonFly BSD.
>       - Make it more reliable on Linux, GNU/kFreeBSD, FreeBSD, NetBSD.
>       * lib/vma-iter.c (struct rofile, rof_open, rof_peekchar, rof_close):
>       Read the entire file into memory in a single system call.
>       (vma_iterate): Update. Read from /proc on DragonFly BSD like on FreeBSD.
>       * lib/vma-iter.h (VMA_ITERATE_SUPPORTED): Define also on DragonFly BSD.

But on Linux (and not on the BSD systems), this has the effect that
vma_iterate() produces a truncated list of VMAs. This fixes it.


2017-10-07  Bruno Haible  <br...@clisp.org>

        vma-iter: Fix truncated result on Linux (regression from 2017-09-26).
        * lib/vma-iter.c (MIN_LEFTOVER): New macro.
        (STACK_ALLOCATED_BUFFER_SIZE): Set to a minimal value if not needed.
        (rof_open): On Linux, do multiple read() calls and make sure
        MIN_LEFTOVER bytes are left when read() returns.

>From 7f56e496c731f8da732d5a3cc2afddad86cda5be Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Sat, 7 Oct 2017 14:07:41 +0200
Subject: [PATCH] vma-iter: Fix truncated result on Linux (regression from
 2017-09-26).

* lib/vma-iter.c (MIN_LEFTOVER): New macro.
(STACK_ALLOCATED_BUFFER_SIZE): Set to a minimal value if not needed.
(rof_open): On Linux, do multiple read() calls and make sure
MIN_LEFTOVER bytes are left when read() returns.
---
 ChangeLog      |  8 ++++++
 lib/vma-iter.c | 87 ++++++++++++++++++++++++++++++++++++++++++++--------------
 2 files changed, 74 insertions(+), 21 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 894c3c6..74d1d1e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
 2017-10-07  Bruno Haible  <br...@clisp.org>
 
+	vma-iter: Fix truncated result on Linux (regression from 2017-09-26).
+	* lib/vma-iter.c (MIN_LEFTOVER): New macro.
+	(STACK_ALLOCATED_BUFFER_SIZE): Set to a minimal value if not needed.
+	(rof_open): On Linux, do multiple read() calls and make sure
+	MIN_LEFTOVER bytes are left when read() returns.
+
+2017-10-07  Bruno Haible  <br...@clisp.org>
+
 	vma-iter: Improve support for GNU/Hurd.
 	* lib/vma-iter.c (vma_iterate): On GNU/Hurd, use the Mach vm_region()
 	API, not the /proc file system.
diff --git a/lib/vma-iter.c b/lib/vma-iter.c
index 0fb8834..2666940 100644
--- a/lib/vma-iter.c
+++ b/lib/vma-iter.c
@@ -34,6 +34,10 @@
 #include <fcntl.h> /* open, O_RDONLY */
 #include <unistd.h> /* getpagesize, lseek, read, close, getpid */
 
+#if defined __linux__
+# include <limits.h> /* PATH_MAX */
+#endif
+
 #if defined __linux__ || defined __FreeBSD_kernel__ || defined __FreeBSD__ || defined __DragonFly__ || defined __NetBSD__ /* || defined __CYGWIN__ */
 # include <sys/types.h>
 # include <sys/mman.h> /* mmap, munmap */
@@ -116,11 +120,27 @@
        VMAs with nonsensical addresses.
    So use mmap(), and ignore the resulting VMA.  */
 
+# ifdef __linux__
+  /* On Linux, if the file does not entirely fit into the buffer, the read()
+     function stops before the line that would come out truncated.  The
+     maximum size of such a line is 73 + PATH_MAX bytes.  To be sure that we
+     have read everything, we must verify that at least that many bytes are
+     left when read() returned.  */
+#  define MIN_LEFTOVER (73 + PATH_MAX)
+# else
+#  define MIN_LEFTOVER 0
+# endif
+
 # ifdef TEST
 /* During testing, we want to run into the hairy cases.  */
 #  define STACK_ALLOCATED_BUFFER_SIZE 32
 # else
-#  define STACK_ALLOCATED_BUFFER_SIZE 1024
+#  if MIN_LEFTOVER < 1024
+#   define STACK_ALLOCATED_BUFFER_SIZE 1024
+#  else
+    /* There is no point in using a stack-allocated buffer if it is too small anyway.  */
+#   define STACK_ALLOCATED_BUFFER_SIZE 1
+#  endif
 # endif
 
 struct rofile
@@ -160,33 +180,58 @@ rof_open (struct rofile *rof, const char *filename)
   for (;;)
     {
       /* Attempt to read the contents in a single system call.  */
-      {
-        int n = read (fd, rof->buffer, size);
-# ifdef EINTR
-        if (n < 0 && errno == EINTR)
-          goto retry;
-# endif
+      if (size > MIN_LEFTOVER)
+        {
+          int n = read (fd, rof->buffer, size);
+          if (n < 0 && errno == EINTR)
+            goto retry;
 # if defined __DragonFly__
-        if (!(n < 0 && errno == EFBIG))
+          if (!(n < 0 && errno == EFBIG))
 # endif
-          {
-            if (n <= 0)
-              /* Empty file.  */
-              goto fail1;
-            if (n < size)
-              {
-                /* The buffer was sufficiently large.  */
-                rof->filled = n;
-                close (fd);
-                return 0;
-              }
-          }
-      }
+            {
+              if (n <= 0)
+                /* Empty file.  */
+                goto fail1;
+              if (n + MIN_LEFTOVER <= size)
+                {
+                  /* The buffer was sufficiently large.  */
+                  rof->filled = n;
+# ifdef __linux__
+                  /* On Linux, the read() call may stop even if the buffer was
+                     large enough.  We need the equivalent of full_read().  */
+                  for (;;)
+                    {
+                      n = read (fd, rof->buffer + rof->filled, size - rof->filled);
+                      if (n < 0 && errno == EINTR)
+                        goto retry;
+                      if (n < 0)
+                        /* Some error.  */
+                        goto fail1;
+                      if (n + MIN_LEFTOVER > size - rof->filled)
+                        /* Allocate a larger buffer.  */
+                        break;
+                      if (n == 0)
+                        {
+                          /* Reached the end of file.  */
+                          close (fd);
+                          return 0;
+                        }
+                      rof->filled += n;
+                    }
+# else
+                  close (fd);
+                  return 0;
+# endif
+                }
+            }
+        }
       /* Allocate a larger buffer.  */
       if (pagesize == 0)
         {
           pagesize = getpagesize ();
           size = pagesize;
+          while (size <= MIN_LEFTOVER)
+            size = 2 * size;
         }
       else
         {
-- 
2.7.4

Reply via email to