This is the mail archive of the glibc-cvs@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

GNU C Library master sources branch master updated. glibc-2.27.9000-562-g916124e


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU C Library master sources".

The branch, master has been updated
       via  916124ed841745b7a1e0fbc43f9909340b47d373 (commit)
       via  3f5e3f5d066dcffb80af48ae2cf35a01a85a8f10 (commit)
      from  397c54c1afa531242602fe3ac7bb47eff0e909f9 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=916124ed841745b7a1e0fbc43f9909340b47d373

commit 916124ed841745b7a1e0fbc43f9909340b47d373
Author: Florian Weimer <fweimer@redhat.com>
Date:   Fri Jul 6 14:23:15 2018 +0200

    nss_files: Fix re-reading of long lines [BZ #18991]
    
    Use the new __libc_readline_unlocked function to pick up
    reading at the same line in case the buffer needs to be enlarged.

diff --git a/ChangeLog b/ChangeLog
index 51e6bdc..7a052c3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,16 @@
 2018-07-06  Florian Weimer  <fweimer@redhat.com>
 
+	[BZ #18991]
+	* nss/nss_files/files-XXX.c (internal_getent): Use
+	__libc_readline_unlocked.  Seek back to the start of the line if
+	parsing failes with ERANGE.
+	(get_contents_ret, get_contents): Remove.
+	* nss/tst-nss-files-hosts-getent.c: New file.
+	* nss/Makefile (tests): Add tst-nss-files-hosts-getent.
+	(tst-nss-files-hosts-getent): Link with -ldl.
+
+2018-07-06  Florian Weimer  <fweimer@redhat.com>
+
 	* include/stdio.h (__libc_readline_unlocked): Declare.
 	(__ftello64, __fseeko64): Declare aliases.
 	* libio/readline.c: New file.
diff --git a/nss/Makefile b/nss/Makefile
index a5cd2aa..66fac7f 100644
--- a/nss/Makefile
+++ b/nss/Makefile
@@ -64,6 +64,7 @@ xtests			= bug-erange
 ifeq (yes,$(build-shared))
 tests += tst-nss-files-hosts-erange
 tests += tst-nss-files-hosts-multi
+tests += tst-nss-files-hosts-getent
 endif
 
 # If we have a thread library then we can test cancellation against
@@ -169,3 +170,4 @@ endif
 
 $(objpfx)tst-nss-files-hosts-erange: $(libdl)
 $(objpfx)tst-nss-files-hosts-multi: $(libdl)
+$(objpfx)tst-nss-files-hosts-getent: $(libdl)
diff --git a/nss/nss_files/files-XXX.c b/nss/nss_files/files-XXX.c
index df33551..60d1544 100644
--- a/nss/nss_files/files-XXX.c
+++ b/nss/nss_files/files-XXX.c
@@ -128,51 +128,6 @@ CONCAT(_nss_files_end,ENTNAME) (void)
 }
 
 
-typedef enum
-{
-  gcr_ok = 0,
-  gcr_error = -1,
-  gcr_overflow = -2
-} get_contents_ret;
-
-/* Hack around the fact that fgets only accepts int sizes.  */
-static get_contents_ret
-get_contents (char *linebuf, size_t len, FILE *stream)
-{
-  size_t remaining_len = len;
-  char *curbuf = linebuf;
-
-  do
-    {
-      int curlen = ((remaining_len > (size_t) INT_MAX) ? INT_MAX
-		    : remaining_len);
-
-      /* Terminate the line so that we can test for overflow.  */
-      ((unsigned char *) curbuf)[curlen - 1] = 0xff;
-
-      char *p = fgets_unlocked (curbuf, curlen, stream);
-
-      /* EOF or read error.  */
-      if (p == NULL)
-        return gcr_error;
-
-      /* Done reading in the line.  */
-      if (((unsigned char *) curbuf)[curlen - 1] == 0xff)
-        return gcr_ok;
-
-      /* Drop the terminating '\0'.  */
-      remaining_len -= curlen - 1;
-      curbuf += curlen - 1;
-    }
-  /* fgets copies one less than the input length.  Our last iteration is of
-     REMAINING_LEN and once that is done, REMAINING_LEN is decremented by
-     REMAINING_LEN - 1, leaving the result as 1.  */
-  while (remaining_len > 1);
-
-  /* This means that the current buffer was not large enough.  */
-  return gcr_overflow;
-}
-
 /* Parsing the database file into `struct STRUCTURE' data structures.  */
 static enum nss_status
 internal_getent (FILE *stream, struct STRUCTURE *result,
@@ -191,45 +146,69 @@ internal_getent (FILE *stream, struct STRUCTURE *result,
       return NSS_STATUS_TRYAGAIN;
     }
 
-  do
+  while (true)
     {
-      get_contents_ret r = get_contents (data->linebuffer, linebuflen, stream);
-
-      if (r == gcr_error)
+      ssize_t r = __libc_readline_unlocked
+	(stream, data->linebuffer, linebuflen);
+      if (r < 0)
+	{
+	  *errnop = errno;
+	  H_ERRNO_SET (NETDB_INTERNAL);
+	  if (*errnop == ERANGE)
+	    /* Request larger buffer.  */
+	    return NSS_STATUS_TRYAGAIN;
+	  else
+	    /* Other read failure.  */
+	    return NSS_STATUS_UNAVAIL;
+	}
+      else if (r == 0)
 	{
-	  /* End of file or read error.  */
+	  /* End of file.  */
 	  H_ERRNO_SET (HOST_NOT_FOUND);
 	  return NSS_STATUS_NOTFOUND;
 	}
 
-      if (r == gcr_overflow)
+      /* Everything OK.  Now skip leading blanks.  */
+      p = data->linebuffer;
+      while (isspace (*p))
+	++p;
+
+      /* Ignore empty and comment lines.  */
+      if (*p == '\0' || *p == '#')
+	continue;
+
+      /* Parse the line.   */
+      *errnop = EINVAL;
+      parse_result = parse_line (p, result, data, buflen, errnop EXTRA_ARGS);
+
+      if (parse_result == -1)
 	{
-	  /* The line is too long.  Give the user the opportunity to
-	     enlarge the buffer.  */
-	  *errnop = ERANGE;
+	  if (*errnop == ERANGE)
+	    {
+	      /* Return to the original file position at the beginning
+		 of the line, so that the next call can read it again
+		 if necessary.  */
+	      if (__fseeko64 (stream, -r, SEEK_CUR) != 0)
+		{
+		  if (errno == ERANGE)
+		    *errnop = EINVAL;
+		  else
+		    *errnop = errno;
+		  H_ERRNO_SET (NETDB_INTERNAL);
+		  return NSS_STATUS_UNAVAIL;
+		}
+	    }
 	  H_ERRNO_SET (NETDB_INTERNAL);
 	  return NSS_STATUS_TRYAGAIN;
 	}
 
-      /* Everything OK.  Now skip leading blanks.  */
-      p = data->linebuffer;
-      while (isspace (*p))
-	++p;
-    }
-  while (*p == '\0' || *p == '#' /* Ignore empty and comment lines.  */
-	 /* Parse the line.  If it is invalid, loop to get the next
-	    line of the file to parse.  */
-	 || ! (parse_result = parse_line (p, result, data, buflen, errnop
-					  EXTRA_ARGS)));
+      /* Return the data if parsed successfully.  */
+      if (parse_result != 0)
+	return NSS_STATUS_SUCCESS;
 
-  if (__glibc_unlikely (parse_result == -1))
-    {
-      H_ERRNO_SET (NETDB_INTERNAL);
-      return NSS_STATUS_TRYAGAIN;
+      /* If it is invalid, loop to get the next line of the file to
+	 parse.  */
     }
-
-  /* Filled in RESULT with the next entry from the database file.  */
-  return NSS_STATUS_SUCCESS;
 }
 
 
diff --git a/nss/tst-nss-files-hosts-getent.c b/nss/tst-nss-files-hosts-getent.c
new file mode 100644
index 0000000..d7514e8
--- /dev/null
+++ b/nss/tst-nss-files-hosts-getent.c
@@ -0,0 +1,276 @@
+/* Enumerate /etc/hosts with a long line (bug 18991).
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <gnu/lib-names.h>
+#include <netdb.h>
+#include <nss.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <support/check.h>
+#include <support/check_nss.h>
+#include <support/namespace.h>
+#include <support/support.h>
+#include <support/test-driver.h>
+#include <support/xmemstream.h>
+#include <support/xstdio.h>
+#include <support/xunistd.h>
+
+struct support_chroot *chroot_env;
+
+/* Number of alias names in the long line.  This is varied to catch
+   different cases where the ERANGE handling can go wrong (line buffer
+   length, alias buffer).  */
+static int name_count;
+
+/* Write /etc/hosts, from outside of the chroot.  */
+static void
+write_hosts (void)
+{
+  FILE *fp = xfopen (chroot_env->path_hosts, "w");
+  fputs ("127.0.0.1   localhost localhost.localdomain\n", fp);
+  fputs ("192.0.2.2 host2.example.com\n", fp);
+  fputs ("192.0.2.1", fp);
+  for (int i = 0; i < name_count; ++i)
+    fprintf (fp, " host%d.example.com", i);
+  fputs ("\n192.0.2.80 www.example.com\n"
+         "192.0.2.5 host5.example.com\n"
+         "192.0.2.81 www1.example.com\n", fp);
+  xfclose (fp);
+}
+
+const char *host1_expected =
+  "name: localhost\n"
+  "alias: localhost.localdomain\n"
+  "address: 127.0.0.1\n";
+const char *host2_expected =
+  "name: host2.example.com\n"
+  "address: 192.0.2.2\n";
+const char *host4_expected =
+  "name: www.example.com\n"
+  "address: 192.0.2.80\n";
+const char *host5_expected =
+  "name: host5.example.com\n"
+  "address: 192.0.2.5\n";
+const char *host6_expected =
+  "name: www1.example.com\n"
+  "address: 192.0.2.81\n";
+
+static void
+prepare (int argc, char **argv)
+{
+  chroot_env = support_chroot_create
+    ((struct support_chroot_configuration)
+     {
+       .resolv_conf = "",
+       .hosts = "",             /* Filled in by write_hosts.  */
+       .host_conf = "multi on\n",
+     });
+}
+
+/* If -1, no sethostent call.  Otherwise, pass do_stayopen as the
+   sethostent argument.  */
+static int do_stayopen;
+
+/* If non-zero, perform an endostent call.  */
+static int do_endent;
+
+static void
+subprocess_getent (void *closure)
+{
+  xchroot (chroot_env->path_chroot);
+
+  errno = 0;
+  if (do_stayopen >= 0)
+    sethostent (do_stayopen);
+  TEST_VERIFY (errno == 0);
+
+  int i = 0;
+  while (true)
+    {
+      struct xmemstream expected;
+      xopen_memstream (&expected);
+      switch (++i)
+        {
+        case 1:
+          fputs (host1_expected, expected.out);
+          break;
+        case 2:
+          fputs (host2_expected, expected.out);
+          break;
+        case 3:
+          fputs ("name: host0.example.com\n", expected.out);
+          for (int j = 1; j < name_count; ++j)
+            fprintf (expected.out, "alias: host%d.example.com\n", j);
+          fputs ("address: 192.0.2.1\n", expected.out);
+          break;
+        case 4:
+          fputs (host4_expected, expected.out);
+          break;
+        case 5:
+          fputs (host5_expected, expected.out);
+          break;
+        case 6:
+          fputs (host6_expected, expected.out);
+          break;
+        default:
+          fprintf (expected.out, "*** unexpected host %d ***\n", i);
+          break;
+        }
+      xfclose_memstream (&expected);
+      char *context = xasprintf ("do_stayopen=%d host=%d", do_stayopen, i);
+
+      errno = 0;
+      struct hostent *e = gethostent ();
+      if (e == NULL)
+        {
+          TEST_VERIFY (errno == 0);
+          break;
+        }
+      check_hostent (context, e, expected.buffer);
+      free (context);
+      free (expected.buffer);
+    }
+
+  errno = 0;
+  if (do_endent)
+    endhostent ();
+  TEST_VERIFY (errno == 0);
+
+  /* Exercise process termination.   */
+  exit (0);
+}
+
+/* getaddrinfo test.  To be run from a subprocess.  */
+static void
+test_gai (int family)
+{
+  struct addrinfo hints =
+    {
+      .ai_family = family,
+      .ai_protocol = IPPROTO_TCP,
+      .ai_socktype = SOCK_STREAM,
+    };
+
+  struct addrinfo *ai;
+  int ret = getaddrinfo ("host2.example.com", "80", &hints, &ai);
+  check_addrinfo ("host2.example.com", ai, ret,
+                  "address: STREAM/TCP 192.0.2.2 80\n"
+                  "address: STREAM/TCP 192.0.2.1 80\n");
+
+  ret = getaddrinfo ("host5.example.com", "80", &hints, &ai);
+  check_addrinfo ("host5.example.com", ai, ret,
+                  "address: STREAM/TCP 192.0.2.1 80\n"
+                  "address: STREAM/TCP 192.0.2.5 80\n");
+
+  ret = getaddrinfo ("www.example.com", "80", &hints, &ai);
+  check_addrinfo ("www.example.com", ai, ret,
+                  "address: STREAM/TCP 192.0.2.80 80\n");
+
+  ret = getaddrinfo ("www1.example.com", "80", &hints, &ai);
+  check_addrinfo ("www1.example.com", ai, ret,
+                  "address: STREAM/TCP 192.0.2.81 80\n");
+}
+
+/* Subprocess routine for gethostbyname/getaddrinfo testing.  */
+static void
+subprocess_gethost (void *closure)
+{
+  xchroot (chroot_env->path_chroot);
+
+  /* This tests enlarging the read buffer in the multi case.  */
+  struct xmemstream expected;
+  xopen_memstream (&expected);
+  fputs ("name: host2.example.com\n", expected.out);
+  for (int j = 1; j < name_count; ++j)
+    /* NB: host2 is duplicated in the alias list.  */
+    fprintf (expected.out, "alias: host%d.example.com\n", j);
+  fputs ("alias: host0.example.com\n"
+         "address: 192.0.2.2\n"
+         "address: 192.0.2.1\n",
+         expected.out);
+  xfclose_memstream (&expected);
+  check_hostent ("host2.example.com",
+                 gethostbyname ("host2.example.com"),
+                 expected.buffer);
+  free (expected.buffer);
+
+  /* Similarly, but with a different order in the /etc/hosts file.  */
+  xopen_memstream (&expected);
+  fputs ("name: host0.example.com\n", expected.out);
+  for (int j = 1; j < name_count; ++j)
+    fprintf (expected.out, "alias: host%d.example.com\n", j);
+  /* NB: host5 is duplicated in the alias list.  */
+  fputs ("alias: host5.example.com\n"
+         "address: 192.0.2.1\n"
+         "address: 192.0.2.5\n",
+         expected.out);
+  xfclose_memstream (&expected);
+  check_hostent ("host5.example.com",
+                 gethostbyname ("host5.example.com"),
+                 expected.buffer);
+  free (expected.buffer);
+
+  check_hostent ("www.example.com",
+                 gethostbyname ("www.example.com"),
+                 host4_expected);
+  check_hostent ("www1.example.com",
+                 gethostbyname ("www1.example.com"),
+                 host6_expected);
+
+  test_gai (AF_INET);
+  test_gai (AF_UNSPEC);
+}
+
+static int
+do_test (void)
+{
+  support_become_root ();
+  if (!support_can_chroot ())
+    return EXIT_UNSUPPORTED;
+
+  __nss_configure_lookup ("hosts", "files");
+  if (dlopen (LIBNSS_FILES_SO, RTLD_LAZY) == NULL)
+    FAIL_EXIT1 ("could not load " LIBNSS_DNS_SO ": %s", dlerror ());
+
+  /* Each name takes about 20 bytes, so this covers a wide range of
+     buffer sizes, from less than 1000 bytes to about 18000 bytes.  */
+  for (name_count = 40; name_count <= 850; ++name_count)
+    {
+      write_hosts ();
+
+      for (do_stayopen = -1; do_stayopen < 2; ++do_stayopen)
+        for (do_endent = 0; do_endent < 2; ++do_endent)
+          {
+            if (test_verbose > 0)
+              printf ("info: name_count=%d do_stayopen=%d do_endent=%d\n",
+                      name_count, do_stayopen, do_endent);
+            support_isolate_in_subprocess (subprocess_getent, NULL);
+          }
+
+      support_isolate_in_subprocess (subprocess_gethost, NULL);
+    }
+
+  support_chroot_free (chroot_env);
+  return 0;
+}
+
+#define PREPARE prepare
+#include <support/test-driver.c>

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=3f5e3f5d066dcffb80af48ae2cf35a01a85a8f10

commit 3f5e3f5d066dcffb80af48ae2cf35a01a85a8f10
Author: Florian Weimer <fweimer@redhat.com>
Date:   Fri Jul 6 16:53:48 2018 +0200

    libio: Implement internal function __libc_readline_unlocked
    
    This is a variant of fgets which fails with ERANGE if the
    buffer is too small, and the buffer length is given as an
    argument of type size_t.
    
    This function will be useful for implementing NSS file reading
    operations.  Compared to a direct implementation using the public API,
    it avoids an lseek system call in case the line terminator can be
    found in the internal read buffer.

diff --git a/ChangeLog b/ChangeLog
index b8bf887..51e6bdc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+2018-07-06  Florian Weimer  <fweimer@redhat.com>
+
+	* include/stdio.h (__libc_readline_unlocked): Declare.
+	(__ftello64, __fseeko64): Declare aliases.
+	* libio/readline.c: New file.
+	* libio/tst-readline.c: Likewise.
+	(routines): Add readline.
+	(tests-internal): Add tst-readlime.
+	* libio/Versions (GLIBC_PRIVATE): Export __fseeko64, __ftello64,
+	__libc_readline_unlocked.
+	* libio/fseeko.c (__fseeko): Rename from fseeko.
+	(fseeko): Add alias.
+	[__OFF_T_MATCHES_OFF64_T] (fseeko64, __fseeko64): Likewise.
+	* libio/fseeko64.c (__fseeko64): Rename from fseeko64.
+	(fseeko64): Add alias.
+	* libio/ftello.c [__OFF_T_MATCHES_OFF64_T] (__ftello64): Add alias.
+	* libio/ftello64.c (__ftello64): Rename from ftello64.
+	(ftello64): Add alias.
+
 2018-07-06  Szabolcs Nagy  <szabolcs.nagy@arm.com>
 
 	* sysdeps/unix/sysv/linux/aarch64/dl-procinfo.h (HWCAP_IMPORTANT): Add
diff --git a/include/stdio.h b/include/stdio.h
index 3ba0edc..9162d4e 100644
--- a/include/stdio.h
+++ b/include/stdio.h
@@ -127,6 +127,19 @@ extern int __fxprintf (FILE *__fp, const char *__fmt, ...)
 extern int __fxprintf_nocancel (FILE *__fp, const char *__fmt, ...)
      __attribute__ ((__format__ (__printf__, 2, 3))) attribute_hidden;
 
+/* Read the next line from FP into BUFFER, of LENGTH bytes.  LINE will
+   include the line terminator and a NUL terminator.  On success,
+   return the length of the line, including the line terminator, but
+   excluding the NUL termintor.  On EOF, return zero and write a NUL
+   terminator.  On error, return -1 and set errno.  If the total byte
+   count (line and both terminators) exceeds LENGTH, return -1 and set
+   errno to ERANGE (but do not mark the stream as failed).
+
+   The behavior is undefined if FP is not seekable, or if the stream
+   is already in an error state.  */
+ssize_t __libc_readline_unlocked (FILE *fp, char *buffer, size_t length);
+libc_hidden_proto (__libc_readline_unlocked);
+
 extern const char *const _sys_errlist_internal[] attribute_hidden;
 extern int _sys_nerr_internal attribute_hidden;
 
@@ -170,6 +183,10 @@ libc_hidden_proto (fwrite)
 libc_hidden_proto (fseek)
 extern __typeof (ftello) __ftello;
 libc_hidden_proto (__ftello)
+extern __typeof (fseeko64) __fseeko64;
+libc_hidden_proto (__fseeko64)
+extern __typeof (ftello64) __ftello64;
+libc_hidden_proto (__ftello64)
 libc_hidden_proto (fflush)
 libc_hidden_proto (fflush_unlocked)
 extern __typeof (fflush_unlocked) __fflush_unlocked;
diff --git a/libio/Makefile b/libio/Makefile
index 64d283e..cab0eae 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -49,7 +49,7 @@ routines	:=							      \
 	__fbufsize __freading __fwriting __freadable __fwritable __flbf	      \
 	__fpurge __fpending __fsetlocking				      \
 									      \
-	libc_fatal fmemopen oldfmemopen vtables
+	libc_fatal fmemopen oldfmemopen vtables readline
 
 tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc   \
 	tst_wprintf2 tst-widetext test-fmemopen tst-ext tst-ext2 \
@@ -66,7 +66,7 @@ tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc   \
 	tst-fwrite-error tst-ftell-partial-wide tst-ftell-active-handler \
 	tst-ftell-append tst-fputws tst-bz22415 tst-fgetc-after-eof
 
-tests-internal = tst-vtables tst-vtables-interposed
+tests-internal = tst-vtables tst-vtables-interposed tst-readline
 
 ifeq (yes,$(build-shared))
 # Add test-fopenloc only if shared library is enabled since it depends on
diff --git a/libio/Versions b/libio/Versions
index 7712334..acb896a 100644
--- a/libio/Versions
+++ b/libio/Versions
@@ -158,5 +158,9 @@ libc {
 
     # Used by NPTL
     _IO_enable_locks;
+
+    __fseeko64;
+    __ftello64;
+    __libc_readline_unlocked;
   }
 }
diff --git a/libio/fseeko.c b/libio/fseeko.c
index 2df0453..4d086c1 100644
--- a/libio/fseeko.c
+++ b/libio/fseeko.c
@@ -24,11 +24,15 @@
    This exception applies to code released by its copyright holders
    in files containing the exception.  */
 
+/* We need to disable the redirect for __fseeko64 for the alias
+   definitions below to work.  */
+#define __fseeko64 __fseeko64_disable
+
 #include "libioP.h"
 #include "stdio.h"
 
 int
-fseeko (FILE *fp, off_t offset, int whence)
+__fseeko (FILE *fp, off_t offset, int whence)
 {
   int result;
   CHECK_FILE (fp, -1);
@@ -37,7 +41,11 @@ fseeko (FILE *fp, off_t offset, int whence)
   _IO_release_lock (fp);
   return result;
 }
+weak_alias (__fseeko, fseeko)
 
 #ifdef __OFF_T_MATCHES_OFF64_T
-weak_alias (fseeko, fseeko64)
+weak_alias (__fseeko, fseeko64)
+# undef __fseeko64
+strong_alias (__fseeko, __fseeko64)
+libc_hidden_ver (__fseeko, __fseeko64)
 #endif
diff --git a/libio/fseeko64.c b/libio/fseeko64.c
index eea6455..1d9bb19 100644
--- a/libio/fseeko64.c
+++ b/libio/fseeko64.c
@@ -32,7 +32,7 @@
 #ifndef __OFF_T_MATCHES_OFF64_T
 
 int
-fseeko64 (FILE *fp, off64_t offset, int whence)
+__fseeko64 (FILE *fp, off64_t offset, int whence)
 {
   int result;
   CHECK_FILE (fp, -1);
@@ -41,5 +41,6 @@ fseeko64 (FILE *fp, off64_t offset, int whence)
   _IO_release_lock (fp);
   return result;
 }
-
+libc_hidden_def (__fseeko64)
+weak_alias (__fseeko64, fseeko64)
 #endif
diff --git a/libio/ftello.c b/libio/ftello.c
index 5405821..5a3caf4 100644
--- a/libio/ftello.c
+++ b/libio/ftello.c
@@ -24,12 +24,15 @@
    This exception applies to code released by its copyright holders
    in files containing the exception.  */
 
+/* We need to disable the redirect for __ftello64 for the alias
+   definitions below to work.  */
+#define __ftello64 __ftello64_disable
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <libioP.h>
 #include <errno.h>
 
-
 off_t
 __ftello (FILE *fp)
 {
@@ -61,4 +64,7 @@ weak_alias (__ftello, ftello)
 
 #ifdef __OFF_T_MATCHES_OFF64_T
 weak_alias (__ftello, ftello64)
+# undef __ftello64
+strong_alias (__ftello, __ftello64)
+libc_hidden_ver (__ftello, __ftello64)
 #endif
diff --git a/libio/ftello64.c b/libio/ftello64.c
index 281667f..d5546e1 100644
--- a/libio/ftello64.c
+++ b/libio/ftello64.c
@@ -32,7 +32,7 @@
 #ifndef __OFF_T_MATCHES_OFF64_T
 
 off64_t
-ftello64 (FILE *fp)
+__ftello64 (FILE *fp)
 {
   off64_t pos;
   CHECK_FILE (fp, -1L);
@@ -52,5 +52,6 @@ ftello64 (FILE *fp)
     }
   return pos;
 }
-
+libc_hidden_def (__ftello64)
+weak_alias (__ftello64, ftello64)
 #endif
diff --git a/libio/readline.c b/libio/readline.c
new file mode 100644
index 0000000..3638459
--- /dev/null
+++ b/libio/readline.c
@@ -0,0 +1,170 @@
+/* fgets with ERANGE error reporting and size_t buffer length.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "libioP.h"
+
+/* Return -1 and set errno to EINVAL if it is ERANGE.  */
+static ssize_t
+fail_no_erange (void)
+{
+  if (errno == ERANGE)
+    __set_errno (EINVAL);
+  return -1;
+}
+
+/* Slow path for reading the line.  Called with no data in the stream
+   read buffer.  Write data to [BUFFER, BUFFER_END).  */
+static ssize_t
+readline_slow (FILE *fp, char *buffer, char *buffer_end)
+{
+  char *start = buffer;
+
+  while (buffer < buffer_end)
+    {
+      if (__underflow (fp) == EOF)
+        {
+          if (_IO_ferror_unlocked (fp))
+            /* If the EOF was caused by a read error, report it.  */
+            return fail_no_erange ();
+          *buffer = '\0';
+          /* Do not include the null terminator.  */
+          return buffer - start;
+        }
+
+      /* __underflow has filled the buffer.  */
+      char *readptr = fp->_IO_read_ptr;
+      ssize_t readlen = fp->_IO_read_end - readptr;
+      /* Make sure that __underflow really has acquired some data.  */
+      assert (readlen > 0);
+      char *pnl = memchr (readptr, '\n', readlen);
+      if (pnl != NULL)
+        {
+          /* We found the terminator.  */
+          size_t line_length = pnl - readptr;
+          if (line_length + 2 > buffer_end - buffer)
+            /* Not enough room in the caller-supplied buffer.  */
+            break;
+          memcpy (buffer, readptr, line_length + 1);
+          buffer[line_length + 1] = '\0';
+          fp->_IO_read_ptr = pnl + 1;
+          /* Do not include the null terminator.  */
+          return buffer - start + line_length + 1;
+        }
+
+      if (readlen >= buffer_end - buffer)
+        /* Not enough room in the caller-supplied buffer.  */
+        break;
+
+      /* Save and consume the stream buffer.  */
+      memcpy (buffer, readptr, readlen);
+      fp->_IO_read_ptr = fp->_IO_read_end;
+      buffer += readlen;
+    }
+
+  /* The line does not fit into the buffer.  */
+  __set_errno (ERANGE);
+  return -1;
+}
+
+ssize_t
+__libc_readline_unlocked (FILE *fp, char *buffer, size_t buffer_length)
+{
+  char *buffer_end = buffer + buffer_length;
+
+  /* Orient the stream.  */
+  if (__builtin_expect (fp->_mode, -1) == 0)
+    _IO_fwide (fp, -1);
+
+  /* Fast path: The line terminator is found in the buffer.  */
+  char *readptr = fp->_IO_read_ptr;
+  ssize_t readlen = fp->_IO_read_end - readptr;
+  off64_t start_offset;         /* File offset before reading anything.  */
+  if (readlen > 0)
+    {
+      char *pnl = memchr (readptr, '\n', readlen);
+      if (pnl != NULL)
+        {
+          size_t line_length = pnl - readptr;
+          /* Account for line and null terminators.  */
+          if (line_length + 2 > buffer_length)
+            {
+              __set_errno (ERANGE);
+              return -1;
+            }
+          memcpy (buffer, readptr, line_length + 1);
+          buffer[line_length + 1] = '\0';
+          /* Consume the entire line.  */
+          fp->_IO_read_ptr = pnl + 1;
+          return line_length + 1;
+        }
+
+      /* If the buffer does not have enough space for what is pending
+         in the stream (plus a NUL terminator), the buffer is too
+         small.  */
+      if (readlen + 1 > buffer_length)
+        {
+          __set_errno (ERANGE);
+          return -1;
+        }
+
+      /* End of line not found.  We need all the buffered data.  Fall
+         through to the slow path.  */
+      memcpy (buffer, readptr, readlen);
+      buffer += readlen;
+      /* The original length is invalid after this point.  Use
+         buffer_end instead.  */
+#pragma GCC poison buffer_length
+      /* Read the old offset before updating the read pointer.  */
+      start_offset = __ftello64 (fp);
+      fp->_IO_read_ptr = fp->_IO_read_end;
+    }
+  else
+    {
+      readlen = 0;
+      start_offset = __ftello64 (fp);
+    }
+
+  /* Slow path: Read more data from the underlying file.  We need to
+     restore the file pointer if the buffer is too small.  First,
+     check if the __ftello64 call above failed.  */
+  if (start_offset < 0)
+    return fail_no_erange ();
+
+  ssize_t result = readline_slow (fp, buffer, buffer_end);
+  if (result < 0)
+    {
+      if (errno == ERANGE)
+        {
+          /* Restore the file pointer so that the caller may read the
+             same line again.  */
+          if (__fseeko64 (fp, start_offset, SEEK_SET) < 0)
+            return fail_no_erange ();
+          __set_errno (ERANGE);
+        }
+      /* Do not restore the file position on other errors; it is
+         likely that the __fseeko64 call would fail, too.  */
+      return -1;
+    }
+  return readlen + result;
+}
+libc_hidden_def (__libc_readline_unlocked)
diff --git a/libio/tst-readline.c b/libio/tst-readline.c
new file mode 100644
index 0000000..60c58c6
--- /dev/null
+++ b/libio/tst-readline.c
@@ -0,0 +1,236 @@
+/* Test the __libc_readline_unlocked function.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* Exercise __libc_readline_unlocked with various combinations of line
+   lengths, stdio buffer sizes, and line read buffer sizes.  */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/test-driver.h>
+#include <support/xmemstream.h>
+#include <support/xstdio.h>
+#include <support/xunistd.h>
+
+enum
+  {
+    maximum_line_length = 7,
+    number_of_lines = 3,
+  };
+
+/* -1: Do not set buffer size.  0: unbuffered.  Otherwise, use this as
+   the size of the buffer.  */
+static int buffer_size;
+
+/* These size of the buffer used for reading.  Must be at least 2.  */
+static int read_size;
+
+/* If a read files with ERANGE, increase the buffer size by this
+   amount.  Must be positive.  */
+static int read_size_increment;
+
+/* If non-zero, do not reset the read size after an ERANGE error.  */
+static int read_size_preserve;
+
+/* If non-zero, no '\n' at the end of the file.  */
+static int no_newline_at_eof;
+
+/* Length of the line, or -1 if the line is not present.  */
+static int line_lengths[number_of_lines];
+
+/* The name of the test file.  */
+static char *test_file_path;
+
+/* The contents of the test file.  */
+static char expected_contents[(maximum_line_length + 2) * number_of_lines + 1];
+static size_t expected_length;
+
+/* Returns a random byte which is not zero or the line terminator.  */
+static char
+random_char (void)
+{
+  static unsigned int rand_state = 1;
+  while (true)
+    {
+      char result = rand_r (&rand_state) >> 16;
+      if (result != 0 && result != '\n')
+        return result;
+    }
+}
+
+/* Create the test file.  */
+static void
+prepare (int argc, char **argv)
+{
+  int fd = create_temp_file ("tst-readline-", &test_file_path);
+  TEST_VERIFY_EXIT (fd >= 0);
+  xclose (fd);
+}
+
+/* Prepare the test file.  Return false if the test parameters are
+   incongruent and the test should be skipped.  */
+static bool
+write_test_file (void)
+{
+  expected_length = 0;
+  char *p = expected_contents;
+  for (int lineno = 0; lineno < number_of_lines; ++lineno)
+    for (int i = 0; i < line_lengths[lineno]; ++i)
+      *p++ = random_char ();
+  expected_length = p - &expected_contents[0];
+  if (no_newline_at_eof)
+    {
+      if (expected_length == 0)
+        return false;
+      --expected_length;
+      --p;
+    }
+  if (test_verbose > 0)
+    {
+      printf ("info: writing test file of %zu bytes:\n", expected_length);
+      for (int i = 0; i < number_of_lines; ++i)
+        printf (" line %d: %d\n", i, line_lengths[i]);
+      if (no_newline_at_eof)
+        puts ("  (no newline at EOF)");
+    }
+  TEST_VERIFY_EXIT (expected_length < sizeof (expected_contents));
+  *p++ = '\0';
+  support_write_file_string (test_file_path, expected_contents);
+  return true;
+}
+
+/* Run a single test (a combination of a test file and read
+   parameters).  */
+static void
+run_test (void)
+{
+  TEST_VERIFY_EXIT (read_size_increment > 0);
+  if (test_verbose > 0)
+    {
+      printf ("info: running test: buffer_size=%d read_size=%d\n"
+              "  read_size_increment=%d read_size_preserve=%d\n",
+              buffer_size, read_size, read_size_increment, read_size_preserve);
+    }
+
+  struct xmemstream result;
+  xopen_memstream (&result);
+
+  FILE *fp = xfopen (test_file_path, "rce");
+  char *fp_buffer = NULL;
+  if (buffer_size == 0)
+    TEST_VERIFY_EXIT (setvbuf (fp, NULL, _IONBF, 0) == 0);
+  if (buffer_size > 0)
+    {
+      fp_buffer = xmalloc (buffer_size);
+      TEST_VERIFY_EXIT (setvbuf (fp, fp_buffer, _IOFBF, buffer_size) == 0);
+    }
+
+  char *line_buffer = xmalloc (read_size);
+  size_t line_buffer_size = read_size;
+
+  while (true)
+    {
+      ssize_t ret = __libc_readline_unlocked
+        (fp, line_buffer, line_buffer_size);
+      if (ret < 0)
+        {
+          TEST_VERIFY (ret == -1);
+          if (errno != ERANGE)
+            FAIL_EXIT1 ("__libc_readline_unlocked: %m");
+          line_buffer_size += read_size_increment;
+          free (line_buffer);
+          line_buffer = xmalloc (line_buffer_size);
+          /* Try reading this line again.  */
+        }
+      else if (ret == 0)
+        break;
+      else
+        {
+          /* A line has been read.  Save it.  */
+          TEST_VERIFY (ret == strlen (line_buffer));
+          const char *pnl = strchr (line_buffer, '\n');
+          /* If there is a \n, it must be at the end.  */
+          TEST_VERIFY (pnl == NULL || pnl == line_buffer + ret - 1);
+          fputs (line_buffer, result.out);
+
+          /* Restore the original read size if required.  */
+          if (line_buffer_size > read_size && !read_size_preserve)
+            {
+              line_buffer_size = read_size;
+              free (line_buffer);
+              line_buffer = xmalloc (line_buffer_size);
+            }
+        }
+    }
+
+  xfclose (fp);
+  free (fp_buffer);
+  free (line_buffer);
+
+  xfclose_memstream (&result);
+  TEST_VERIFY (result.length == expected_length);
+  TEST_VERIFY (strcmp (result.buffer, expected_contents) == 0);
+  if (test_verbose > 0)
+    {
+      printf ("info: expected (%zu): [[%s]]\n",
+              expected_length, expected_contents);
+      printf ("info:   actual (%zu): [[%s]]\n", result.length, result.buffer);
+    }
+  free (result.buffer);
+}
+
+/* Test one test file with multiple read parameters.  */
+static void
+test_one_file (void)
+{
+  for (buffer_size = -1; buffer_size <= maximum_line_length + 1; ++buffer_size)
+    for (read_size = 2; read_size <= maximum_line_length + 2; ++read_size)
+      for (read_size_increment = 1; read_size_increment <= 4;
+           ++read_size_increment)
+        for (read_size_preserve = 0; read_size_preserve < 2;
+             ++read_size_preserve)
+          run_test ();
+}
+
+
+static int
+do_test (void)
+{
+  /* Set up the test file contents.  */
+  for (line_lengths[0] = -1; line_lengths[0] <= maximum_line_length;
+       ++line_lengths[0])
+    for (line_lengths[1] = -1; line_lengths[1] <= maximum_line_length;
+         ++line_lengths[1])
+      for (line_lengths[2] = -1; line_lengths[2] <= maximum_line_length;
+           ++line_lengths[2])
+        for (no_newline_at_eof = 0; no_newline_at_eof < 2; ++no_newline_at_eof)
+          {
+            if (!write_test_file ())
+              continue;
+            test_one_file ();
+          }
+  free (test_file_path);
+  return 0;
+}
+
+#define PREPARE prepare
+#include <support/test-driver.c>

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                        |   30 ++++
 include/stdio.h                  |   17 +++
 libio/Makefile                   |    4 +-
 libio/Versions                   |    4 +
 libio/fseeko.c                   |   12 ++-
 libio/fseeko64.c                 |    5 +-
 libio/ftello.c                   |    8 +-
 libio/ftello64.c                 |    5 +-
 libio/readline.c                 |  170 +++++++++++++++++++++++
 libio/tst-readline.c             |  236 ++++++++++++++++++++++++++++++++
 nss/Makefile                     |    2 +
 nss/nss_files/files-XXX.c        |  121 +++++++----------
 nss/tst-nss-files-hosts-getent.c |  276 ++++++++++++++++++++++++++++++++++++++
 13 files changed, 810 insertions(+), 80 deletions(-)
 create mode 100644 libio/readline.c
 create mode 100644 libio/tst-readline.c
 create mode 100644 nss/tst-nss-files-hosts-getent.c


hooks/post-receive
-- 
GNU C Library master sources


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]