e354a5
commit bdee910e88006ae33dc83ac3d2c0708adb6627d0
e354a5
Author: Florian Weimer <fweimer@redhat.com>
e354a5
Date:   Wed Jul 15 13:41:31 2020 +0200
e354a5
e354a5
    nss: Add __nss_fgetent_r
e354a5
    
e354a5
    And helper functions __nss_readline, __nss_readline_seek,
e354a5
     __nss_parse_line_result.
e354a5
    
e354a5
    This consolidates common code for handling overlong lines and
e354a5
    parse files.  Use the new functionality in internal_getent
e354a5
    in nss/nss_files/files-XXX.c.
e354a5
    
e354a5
    Tested-by: Carlos O'Donell <carlos@redhat.com>
e354a5
    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
e354a5
e354a5
diff -rupN a/include/nss_files.h b/include/nss_files.h
e354a5
--- a/include/nss_files.h	2020-09-14 17:48:49.353699306 -0400
e354a5
+++ b/include/nss_files.h	2020-09-14 17:55:21.856488740 -0400
e354a5
@@ -25,6 +25,28 @@
e354a5
 FILE *__nss_files_fopen (const char *path);
e354a5
 libc_hidden_proto (__nss_files_fopen)
e354a5
 
e354a5
+/* Read a line from FP, storing it BUF.  Strip leading blanks and skip
e354a5
+   comments.  Sets errno and returns error code on failure.  Special
e354a5
+   failure: ERANGE means the buffer is too small.  The function writes
e354a5
+   the original offset to *POFFSET (which can be negative in the case
e354a5
+   of non-seekable input).  */
e354a5
+int __nss_readline (FILE *fp, char *buf, size_t len, off64_t *poffset);
e354a5
+libc_hidden_proto (__nss_readline)
e354a5
+
e354a5
+/* Seek FP to OFFSET.  Sets errno and returns error code on failure.
e354a5
+   On success, sets errno to ERANGE and returns ERANGE (to indicate
e354a5
+   re-reading of the same input line to the caller).  If OFFSET is
e354a5
+   negative, fail with ESPIPE without seeking.  Intended to be used
e354a5
+   after parsing data read by __nss_readline failed with ERANGE.  */
e354a5
+int __nss_readline_seek (FILE *fp, off64_t offset) attribute_hidden;
e354a5
+
e354a5
+/* Handles the result of a parse_line call (as defined by
e354a5
+   nss/nss_files/files-parse.c).  Adjusts the file offset of FP as
e354a5
+   necessary.  Returns 0 on success, and updates errno on failure (and
e354a5
+   returns that error code).  */
e354a5
+int __nss_parse_line_result (FILE *fp, off64_t offset, int parse_line_result);
e354a5
+libc_hidden_proto (__nss_parse_line_result)
e354a5
+
e354a5
 struct parser_data;
e354a5
 
e354a5
 /* Instances of the parse_line function from
e354a5
@@ -52,4 +74,11 @@ libnss_files_hidden_proto (_nss_files_pa
e354a5
 libc_hidden_proto (_nss_files_parse_sgent)
e354a5
 libc_hidden_proto (_nss_files_parse_spent)
e354a5
 
e354a5
+/* Generic implementation of fget*ent_r.  Reads lines from FP until
e354a5
+   EOF or a successful parse into *RESULT using PARSER.  Returns 0 on
e354a5
+   success, ENOENT on EOF, ERANGE on too-small buffer.  */
e354a5
+int __nss_fgetent_r (FILE *fp, void *result,
e354a5
+                     char *buffer, size_t buffer_length,
e354a5
+                     nss_files_parse_line parser) attribute_hidden;
e354a5
+
e354a5
 #endif /* _NSS_FILES_H */
e354a5
diff -rupN a/nss/Makefile b/nss/Makefile
e354a5
--- a/nss/Makefile	2020-09-14 17:48:49.293697045 -0400
e354a5
+++ b/nss/Makefile	2020-09-14 17:55:21.860488891 -0400
e354a5
@@ -28,7 +28,9 @@ headers			:= nss.h
e354a5
 routines		= nsswitch getnssent getnssent_r digits_dots \
e354a5
 			  valid_field valid_list_field rewrite_field \
e354a5
 			  $(addsuffix -lookup,$(databases)) \
e354a5
-			  compat-lookup nss_hash nss_files_fopen
e354a5
+			  compat-lookup nss_hash nss_files_fopen \
e354a5
+			  nss_readline nss_parse_line_result \
e354a5
+			  nss_fgetent_r
e354a5
 
e354a5
 # These are the databases that go through nss dispatch.
e354a5
 # Caution: if you add a database here, you must add its real name
e354a5
diff -rupN a/nss/Versions b/nss/Versions
e354a5
--- a/nss/Versions	2020-09-14 17:48:49.294697083 -0400
e354a5
+++ b/nss/Versions	2020-09-14 17:55:21.867489155 -0400
e354a5
@@ -21,7 +21,7 @@ libc {
e354a5
     __nss_passwd_lookup2; __nss_group_lookup2; __nss_hosts_lookup2;
e354a5
     __nss_services_lookup2; __nss_next2; __nss_lookup;
e354a5
     __nss_hash; __nss_database_lookup2;
e354a5
-    __nss_files_fopen;
e354a5
+    __nss_files_fopen; __nss_readline; __nss_parse_line_result;
e354a5
   }
e354a5
 }
e354a5
 
e354a5
diff -rupN a/nss/nss_fgetent_r.c b/nss/nss_fgetent_r.c
e354a5
--- a/nss/nss_fgetent_r.c	1969-12-31 19:00:00.000000000 -0500
e354a5
+++ b/nss/nss_fgetent_r.c	2020-09-14 17:55:21.870489268 -0400
e354a5
@@ -0,0 +1,55 @@
e354a5
+/* Generic implementation of fget*ent_r.
e354a5
+   Copyright (C) 2020 Free Software Foundation, Inc.
e354a5
+   This file is part of the GNU C Library.
e354a5
+
e354a5
+   The GNU C Library is free software; you can redistribute it and/or
e354a5
+   modify it under the terms of the GNU Lesser General Public
e354a5
+   License as published by the Free Software Foundation; either
e354a5
+   version 2.1 of the License, or (at your option) any later version.
e354a5
+
e354a5
+   The GNU C Library is distributed in the hope that it will be useful,
e354a5
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
e354a5
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
e354a5
+   Lesser General Public License for more details.
e354a5
+
e354a5
+   You should have received a copy of the GNU Lesser General Public
e354a5
+   License along with the GNU C Library; if not, see
e354a5
+   <https://www.gnu.org/licenses/>.  */
e354a5
+
e354a5
+#include <errno.h>
e354a5
+#include <nss_files.h>
e354a5
+
e354a5
+int
e354a5
+__nss_fgetent_r (FILE *fp, void *result, char *buffer, size_t buffer_length,
e354a5
+                 nss_files_parse_line parser)
e354a5
+{
e354a5
+  int ret;
e354a5
+
e354a5
+  _IO_flockfile (fp);
e354a5
+
e354a5
+  while (true)
e354a5
+    {
e354a5
+      off64_t original_offset;
e354a5
+      ret = __nss_readline (fp, buffer, buffer_length, &original_offset);
e354a5
+      if (ret == 0)
e354a5
+        {
e354a5
+          /* Parse the line into *RESULT.  */
e354a5
+          ret = parser (buffer, result,
e354a5
+                        (struct parser_data *) buffer, buffer_length, &errno);
e354a5
+
e354a5
+          /* Translate the result code from the parser into an errno
e354a5
+             value.  Also seeks back to the start of the line if
e354a5
+             necessary.  */
e354a5
+          ret = __nss_parse_line_result (fp, original_offset, ret);
e354a5
+
e354a5
+          if (ret == EINVAL)
e354a5
+            /* Skip over malformed lines.  */
e354a5
+            continue;
e354a5
+        }
e354a5
+      break;
e354a5
+    }
e354a5
+
e354a5
+  _IO_funlockfile (fp);
e354a5
+
e354a5
+  return ret;
e354a5
+}
e354a5
diff -rupN a/nss/nss_files/files-XXX.c b/nss/nss_files/files-XXX.c
e354a5
--- a/nss/nss_files/files-XXX.c	2020-09-14 17:48:49.296697158 -0400
e354a5
+++ b/nss/nss_files/files-XXX.c	2020-09-14 17:55:21.878489569 -0400
e354a5
@@ -135,10 +135,9 @@ internal_getent (FILE *stream, struct ST
e354a5
 		 char *buffer, size_t buflen, int *errnop H_ERRNO_PROTO
e354a5
 		 EXTRA_ARGS_DECL)
e354a5
 {
e354a5
-  char *p;
e354a5
   struct parser_data *data = (void *) buffer;
e354a5
   size_t linebuflen = buffer + buflen - data->linebuffer;
e354a5
-  int parse_result;
e354a5
+  int saved_errno = errno;	/* Do not clobber errno on success.  */
e354a5
 
e354a5
   if (buflen < sizeof *data + 2)
e354a5
     {
e354a5
@@ -149,66 +148,42 @@ internal_getent (FILE *stream, struct ST
e354a5
 
e354a5
   while (true)
e354a5
     {
e354a5
-      ssize_t r = __libc_readline_unlocked
e354a5
-	(stream, data->linebuffer, linebuflen);
e354a5
-      if (r < 0)
e354a5
-	{
e354a5
-	  *errnop = errno;
e354a5
-	  H_ERRNO_SET (NETDB_INTERNAL);
e354a5
-	  if (*errnop == ERANGE)
e354a5
-	    /* Request larger buffer.  */
e354a5
-	    return NSS_STATUS_TRYAGAIN;
e354a5
-	  else
e354a5
-	    /* Other read failure.  */
e354a5
-	    return NSS_STATUS_UNAVAIL;
e354a5
-	}
e354a5
-      else if (r == 0)
e354a5
+      off64_t original_offset;
e354a5
+      int ret = __nss_readline (stream, data->linebuffer, linebuflen,
e354a5
+				&original_offset);
e354a5
+      if (ret == ENOENT)
e354a5
 	{
e354a5
 	  /* End of file.  */
e354a5
 	  H_ERRNO_SET (HOST_NOT_FOUND);
e354a5
+	  __set_errno (saved_errno);
e354a5
 	  return NSS_STATUS_NOTFOUND;
e354a5
 	}
e354a5
-
e354a5
-      /* Everything OK.  Now skip leading blanks.  */
e354a5
-      p = data->linebuffer;
e354a5
-      while (isspace (*p))
e354a5
-	++p;
e354a5
-
e354a5
-      /* Ignore empty and comment lines.  */
e354a5
-      if (*p == '\0' || *p == '#')
e354a5
-	continue;
e354a5
-
e354a5
-      /* Parse the line.   */
e354a5
-      *errnop = EINVAL;
e354a5
-      parse_result = parse_line (p, result, data, buflen, errnop EXTRA_ARGS);
e354a5
-
e354a5
-      if (parse_result == -1)
e354a5
+      else if (ret == 0)
e354a5
 	{
e354a5
-	  if (*errnop == ERANGE)
e354a5
+	  ret = __nss_parse_line_result (stream, original_offset,
e354a5
+					 parse_line (data->linebuffer,
e354a5
+						     result, data, buflen,
e354a5
+						     errnop EXTRA_ARGS));
e354a5
+	  if (ret == 0)
e354a5
 	    {
e354a5
-	      /* Return to the original file position at the beginning
e354a5
-		 of the line, so that the next call can read it again
e354a5
-		 if necessary.  */
e354a5
-	      if (__fseeko64 (stream, -r, SEEK_CUR) != 0)
e354a5
-		{
e354a5
-		  if (errno == ERANGE)
e354a5
-		    *errnop = EINVAL;
e354a5
-		  else
e354a5
-		    *errnop = errno;
e354a5
-		  H_ERRNO_SET (NETDB_INTERNAL);
e354a5
-		  return NSS_STATUS_UNAVAIL;
e354a5
-		}
e354a5
+	      /* Line has been parsed successfully.  */
e354a5
+	      __set_errno (saved_errno);
e354a5
+	      return NSS_STATUS_SUCCESS;
e354a5
 	    }
e354a5
-	  H_ERRNO_SET (NETDB_INTERNAL);
e354a5
-	  return NSS_STATUS_TRYAGAIN;
e354a5
+	  else if (ret == EINVAL)
e354a5
+	    /* If it is invalid, loop to get the next line of the file
e354a5
+	       to parse.  */
e354a5
+	    continue;
e354a5
 	}
e354a5
 
e354a5
-      /* Return the data if parsed successfully.  */
e354a5
-      if (parse_result != 0)
e354a5
-	return NSS_STATUS_SUCCESS;
e354a5
-
e354a5
-      /* If it is invalid, loop to get the next line of the file to
e354a5
-	 parse.  */
e354a5
+      *errnop = ret;
e354a5
+      H_ERRNO_SET (NETDB_INTERNAL);
e354a5
+      if (ret == ERANGE)
e354a5
+	/* Request larger buffer.  */
e354a5
+	return NSS_STATUS_TRYAGAIN;
e354a5
+      else
e354a5
+	/* Other read failure.  */
e354a5
+	return NSS_STATUS_UNAVAIL;
e354a5
     }
e354a5
 }
e354a5
 
e354a5
diff -rupN a/nss/nss_parse_line_result.c b/nss/nss_parse_line_result.c
e354a5
--- a/nss/nss_parse_line_result.c	1969-12-31 19:00:00.000000000 -0500
e354a5
+++ b/nss/nss_parse_line_result.c	2020-09-14 17:55:21.880489645 -0400
e354a5
@@ -0,0 +1,46 @@
e354a5
+/* Implementation of __nss_parse_line_result.
e354a5
+   Copyright (C) 2020 Free Software Foundation, Inc.
e354a5
+   This file is part of the GNU C Library.
e354a5
+
e354a5
+   The GNU C Library is free software; you can redistribute it and/or
e354a5
+   modify it under the terms of the GNU Lesser General Public
e354a5
+   License as published by the Free Software Foundation; either
e354a5
+   version 2.1 of the License, or (at your option) any later version.
e354a5
+
e354a5
+   The GNU C Library is distributed in the hope that it will be useful,
e354a5
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
e354a5
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
e354a5
+   Lesser General Public License for more details.
e354a5
+
e354a5
+   You should have received a copy of the GNU Lesser General Public
e354a5
+   License along with the GNU C Library; if not, see
e354a5
+   <https://www.gnu.org/licenses/>.  */
e354a5
+
e354a5
+#include <nss_files.h>
e354a5
+
e354a5
+#include <assert.h>
e354a5
+#include <errno.h>
e354a5
+
e354a5
+int
e354a5
+__nss_parse_line_result (FILE *fp, off64_t offset, int parse_line_result)
e354a5
+{
e354a5
+  assert (parse_line_result >= -1 && parse_line_result <= 1);
e354a5
+
e354a5
+  switch (__builtin_expect (parse_line_result, 1))
e354a5
+    {
e354a5
+    case 1:
e354a5
+      /* Sucess.  */
e354a5
+      return 0;
e354a5
+    case 0:
e354a5
+      /* Parse error.  */
e354a5
+      __set_errno (EINVAL);
e354a5
+      return EINVAL;
e354a5
+    case -1:
e354a5
+      /* Out of buffer space.  */
e354a5
+      return __nss_readline_seek (fp, offset);
e354a5
+
e354a5
+      default:
e354a5
+        __builtin_unreachable ();
e354a5
+    }
e354a5
+}
e354a5
+libc_hidden_def (__nss_parse_line_result)
e354a5
diff -rupN a/nss/nss_readline.c b/nss/nss_readline.c
e354a5
--- a/nss/nss_readline.c	1969-12-31 19:00:00.000000000 -0500
e354a5
+++ b/nss/nss_readline.c	2020-09-14 17:55:21.883489758 -0400
e354a5
@@ -0,0 +1,99 @@
e354a5
+/* Read a line from an nss_files database file.
e354a5
+   Copyright (C) 2020 Free Software Foundation, Inc.
e354a5
+   This file is part of the GNU C Library.
e354a5
+
e354a5
+   The GNU C Library is free software; you can redistribute it and/or
e354a5
+   modify it under the terms of the GNU Lesser General Public
e354a5
+   License as published by the Free Software Foundation; either
e354a5
+   version 2.1 of the License, or (at your option) any later version.
e354a5
+
e354a5
+   The GNU C Library is distributed in the hope that it will be useful,
e354a5
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
e354a5
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
e354a5
+   Lesser General Public License for more details.
e354a5
+
e354a5
+   You should have received a copy of the GNU Lesser General Public
e354a5
+   License along with the GNU C Library; if not, see
e354a5
+   <https://www.gnu.org/licenses/>.  */
e354a5
+
e354a5
+#include <nss_files.h>
e354a5
+
e354a5
+#include <ctype.h>
e354a5
+#include <errno.h>
e354a5
+#include <string.h>
e354a5
+
e354a5
+int
e354a5
+__nss_readline (FILE *fp, char *buf, size_t len, off64_t *poffset)
e354a5
+{
e354a5
+  /* We need space for at least one character, the line terminator,
e354a5
+     and the NUL byte.  */
e354a5
+  if (len < 3)
e354a5
+    {
e354a5
+      *poffset = -1;
e354a5
+      __set_errno (ERANGE);
e354a5
+      return ERANGE;
e354a5
+    }
e354a5
+
e354a5
+  while (true)
e354a5
+    {
e354a5
+      /* Keep original offset for retries.  */
e354a5
+      *poffset = __ftello64 (fp);
e354a5
+
e354a5
+      buf[len - 1] = '\xff';        /* Marker to recognize truncation.  */
e354a5
+      if (fgets_unlocked (buf, len, fp) == NULL)
e354a5
+        {
e354a5
+          if (feof_unlocked (fp))
e354a5
+            {
e354a5
+              __set_errno (ENOENT);
e354a5
+              return ENOENT;
e354a5
+            }
e354a5
+          else
e354a5
+            {
e354a5
+              /* Any other error.  Do not return ERANGE in this case
e354a5
+                 because the caller would retry.  */
e354a5
+              if (errno == ERANGE)
e354a5
+                __set_errno (EINVAL);
e354a5
+              return errno;
e354a5
+            }
e354a5
+        }
e354a5
+      else if (buf[len - 1] != '\xff')
e354a5
+        /* The buffer is too small.  Arrange for re-reading the same
e354a5
+           line on the next call.  */
e354a5
+        return __nss_readline_seek (fp, *poffset);
e354a5
+
e354a5
+      /* fgets_unlocked succeeded.  */
e354a5
+
e354a5
+      /* Remove leading whitespace.  */
e354a5
+      char *p = buf;
e354a5
+      while (isspace (*p))
e354a5
+        ++p;
e354a5
+      if (*p == '\0' || *p == '#')
e354a5
+        /* Skip empty lines and comments.  */
e354a5
+        continue;
e354a5
+      if (p != buf)
e354a5
+        memmove (buf, p, strlen (p));
e354a5
+
e354a5
+      /* Return line to the caller.  */
e354a5
+      return 0;
e354a5
+    }
e354a5
+}
e354a5
+libc_hidden_def (__nss_readline)
e354a5
+
e354a5
+int
e354a5
+__nss_readline_seek (FILE *fp, off64_t offset)
e354a5
+{
e354a5
+  if (offset < 0 /* __ftello64 failed.  */
e354a5
+      || __fseeko64 (fp, offset, SEEK_SET) < 0)
e354a5
+    {
e354a5
+      /* Without seeking support, it is not possible to
e354a5
+         re-read the same line, so this is a hard failure.  */
e354a5
+      fseterr_unlocked (fp);
e354a5
+      __set_errno (ESPIPE);
e354a5
+      return ESPIPE;
e354a5
+    }
e354a5
+  else
e354a5
+    {
e354a5
+      __set_errno (ERANGE);
e354a5
+      return ERANGE;
e354a5
+    }
e354a5
+}