]> Savannah Git Hosting - gnulib.git/commitdiff
vma-iter: Fix truncated result on Linux (regression from 2017-09-26).
authorBruno Haible <bruno@clisp.org>
Sat, 7 Oct 2017 12:07:41 +0000 (14:07 +0200)
committerBruno Haible <bruno@clisp.org>
Sat, 7 Oct 2017 12:07:41 +0000 (14:07 +0200)
* 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
lib/vma-iter.c

index 894c3c6c27fc0edb57d5aa316e743ea2a7438f38..74d1d1ec177d85886c08cefae3fe0f75959c25fa 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2017-10-07  Bruno Haible  <bruno@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  <bruno@clisp.org>
 
        vma-iter: Improve support for GNU/Hurd.
index 0fb88343336e4ef3ac7a9c2cc69ac34cf88bc679..26669406493c8402e02e700fe7ccd219747b7b37 100644 (file)
 #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 */
        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
         {