summary refs log tree commit diff
path: root/nss/nss_files
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2018-07-06 14:23:15 +0200
committerFlorian Weimer <fweimer@redhat.com>2018-07-06 17:52:54 +0200
commit916124ed841745b7a1e0fbc43f9909340b47d373 (patch)
treec523c3829788b33588fdbc1953d889e8fa258ee7 /nss/nss_files
parent3f5e3f5d066dcffb80af48ae2cf35a01a85a8f10 (diff)
downloadglibc-916124ed841745b7a1e0fbc43f9909340b47d373.tar.gz
glibc-916124ed841745b7a1e0fbc43f9909340b47d373.tar.xz
glibc-916124ed841745b7a1e0fbc43f9909340b47d373.zip
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.
Diffstat (limited to 'nss/nss_files')
-rw-r--r--nss/nss_files/files-XXX.c121
1 files changed, 50 insertions, 71 deletions
diff --git a/nss/nss_files/files-XXX.c b/nss/nss_files/files-XXX.c
index df33551258..60d1544a96 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;
 }