[06/11] nss: Add __nss_fgetent_r
Commit Message
And helper functions __nss_readline, __nss_readline_seek,
__nss_parse_line_result.
This consolidates common code for handling overlong lines and
parse files. Use the new functionality in internal_getent
in nss/nss_files/files-XXX.c.
---
include/nss_files.h | 29 +++++++++++
nss/Makefile | 4 +-
nss/Versions | 2 +-
nss/nss_fgetent_r.c | 55 +++++++++++++++++++++
nss/nss_files/files-XXX.c | 79 ++++++++++-------------------
nss/nss_parse_line_result.c | 46 +++++++++++++++++
nss/nss_readline.c | 99 +++++++++++++++++++++++++++++++++++++
7 files changed, 260 insertions(+), 54 deletions(-)
create mode 100644 nss/nss_fgetent_r.c
create mode 100644 nss/nss_parse_line_result.c
create mode 100644 nss/nss_readline.c
Comments
On 7/17/20 4:30 AM, Florian Weimer via Libc-alpha wrote:
> And helper functions __nss_readline, __nss_readline_seek,
> __nss_parse_line_result.
>
> This consolidates common code for handling overlong lines and
> parse files. Use the new functionality in internal_getent
> in nss/nss_files/files-XXX.c.
OK for 2.32. Very clean!
Tested-by: Carlos O'Donell <carlos@redhat.com>
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
> ---
> include/nss_files.h | 29 +++++++++++
> nss/Makefile | 4 +-
> nss/Versions | 2 +-
> nss/nss_fgetent_r.c | 55 +++++++++++++++++++++
> nss/nss_files/files-XXX.c | 79 ++++++++++-------------------
> nss/nss_parse_line_result.c | 46 +++++++++++++++++
> nss/nss_readline.c | 99 +++++++++++++++++++++++++++++++++++++
> 7 files changed, 260 insertions(+), 54 deletions(-)
> create mode 100644 nss/nss_fgetent_r.c
> create mode 100644 nss/nss_parse_line_result.c
> create mode 100644 nss/nss_readline.c
>
> diff --git a/include/nss_files.h b/include/nss_files.h
> index d0f26815b5..f45ea02dc0 100644
> --- a/include/nss_files.h
> +++ b/include/nss_files.h
> @@ -25,6 +25,28 @@
> FILE *__nss_files_fopen (const char *path);
> libc_hidden_proto (__nss_files_fopen)
>
> +/* Read a line from FP, storing it BUF. Strip leading blanks and skip
> + comments. Sets errno and returns error code on failure. Special
> + failure: ERANGE means the buffer is too small. The function writes
> + the original offset to *POFFSET (which can be negative in the case
> + of non-seekable input). */
> +int __nss_readline (FILE *fp, char *buf, size_t len, off64_t *poffset);
> +libc_hidden_proto (__nss_readline)
> +
> +/* Seek FP to OFFSET. Sets errno and returns error code on failure.
> + On success, sets errno to ERANGE and returns ERANGE (to indicate
> + re-reading of the same input line to the caller). If OFFSET is
> + negative, fail with ESPIPE without seeking. Intended to be used
> + after parsing data read by __nss_readline failed with ERANGE. */
> +int __nss_readline_seek (FILE *fp, off64_t offset) attribute_hidden;
> +
> +/* Handles the result of a parse_line call (as defined by
> + nss/nss_files/files-parse.c). Adjusts the file offset of FP as
> + necessary. Returns 0 on success, and updates errno on failure (and
> + returns that error code). */
> +int __nss_parse_line_result (FILE *fp, off64_t offset, int parse_line_result);
> +libc_hidden_proto (__nss_parse_line_result)
> +
> struct parser_data;
>
> /* Instances of the parse_line function from
> @@ -52,4 +74,11 @@ libnss_files_hidden_proto (_nss_files_parse_servent)
> libc_hidden_proto (_nss_files_parse_sgent)
> libc_hidden_proto (_nss_files_parse_spent)
>
> +/* Generic implementation of fget*ent_r. Reads lines from FP until
> + EOF or a successful parse into *RESULT using PARSER. Returns 0 on
> + success, ENOENT on EOF, ERANGE on too-small buffer. */
> +int __nss_fgetent_r (FILE *fp, void *result,
> + char *buffer, size_t buffer_length,
> + nss_files_parse_line parser) attribute_hidden;
> +
> #endif /* _NSS_FILES_H */
> diff --git a/nss/Makefile b/nss/Makefile
> index 00f4d89310..20c412c3e1 100644
> --- a/nss/Makefile
> +++ b/nss/Makefile
> @@ -28,7 +28,9 @@ headers := nss.h
> routines = nsswitch getnssent getnssent_r digits_dots \
> valid_field valid_list_field rewrite_field \
> $(addsuffix -lookup,$(databases)) \
> - compat-lookup nss_hash nss_files_fopen
> + compat-lookup nss_hash nss_files_fopen \
> + nss_readline nss_parse_line_result \
> + nss_fgetent_r
>
> # These are the databases that go through nss dispatch.
> # Caution: if you add a database here, you must add its real name
> diff --git a/nss/Versions b/nss/Versions
> index f489cb6eb0..71703750bf 100644
> --- a/nss/Versions
> +++ b/nss/Versions
> @@ -18,7 +18,7 @@ libc {
> __nss_passwd_lookup2; __nss_group_lookup2; __nss_hosts_lookup2;
> __nss_services_lookup2; __nss_next2; __nss_lookup;
> __nss_hash; __nss_database_lookup2;
> - __nss_files_fopen;
> + __nss_files_fopen; __nss_readline; __nss_parse_line_result;
> }
> }
>
> diff --git a/nss/nss_fgetent_r.c b/nss/nss_fgetent_r.c
> new file mode 100644
> index 0000000000..8f7c5b5cc7
> --- /dev/null
> +++ b/nss/nss_fgetent_r.c
> @@ -0,0 +1,55 @@
> +/* Generic implementation of fget*ent_r.
> + Copyright (C) 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 <errno.h>
> +#include <nss_files.h>
> +
> +int
> +__nss_fgetent_r (FILE *fp, void *result, char *buffer, size_t buffer_length,
> + nss_files_parse_line parser)
> +{
> + int ret;
> +
> + _IO_flockfile (fp);
> +
> + while (true)
> + {
> + off64_t original_offset;
> + ret = __nss_readline (fp, buffer, buffer_length, &original_offset);
> + if (ret == 0)
> + {
> + /* Parse the line into *RESULT. */
> + ret = parser (buffer, result,
> + (struct parser_data *) buffer, buffer_length, &errno);
> +
> + /* Translate the result code from the parser into an errno
> + value. Also seeks back to the start of the line if
> + necessary. */
> + ret = __nss_parse_line_result (fp, original_offset, ret);
> +
> + if (ret == EINVAL)
> + /* Skip over malformed lines. */
> + continue;
> + }
> + break;
> + }
> +
> + _IO_funlockfile (fp);
> +
> + return ret;
> +}
> diff --git a/nss/nss_files/files-XXX.c b/nss/nss_files/files-XXX.c
> index 9cc5137953..1db9e46127 100644
> --- a/nss/nss_files/files-XXX.c
> +++ b/nss/nss_files/files-XXX.c
> @@ -135,10 +135,9 @@ internal_getent (FILE *stream, struct STRUCTURE *result,
> char *buffer, size_t buflen, int *errnop H_ERRNO_PROTO
> EXTRA_ARGS_DECL)
> {
> - char *p;
> struct parser_data *data = (void *) buffer;
> size_t linebuflen = buffer + buflen - data->linebuffer;
> - int parse_result;
> + int saved_errno = errno; /* Do not clobber errno on success. */
>
> if (buflen < sizeof *data + 2)
> {
> @@ -149,66 +148,42 @@ internal_getent (FILE *stream, struct STRUCTURE *result,
>
> while (true)
> {
> - 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)
> + off64_t original_offset;
> + int ret = __nss_readline (stream, data->linebuffer, linebuflen,
> + &original_offset);
OK.
> + if (ret == ENOENT)
> {
> /* End of file. */
> H_ERRNO_SET (HOST_NOT_FOUND);
> + __set_errno (saved_errno);
> return NSS_STATUS_NOTFOUND;
> }
> -
> - /* 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)
> + else if (ret == 0)
> {
> - if (*errnop == ERANGE)
> + ret = __nss_parse_line_result (stream, original_offset,
> + parse_line (data->linebuffer,
> + result, data, buflen,
> + errnop EXTRA_ARGS));
OK.
> + if (ret == 0)
> {
> - /* 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;
> - }
> + /* Line has been parsed successfully. */
> + __set_errno (saved_errno);
> + return NSS_STATUS_SUCCESS;
> }
> - H_ERRNO_SET (NETDB_INTERNAL);
> - return NSS_STATUS_TRYAGAIN;
> + else if (ret == EINVAL)
> + /* If it is invalid, loop to get the next line of the file
> + to parse. */
> + continue;
> }
>
> - /* Return the data if parsed successfully. */
> - if (parse_result != 0)
> - return NSS_STATUS_SUCCESS;
> -
> - /* If it is invalid, loop to get the next line of the file to
> - parse. */
> + *errnop = ret;
> + H_ERRNO_SET (NETDB_INTERNAL);
> + if (ret == ERANGE)
> + /* Request larger buffer. */
> + return NSS_STATUS_TRYAGAIN;
> + else
> + /* Other read failure. */
> + return NSS_STATUS_UNAVAIL;
> }
> }
>
> diff --git a/nss/nss_parse_line_result.c b/nss/nss_parse_line_result.c
> new file mode 100644
> index 0000000000..cd008e3c36
> --- /dev/null
> +++ b/nss/nss_parse_line_result.c
> @@ -0,0 +1,46 @@
> +/* Implementation of __nss_parse_line_result.
> + Copyright (C) 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 <nss_files.h>
> +
> +#include <assert.h>
> +#include <errno.h>
> +
> +int
> +__nss_parse_line_result (FILE *fp, off64_t offset, int parse_line_result)
> +{
> + assert (parse_line_result >= -1 && parse_line_result <= 1);
> +
> + switch (__builtin_expect (parse_line_result, 1))
> + {
> + case 1:
> + /* Sucess. */
> + return 0;
> + case 0:
> + /* Parse error. */
> + __set_errno (EINVAL);
> + return EINVAL;
> + case -1:
> + /* Out of buffer space. */
> + return __nss_readline_seek (fp, offset);
> +
> + default:
> + __builtin_unreachable ();
> + }
> +}
> +libc_hidden_def (__nss_parse_line_result)
> diff --git a/nss/nss_readline.c b/nss/nss_readline.c
> new file mode 100644
> index 0000000000..44e0dd9319
> --- /dev/null
> +++ b/nss/nss_readline.c
> @@ -0,0 +1,99 @@
> +/* Read a line from an nss_files database file.
> + Copyright (C) 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 <nss_files.h>
> +
> +#include <ctype.h>
> +#include <errno.h>
> +#include <string.h>
> +
> +int
> +__nss_readline (FILE *fp, char *buf, size_t len, off64_t *poffset)
> +{
OK. Like this clean implementation.
> + /* We need space for at least one character, the line terminator,
> + and the NUL byte. */
> + if (len < 3)
> + {
> + *poffset = -1;
> + __set_errno (ERANGE);
> + return ERANGE;
> + }
> +
> + while (true)
> + {
> + /* Keep original offset for retries. */
> + *poffset = __ftello64 (fp);
> +
> + buf[len - 1] = '\xff'; /* Marker to recognize truncation. */
> + if (fgets_unlocked (buf, len, fp) == NULL)
> + {
> + if (feof_unlocked (fp))
> + {
> + __set_errno (ENOENT);
> + return ENOENT;
> + }
> + else
> + {
> + /* Any other error. Do not return ERANGE in this case
> + because the caller would retry. */
> + if (errno == ERANGE)
> + __set_errno (EINVAL);
> + return errno;
> + }
> + }
> + else if (buf[len - 1] != '\xff')
> + /* The buffer is too small. Arrange for re-reading the same
> + line on the next call. */
> + return __nss_readline_seek (fp, *poffset);
> +
> + /* fgets_unlocked succeeded. */
> +
> + /* Remove leading whitespace. */
> + char *p = buf;
> + while (isspace (*p))
> + ++p;
> + if (*p == '\0' || *p == '#')
> + /* Skip empty lines and comments. */
> + continue;
> + if (p != buf)
> + memmove (buf, p, strlen (p));
> +
> + /* Return line to the caller. */
> + return 0;
> + }
> +}
> +libc_hidden_def (__nss_readline)
> +
> +int
> +__nss_readline_seek (FILE *fp, off64_t offset)
> +{
> + if (offset < 0 /* __ftello64 failed. */
> + || __fseeko64 (fp, offset, SEEK_SET) < 0)
> + {
> + /* Without seeking support, it is not possible to
> + re-read the same line, so this is a hard failure. */
OK.
> + fseterr_unlocked (fp);
> + __set_errno (ESPIPE);
> + return ESPIPE;
> + }
> + else
> + {
> + __set_errno (ERANGE);
> + return ERANGE;
> + }
> +}
>
@@ -25,6 +25,28 @@
FILE *__nss_files_fopen (const char *path);
libc_hidden_proto (__nss_files_fopen)
+/* Read a line from FP, storing it BUF. Strip leading blanks and skip
+ comments. Sets errno and returns error code on failure. Special
+ failure: ERANGE means the buffer is too small. The function writes
+ the original offset to *POFFSET (which can be negative in the case
+ of non-seekable input). */
+int __nss_readline (FILE *fp, char *buf, size_t len, off64_t *poffset);
+libc_hidden_proto (__nss_readline)
+
+/* Seek FP to OFFSET. Sets errno and returns error code on failure.
+ On success, sets errno to ERANGE and returns ERANGE (to indicate
+ re-reading of the same input line to the caller). If OFFSET is
+ negative, fail with ESPIPE without seeking. Intended to be used
+ after parsing data read by __nss_readline failed with ERANGE. */
+int __nss_readline_seek (FILE *fp, off64_t offset) attribute_hidden;
+
+/* Handles the result of a parse_line call (as defined by
+ nss/nss_files/files-parse.c). Adjusts the file offset of FP as
+ necessary. Returns 0 on success, and updates errno on failure (and
+ returns that error code). */
+int __nss_parse_line_result (FILE *fp, off64_t offset, int parse_line_result);
+libc_hidden_proto (__nss_parse_line_result)
+
struct parser_data;
/* Instances of the parse_line function from
@@ -52,4 +74,11 @@ libnss_files_hidden_proto (_nss_files_parse_servent)
libc_hidden_proto (_nss_files_parse_sgent)
libc_hidden_proto (_nss_files_parse_spent)
+/* Generic implementation of fget*ent_r. Reads lines from FP until
+ EOF or a successful parse into *RESULT using PARSER. Returns 0 on
+ success, ENOENT on EOF, ERANGE on too-small buffer. */
+int __nss_fgetent_r (FILE *fp, void *result,
+ char *buffer, size_t buffer_length,
+ nss_files_parse_line parser) attribute_hidden;
+
#endif /* _NSS_FILES_H */
@@ -28,7 +28,9 @@ headers := nss.h
routines = nsswitch getnssent getnssent_r digits_dots \
valid_field valid_list_field rewrite_field \
$(addsuffix -lookup,$(databases)) \
- compat-lookup nss_hash nss_files_fopen
+ compat-lookup nss_hash nss_files_fopen \
+ nss_readline nss_parse_line_result \
+ nss_fgetent_r
# These are the databases that go through nss dispatch.
# Caution: if you add a database here, you must add its real name
@@ -18,7 +18,7 @@ libc {
__nss_passwd_lookup2; __nss_group_lookup2; __nss_hosts_lookup2;
__nss_services_lookup2; __nss_next2; __nss_lookup;
__nss_hash; __nss_database_lookup2;
- __nss_files_fopen;
+ __nss_files_fopen; __nss_readline; __nss_parse_line_result;
}
}
new file mode 100644
@@ -0,0 +1,55 @@
+/* Generic implementation of fget*ent_r.
+ Copyright (C) 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 <errno.h>
+#include <nss_files.h>
+
+int
+__nss_fgetent_r (FILE *fp, void *result, char *buffer, size_t buffer_length,
+ nss_files_parse_line parser)
+{
+ int ret;
+
+ _IO_flockfile (fp);
+
+ while (true)
+ {
+ off64_t original_offset;
+ ret = __nss_readline (fp, buffer, buffer_length, &original_offset);
+ if (ret == 0)
+ {
+ /* Parse the line into *RESULT. */
+ ret = parser (buffer, result,
+ (struct parser_data *) buffer, buffer_length, &errno);
+
+ /* Translate the result code from the parser into an errno
+ value. Also seeks back to the start of the line if
+ necessary. */
+ ret = __nss_parse_line_result (fp, original_offset, ret);
+
+ if (ret == EINVAL)
+ /* Skip over malformed lines. */
+ continue;
+ }
+ break;
+ }
+
+ _IO_funlockfile (fp);
+
+ return ret;
+}
@@ -135,10 +135,9 @@ internal_getent (FILE *stream, struct STRUCTURE *result,
char *buffer, size_t buflen, int *errnop H_ERRNO_PROTO
EXTRA_ARGS_DECL)
{
- char *p;
struct parser_data *data = (void *) buffer;
size_t linebuflen = buffer + buflen - data->linebuffer;
- int parse_result;
+ int saved_errno = errno; /* Do not clobber errno on success. */
if (buflen < sizeof *data + 2)
{
@@ -149,66 +148,42 @@ internal_getent (FILE *stream, struct STRUCTURE *result,
while (true)
{
- 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)
+ off64_t original_offset;
+ int ret = __nss_readline (stream, data->linebuffer, linebuflen,
+ &original_offset);
+ if (ret == ENOENT)
{
/* End of file. */
H_ERRNO_SET (HOST_NOT_FOUND);
+ __set_errno (saved_errno);
return NSS_STATUS_NOTFOUND;
}
-
- /* 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)
+ else if (ret == 0)
{
- if (*errnop == ERANGE)
+ ret = __nss_parse_line_result (stream, original_offset,
+ parse_line (data->linebuffer,
+ result, data, buflen,
+ errnop EXTRA_ARGS));
+ if (ret == 0)
{
- /* 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;
- }
+ /* Line has been parsed successfully. */
+ __set_errno (saved_errno);
+ return NSS_STATUS_SUCCESS;
}
- H_ERRNO_SET (NETDB_INTERNAL);
- return NSS_STATUS_TRYAGAIN;
+ else if (ret == EINVAL)
+ /* If it is invalid, loop to get the next line of the file
+ to parse. */
+ continue;
}
- /* Return the data if parsed successfully. */
- if (parse_result != 0)
- return NSS_STATUS_SUCCESS;
-
- /* If it is invalid, loop to get the next line of the file to
- parse. */
+ *errnop = ret;
+ H_ERRNO_SET (NETDB_INTERNAL);
+ if (ret == ERANGE)
+ /* Request larger buffer. */
+ return NSS_STATUS_TRYAGAIN;
+ else
+ /* Other read failure. */
+ return NSS_STATUS_UNAVAIL;
}
}
new file mode 100644
@@ -0,0 +1,46 @@
+/* Implementation of __nss_parse_line_result.
+ Copyright (C) 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 <nss_files.h>
+
+#include <assert.h>
+#include <errno.h>
+
+int
+__nss_parse_line_result (FILE *fp, off64_t offset, int parse_line_result)
+{
+ assert (parse_line_result >= -1 && parse_line_result <= 1);
+
+ switch (__builtin_expect (parse_line_result, 1))
+ {
+ case 1:
+ /* Sucess. */
+ return 0;
+ case 0:
+ /* Parse error. */
+ __set_errno (EINVAL);
+ return EINVAL;
+ case -1:
+ /* Out of buffer space. */
+ return __nss_readline_seek (fp, offset);
+
+ default:
+ __builtin_unreachable ();
+ }
+}
+libc_hidden_def (__nss_parse_line_result)
new file mode 100644
@@ -0,0 +1,99 @@
+/* Read a line from an nss_files database file.
+ Copyright (C) 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 <nss_files.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+
+int
+__nss_readline (FILE *fp, char *buf, size_t len, off64_t *poffset)
+{
+ /* We need space for at least one character, the line terminator,
+ and the NUL byte. */
+ if (len < 3)
+ {
+ *poffset = -1;
+ __set_errno (ERANGE);
+ return ERANGE;
+ }
+
+ while (true)
+ {
+ /* Keep original offset for retries. */
+ *poffset = __ftello64 (fp);
+
+ buf[len - 1] = '\xff'; /* Marker to recognize truncation. */
+ if (fgets_unlocked (buf, len, fp) == NULL)
+ {
+ if (feof_unlocked (fp))
+ {
+ __set_errno (ENOENT);
+ return ENOENT;
+ }
+ else
+ {
+ /* Any other error. Do not return ERANGE in this case
+ because the caller would retry. */
+ if (errno == ERANGE)
+ __set_errno (EINVAL);
+ return errno;
+ }
+ }
+ else if (buf[len - 1] != '\xff')
+ /* The buffer is too small. Arrange for re-reading the same
+ line on the next call. */
+ return __nss_readline_seek (fp, *poffset);
+
+ /* fgets_unlocked succeeded. */
+
+ /* Remove leading whitespace. */
+ char *p = buf;
+ while (isspace (*p))
+ ++p;
+ if (*p == '\0' || *p == '#')
+ /* Skip empty lines and comments. */
+ continue;
+ if (p != buf)
+ memmove (buf, p, strlen (p));
+
+ /* Return line to the caller. */
+ return 0;
+ }
+}
+libc_hidden_def (__nss_readline)
+
+int
+__nss_readline_seek (FILE *fp, off64_t offset)
+{
+ if (offset < 0 /* __ftello64 failed. */
+ || __fseeko64 (fp, offset, SEEK_SET) < 0)
+ {
+ /* Without seeking support, it is not possible to
+ re-read the same line, so this is a hard failure. */
+ fseterr_unlocked (fp);
+ __set_errno (ESPIPE);
+ return ESPIPE;
+ }
+ else
+ {
+ __set_errno (ERANGE);
+ return ERANGE;
+ }
+}