From 9ae3772c878290c09698789d5de6290447612c3c Mon Sep 17 00:00:00 2001 From: Packit Service Date: Mar 14 2021 14:58:03 +0000 Subject: Apply patch glibc-rh1871397-6.patch patch_name: glibc-rh1871397-6.patch present_in_specfile: true location_in_specfile: 369 --- diff --git a/include/nss_files.h b/include/nss_files.h index d0f2681..f45ea02 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 3ee7f53..7359da3 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 48c1f75..cb33bef 100644 --- a/nss/Versions +++ b/nss/Versions @@ -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 --git a/nss/nss_fgetent_r.c b/nss/nss_fgetent_r.c new file mode 100644 index 0000000..8f7c5b5 --- /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 + . */ + +#include +#include + +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 ab6ba84..37a3ce2 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); + 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 --git a/nss/nss_parse_line_result.c b/nss/nss_parse_line_result.c new file mode 100644 index 0000000..cd008e3 --- /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 + . */ + +#include + +#include +#include + +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 0000000..44e0dd9 --- /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 + . */ + +#include + +#include +#include +#include + +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; + } +}