407 lines
13 KiB
Diff
407 lines
13 KiB
Diff
commit bdee910e88006ae33dc83ac3d2c0708adb6627d0
|
|
Author: Florian Weimer <fweimer@redhat.com>
|
|
Date: Wed Jul 15 13:41:31 2020 +0200
|
|
|
|
nss: Add __nss_fgetent_r
|
|
|
|
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.
|
|
|
|
Tested-by: Carlos O'Donell <carlos@redhat.com>
|
|
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
|
|
|
|
diff -rupN a/include/nss_files.h b/include/nss_files.h
|
|
--- a/include/nss_files.h 2020-09-14 17:48:49.353699306 -0400
|
|
+++ b/include/nss_files.h 2020-09-14 17:55:21.856488740 -0400
|
|
@@ -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_pa
|
|
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 -rupN a/nss/Makefile b/nss/Makefile
|
|
--- a/nss/Makefile 2020-09-14 17:48:49.293697045 -0400
|
|
+++ b/nss/Makefile 2020-09-14 17:55:21.860488891 -0400
|
|
@@ -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 -rupN a/nss/Versions b/nss/Versions
|
|
--- a/nss/Versions 2020-09-14 17:48:49.294697083 -0400
|
|
+++ b/nss/Versions 2020-09-14 17:55:21.867489155 -0400
|
|
@@ -21,7 +21,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 -rupN a/nss/nss_fgetent_r.c b/nss/nss_fgetent_r.c
|
|
--- a/nss/nss_fgetent_r.c 1969-12-31 19:00:00.000000000 -0500
|
|
+++ b/nss/nss_fgetent_r.c 2020-09-14 17:55:21.870489268 -0400
|
|
@@ -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 -rupN a/nss/nss_files/files-XXX.c b/nss/nss_files/files-XXX.c
|
|
--- a/nss/nss_files/files-XXX.c 2020-09-14 17:48:49.296697158 -0400
|
|
+++ b/nss/nss_files/files-XXX.c 2020-09-14 17:55:21.878489569 -0400
|
|
@@ -135,10 +135,9 @@ internal_getent (FILE *stream, struct ST
|
|
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 ST
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
diff -rupN a/nss/nss_parse_line_result.c b/nss/nss_parse_line_result.c
|
|
--- a/nss/nss_parse_line_result.c 1969-12-31 19:00:00.000000000 -0500
|
|
+++ b/nss/nss_parse_line_result.c 2020-09-14 17:55:21.880489645 -0400
|
|
@@ -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 -rupN a/nss/nss_readline.c b/nss/nss_readline.c
|
|
--- a/nss/nss_readline.c 1969-12-31 19:00:00.000000000 -0500
|
|
+++ b/nss/nss_readline.c 2020-09-14 17:55:21.883489758 -0400
|
|
@@ -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;
|
|
+ }
|
|
+}
|