libio: Remove __libc_readline_unlocked

__nss_readline supersedes it.  This reverts part of commit
3f5e3f5d066dcffb80af48ae2cf35a01a85a8f10 ("libio: Implement
internal function __libc_readline_unlocked").  The internal
aliases __fseeko64 and __ftello64 are preserved because
they are needed by __nss_readline as well.

Tested-by: Carlos O'Donell <carlos@redhat.com>
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
This commit is contained in:
Florian Weimer 2020-07-15 12:37:01 +02:00
parent 00bc6830e3
commit ec2f1fddf2
5 changed files with 2 additions and 423 deletions

View File

@ -179,19 +179,6 @@ int __vfxprintf (FILE *__fp, const char *__fmt, __gnuc_va_list,
unsigned int)
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 const char *__get_errlist (int) attribute_hidden;
extern const char *__get_errname (int) attribute_hidden;

View File

@ -49,7 +49,7 @@ routines := \
__fbufsize __freading __fwriting __freadable __fwritable __flbf \
__fpurge __fpending __fsetlocking \
\
libc_fatal fmemopen oldfmemopen vtables readline
libc_fatal fmemopen oldfmemopen vtables
tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc \
tst_wprintf2 tst-widetext test-fmemopen tst-ext tst-ext2 \
@ -68,7 +68,7 @@ tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc \
tst-sprintf-ub tst-sprintf-chk-ub tst-bz24051 tst-bz24153 \
tst-wfile-sync
tests-internal = tst-vtables tst-vtables-interposed tst-readline
tests-internal = tst-vtables tst-vtables-interposed
ifeq (yes,$(build-shared))
# Add test-fopenloc only if shared library is enabled since it depends on

View File

@ -161,6 +161,5 @@ libc {
__fseeko64;
__ftello64;
__libc_readline_unlocked;
}
}

View File

@ -1,170 +0,0 @@
/* fgets with ERANGE error reporting and size_t buffer length.
Copyright (C) 2018-2020 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
<https://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)

View File

@ -1,237 +0,0 @@
/* Test the __libc_readline_unlocked function.
Copyright (C) 2018-2020 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
<https://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 TIMEOUT 100
#define PREPARE prepare
#include <support/test-driver.c>