Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/2227988/?format=api
{ "id": 2227988, "url": "http://patchwork.ozlabs.org/api/patches/2227988/?format=api", "web_url": "http://patchwork.ozlabs.org/project/glibc/patch/20260424172759.212189-1-marocketbd@gmail.com/", "project": { "id": 41, "url": "http://patchwork.ozlabs.org/api/projects/41/?format=api", "name": "GNU C Library", "link_name": "glibc", "list_id": "libc-alpha.sourceware.org", "list_email": "libc-alpha@sourceware.org", "web_url": "", "scm_url": "", "webscm_url": "", "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<20260424172759.212189-1-marocketbd@gmail.com>", "list_archive_url": null, "date": "2026-04-24T17:27:59", "name": "[v3] misc: Optimize getusershell.c", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "cc074baa0a6210dbfa1f8cfdf0c35978eebb49a1", "submitter": { "id": 92898, "url": "http://patchwork.ozlabs.org/api/people/92898/?format=api", "name": "Rocket Ma", "email": "marocketbd@gmail.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/glibc/patch/20260424172759.212189-1-marocketbd@gmail.com/mbox/", "series": [ { "id": 501396, "url": "http://patchwork.ozlabs.org/api/series/501396/?format=api", "web_url": "http://patchwork.ozlabs.org/project/glibc/list/?series=501396", "date": "2026-04-24T17:27:59", "name": "[v3] misc: Optimize getusershell.c", "version": 3, "mbox": "http://patchwork.ozlabs.org/series/501396/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2227988/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2227988/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "libc-alpha@sourceware.org" ], "Delivered-To": [ "patchwork-incoming@legolas.ozlabs.org", "libc-alpha@sourceware.org" ], "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=CYLmNiuY;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=sourceware.org\n (client-ip=38.145.34.32; helo=vm01.sourceware.org;\n envelope-from=libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org;\n receiver=patchwork.ozlabs.org)", "sourceware.org;\n\tdkim=pass (2048-bit key,\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=CYLmNiuY", "sourceware.org;\n dmarc=pass (p=none dis=none) header.from=gmail.com", "sourceware.org; spf=pass smtp.mailfrom=gmail.com", "server2.sourceware.org;\n arc=none smtp.remote-ip=2607:f8b0:4864:20::132e" ], "Received": [ "from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g2Kgg4v57z1y2d\n\tfor <incoming@patchwork.ozlabs.org>; Sat, 25 Apr 2026 03:28:47 +1000 (AEST)", "from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id AF0894BB58D1\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 24 Apr 2026 17:28:45 +0000 (GMT)", "from mail-dy1-x132e.google.com (mail-dy1-x132e.google.com\n [IPv6:2607:f8b0:4864:20::132e])\n by sourceware.org (Postfix) with ESMTPS id 6AEE44BB3BCF\n for <libc-alpha@sourceware.org>; Fri, 24 Apr 2026 17:28:22 +0000 (GMT)", "by mail-dy1-x132e.google.com with SMTP id\n 5a478bee46e88-2de831d2b20so1050076eec.1\n for <libc-alpha@sourceware.org>; Fri, 24 Apr 2026 10:28:22 -0700 (PDT)", "from localhost ([23.94.240.252]) by smtp.gmail.com with UTF8SMTPSA\n id\n 5a478bee46e88-2e53a4a8018sm41645747eec.8.2026.04.24.10.28.20\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Fri, 24 Apr 2026 10:28:20 -0700 (PDT)" ], "DKIM-Filter": [ "OpenDKIM Filter v2.11.0 sourceware.org AF0894BB58D1", "OpenDKIM Filter v2.11.0 sourceware.org 6AEE44BB3BCF" ], "DMARC-Filter": "OpenDMARC Filter v1.4.2 sourceware.org 6AEE44BB3BCF", "ARC-Filter": "OpenARC Filter v1.0.0 sourceware.org 6AEE44BB3BCF", "ARC-Seal": "i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1777051702; cv=none;\n b=cJnloZOICtk9JYaQZYpNzrE7hb1/k9iXbmCe4YCWFTvihIKbHwhaabM9Ffdlnt/SYAmIGBoH7+YQ87LF9NTOEGspnoujqwAbivUUHH01AGSYSv07PnEyyT59oftCHazbZ3/IbS+cTHGZK7/Kb5PbX9mhExXj0T9RcQYfyfgc/Ro=", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1777051702; c=relaxed/simple;\n bh=TC0cCaLoVOG3FoxFLj8xHF393OqdZOtdM3cOmTaTgnk=;\n h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version;\n b=UmNuzzBgHDRnazicJhFHvHut/dWsQz+rIUjKPSBPczVZY+gFPFPP9DPowA2pfOG9Yc3mMxwBpX5OP54M6+YdZZ3yow7tlpbBQGI3usbwLqJWn2Z0OnMN3uMLwSpSvcA8wQgHDxR6v0AsqYjaPmEEoojBpkJ3Aab2H0bx3mmgGsQ=", "ARC-Authentication-Results": "i=1; server2.sourceware.org", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=gmail.com; s=20251104; t=1777051701; x=1777656501; darn=sourceware.org;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n :message-id:reply-to;\n bh=mgM/bFTOpazhuNlHeA1niMO3qMKMF+ldWWjsa4jClzE=;\n b=CYLmNiuYRacriO8FtLBq8cWtGSAt55IreuuHZIsbjT7vtNA1eFvaZCylqxgbdvNgVQ\n 0WACI1xlUWHU+EkaLekXUtmRhJrPHj7H+MQFwbJGI4gObDB69Jrm+quCZ07LTxhRvlUP\n CO15YnsLfN8r3BpCpcIZTWphZKS7MWaZ02q4REJVGNOdpCqdzcK0WMV92WZe9JIYBb+X\n mprU5X3yXzSVQVBEsxAEZpLzicMib+1JT10KMUEMTHpKfsISdZwLN8UqQvuWVhzJY0wq\n 1K9m1dQPy3bmeqxDBjAH64iJhfVW3AAOwcHl4i6sZO3xxcxRaMzgbyx1ZC0IzvCW3bbY\n RFJA==", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1777051701; x=1777656501;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from\n :to:cc:subject:date:message-id:reply-to;\n bh=mgM/bFTOpazhuNlHeA1niMO3qMKMF+ldWWjsa4jClzE=;\n b=etgYWNJue08AnYLUkxfZ7t3Tsm/tYBCZB/CHE+cvt8piPFojK+t7T5PQgn+rfBZsuT\n m1LTkP2Gc+kgBd/pUYJVah1bIOHG7tY5RVyPo3R+BfvsgsEgSVCpgHWRL//aaoC4t9EY\n d7D+FcTbsqqW7buIf1g1DNMwNib1UIHscrYKTjACj7gDFt49ZCGM66efdxTZt9j5CS+8\n OYXmvSeXSxiSKdVf+U7PRhES3qhlT7Oa+8d5ouG+TbAD0MjMENcIEXe/lr1nz+5SgSZJ\n C2Em4jF5NUqFT67jzSysKHLAywp5Ix3lDd/Z0gm0rMhNBGvaMnRYrBwfdmCaZRr5NGK5\n 1TnA==", "X-Gm-Message-State": "AOJu0YwaE29mE8Qus/X/VdftEcZdYdiQm0fSW1cd+qheyYUZZ7Yb64Ro\n fp59dYU0LIWZtxXpZ0AcTJxiVxmpAoxZYMHxcfHRXFcV6OI7glr4pIK1", "X-Gm-Gg": "AeBDiesm4m9ZBEqCTsaRzRSFlWnaWoFZXbf/FKFR+mSalANKSX84yGvox5ZBX4vXvAc\n NxUDb0Aml0HtWt1nGBhzQW7BsElZqCVrM7Kx6Cdu6EgTEsdcXT3kiD2Y7lfEMjEHDlykXMaV74D\n Fg8DbX5T13xBeV2aN5Zm4cnNDlO7EPKRLMvi9h9CWiSFb0Orc5Jvd3rwnFFHxKzpZ6Oh6FuPTGe\n l0HApGS+aKWSM2yBPWc6xf5IALh3iJAd5WrRtf2vmZ2mU4Vq16TkAdT0M28fjTId/qsBvzDOmAM\n Wg1MCK98tl96OeL/24l+TnZyA1KrX5C1begh83dxJvPHBzyK5EXwra9lHLfQe1+abSzZ+RvwDGP\n tbXNaJrJCYA55fr/1fOT+gZZYJI1Mrg1QeOGmq1SA9IlF1wE1Wb7DKU8EQka/XRNHrU5RluNyEW\n qJTCvnrwEUqu7M6r4HFdMTPVwa2Osg2m3pOCMQANIfxzEM6UbF+Wgh8pyL6wYybIwdE311Np0Oj\n Fsbrrui0ctnDCX/O7fEV7bPUyCY/1ATEUjjb5lmgrJzyUTcJiJzaJIH2TN7qwunwd6GhqKr37M=", "X-Received": "by 2002:a05:7300:bc9a:b0:2d3:f43c:d684 with SMTP id\n 5a478bee46e88-2e4647cf9e8mr15408987eec.2.1777051700971;\n Fri, 24 Apr 2026 10:28:20 -0700 (PDT)", "From": "Rocket Ma <marocketbd@gmail.com>", "To": "Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>", "Cc": "libc-alpha@sourceware.org,\n\tCollin Funk <collin.funk1@gmail.com>", "Subject": "[PATCH v3] misc: Optimize getusershell.c", "Date": "Fri, 24 Apr 2026 10:27:59 -0700", "Message-ID": "<20260424172759.212189-1-marocketbd@gmail.com>", "X-Mailer": "git-send-email 2.47.3", "In-Reply-To": "<70a7356a-43a5-41d2-af1b-c03a822b7b90@linaro.org>", "References": "<20260422164926.62177-1-marocketbd@gmail.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "X-BeenThere": "libc-alpha@sourceware.org", "X-Mailman-Version": "2.1.30", "Precedence": "list", "List-Id": "Libc-alpha mailing list <libc-alpha.sourceware.org>", "List-Unsubscribe": "<https://sourceware.org/mailman/options/libc-alpha>,\n <mailto:libc-alpha-request@sourceware.org?subject=unsubscribe>", "List-Archive": "<https://sourceware.org/pipermail/libc-alpha/>", "List-Post": "<mailto:libc-alpha@sourceware.org>", "List-Help": "<mailto:libc-alpha-request@sourceware.org?subject=help>", "List-Subscribe": "<https://sourceware.org/mailman/listinfo/libc-alpha>,\n <mailto:libc-alpha-request@sourceware.org?subject=subscribe>", "Errors-To": "libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org" }, "content": "* misc/getusershell.c: Completely rewrite the unit. Only allocate one\nbig buffer to store shell names. Add a missing unit test.\n\nThe new implementation read the whole file into one buffer, and wipe out\nevery byte but shell names. Later when addressing shell names from first\nshell, jump to next '\\0' and then jump to next '/'. This could reduce\nmemory footprint and shall improve some performance.\n\nSigned-off-by: Rocket Ma <marocketbd@gmail.com>\n---\n> I don't think should allow users changing the return value, nor we have this\n> set in function contract. Maybe an assert would be better here.\n\nThe API returns a (char *) without const, so I thought that means the string\nmight be writable then. Anyway, now I assume they are read-only when return.\n\n> We try to avoid assignment and comparison in the same expression, we\n> prefer to:\n\nI referred some glibc code and add a comment, now the code should be neater.\n\n> The current implementation already sets and returns read-only strings,\n> so the implicit function contract is user are not supposed to write on\n> the getusershell returned values. So I think there is no much gain in\n> returning allocated strings here.\n\nOK, directly point shells to builtin ones when fail.\n\n> Missing space before '(' here and below.\n\nI forgot to run clang-format when finishing the test. :)\n\nNote: you are reviewing my patch v1, not v2.\n---\n misc/Makefile | 1 +\n misc/getusershell.c | 265 ++++++++++++++++++-----------------\n misc/tst-getusershell.c | 75 ++++++++++\n misc/tst-getusershell.shells | 7 +\n 4 files changed, 223 insertions(+), 125 deletions(-)\n create mode 100644 misc/tst-getusershell.c\n create mode 100644 misc/tst-getusershell.shells", "diff": "diff --git a/misc/Makefile b/misc/Makefile\nindex 4395366d74..31c930ef64 100644\n--- a/misc/Makefile\n+++ b/misc/Makefile\n@@ -245,6 +245,7 @@ tests := \\\n tst-empty \\\n tst-error1 \\\n tst-fdset \\\n+ tst-getusershell \\\n tst-hsearch \\\n tst-insremque \\\n tst-ioctl \\\ndiff --git a/misc/getusershell.c b/misc/getusershell.c\nindex 4221095dca..e30c566f9d 100644\n--- a/misc/getusershell.c\n+++ b/misc/getusershell.c\n@@ -1,143 +1,158 @@\n-/*\n- * Copyright (c) 1985, 1993\n- *\tThe Regents of the University of California. All rights reserved.\n- *\n- * Redistribution and use in source and binary forms, with or without\n- * modification, are permitted provided that the following conditions\n- * are met:\n- * 1. Redistributions of source code must retain the above copyright\n- * notice, this list of conditions and the following disclaimer.\n- * 2. Redistributions in binary form must reproduce the above copyright\n- * notice, this list of conditions and the following disclaimer in the\n- * documentation and/or other materials provided with the distribution.\n- * 4. Neither the name of the University nor the names of its contributors\n- * may be used to endorse or promote products derived from this software\n- * without specific prior written permission.\n- *\n- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND\n- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE\n- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n- * SUCH DAMAGE.\n- */\n-\n-#if defined(LIBC_SCCS) && !defined(lint)\n-static char sccsid[] = \"@(#)getusershell.c\t8.1 (Berkeley) 6/4/93\";\n-#endif /* LIBC_SCCS and not lint */\n-\n-#include <sys/param.h>\n-#include <sys/file.h>\n-#include <sys/stat.h>\n+/* Copyright (C) 2026 The GNU Toolchain Authors.\n+ This file is part of the GNU C Library.\n+\n+ The GNU C Library is free software; you can redistribute it and/or\n+ modify it under the terms of the GNU Lesser General Public\n+ License as published by the Free Software Foundation; either\n+ version 2.1 of the License, or (at your option) any later version.\n+\n+ The GNU C Library is distributed in the hope that it will be useful,\n+ but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n+ Lesser General Public License for more details.\n+\n+ You should have received a copy of the GNU Lesser General Public\n+ License along with the GNU C Library; if not, see\n+ <https://www.gnu.org/licenses/>. */\n+\n+#include \"ctype/ctype.h\"\n+#include \"iolibio.h\"\n+#include <assert.h>\n+#include <fcntl.h>\n+#include <stddef.h>\n #include <stdio.h>\n-#include <stdio_ext.h>\n-#include <ctype.h>\n-#include <stdlib.h>\n+#include <stdint.h>\n+#include <sys/stat.h>\n #include <unistd.h>\n #include <paths.h>\n+#include <stdlib.h>\n+#include <string.h>\n \n-/*\n- * Local shells should NOT be added here. They should be added in\n- * /etc/shells.\n- */\n-\n-/* NB: we do not initialize okshells here. The initialization needs\n- relocations. These interfaces are used so rarely that this is not\n- justified. Instead explicitly initialize the array when it is\n- used. */\n-#if 0\n-static const char *const okshells[] = { _PATH_BSHELL, _PATH_CSHELL, NULL };\n-#else\n-static const char *okshells[3];\n-#endif\n-static char **curshell, **shells, *strings;\n-static char **initshells (void) __THROW;\n-\n-/*\n- * Get a list of shells from _PATH_SHELLS, if it exists.\n- */\n-char *\n-getusershell (void)\n+#define DEFAULT_SHELLS _PATH_BSHELL \"\\0\" _PATH_CSHELL\n+\n+static char *shellbuf;\n+static char *shellend;\n+static char *nextshell;\n+\n+static char *\n+address_next_shell (void)\n {\n-\tchar *ret;\n-\n-\tif (curshell == NULL)\n-\t\tcurshell = initshells();\n-\tret = *curshell;\n-\tif (ret != NULL)\n-\t\tcurshell++;\n-\treturn (ret);\n+ char *curshell = nextshell;\n+ if (nextshell == NULL)\n+ return curshell;\n+\n+ /* init_shells guarantees we have a \\0 at the end */\n+ char *next0 = memchr (nextshell, '\\0', shellend - nextshell);\n+ assert (next0 != NULL);\n+ nextshell = memchr (next0, '/', shellend - next0);\n+ return curshell;\n }\n \n-void\n-endusershell (void)\n+/* Read /etc/shells, strip unnecessary bytes, and setup nextshell */\n+static void\n+init_shells (void)\n {\n+ free (shellbuf);\n+ shellbuf = NULL;\n+ shellend = NULL;\n+ nextshell = NULL;\n+\n+ struct __stat64_t64 fstat;\n+ FILE *fp = fopen (_PATH_SHELLS, \"rce\");\n+ if (fp == NULL)\n+ goto default_out;\n+ int rc = __fstat64_time64 (__fileno (fp), &fstat);\n+ if (rc == -1)\n+ goto close_out;\n+ /* Consider if buflen will overflow. */\n+ if (fstat.st_size < 2 || fstat.st_size > PTRDIFF_MAX - 1)\n+ goto close_out;\n+ /* 1 byte for \\n (will be overwritten as \\0). */\n+ size_t buflen = fstat.st_size + 1;\n+ shellbuf = malloc (buflen);\n+ if (shellbuf == NULL)\n+ goto close_out;\n+ shellbuf[buflen - 1] = '\\n';\n+ _IO_setbuf (fp, NULL);\n+ size_t cnt = _IO_fread (shellbuf, 1, fstat.st_size, fp);\n+ if (cnt != fstat.st_size)\n+ goto free_out;\n \n-\tfree(shells);\n-\tshells = NULL;\n-\tfree(strings);\n-\tstrings = NULL;\n-\tcurshell = NULL;\n+ /* Loop through file and only keep shell names.\n+ The rest characters are set to \\0 to reduce addressing workload. */\n+ char *top;\n+ char *line_start, *line_end;\n+ char *slash, *discard;\n+\n+ top = shellbuf + buflen;\n+ line_start = shellbuf;\n+ while ((line_end = memchr (line_start, '\\n', top - line_start)) != NULL)\n+ {\n+ line_end++; /* include \\n */\n+ discard = line_start;\n+\n+ slash = memchr (line_start, '/', line_end - line_start);\n+ if (slash == NULL)\n+\tgoto wipe_line;\n+\n+ discard = memchr (line_start, '#', line_end - line_start);\n+ if (discard != NULL && discard < slash)\n+\tgoto wipe_line;\n+\n+ (void) memset (line_start, '\\0', slash - line_start);\n+ discard = slash;\n+ while (discard < line_end && *discard != '#'\n+\t && !isspace_l ((unsigned char) *discard, _nl_C_locobj_ptr))\n+\tdiscard++;\n+\n+ wipe_line:\n+ (void) memset (discard, '\\0', line_end - discard);\n+ line_start = line_end;\n+ }\n+\n+ nextshell = memchr (shellbuf, '/', top - shellbuf);\n+ if (nextshell == NULL)\n+ goto free_out;\n+ shellend = top;\n+ (void) fclose (fp);\n+ return;\n+\n+free_out:\n+ free (shellbuf);\n+ shellbuf = NULL;\n+close_out:\n+ (void) fclose (fp);\n+default_out:\n+ /* Fallback plan: use builtin read-only default shells.\n+ shellbuf is NULL here so it can be freed.*/\n+ nextshell = (char *) DEFAULT_SHELLS;\n+ shellend = (char *) DEFAULT_SHELLS + sizeof (DEFAULT_SHELLS);\n+}\n+\n+char *\n+getusershell (void)\n+{\n+ if (shellend == NULL)\n+ init_shells ();\n+ return address_next_shell ();\n }\n \n void\n setusershell (void)\n {\n-\n-\tcurshell = initshells();\n+ if (shellend == NULL)\n+ init_shells ();\n+ else if (shellbuf != NULL)\n+ nextshell = memchr (shellbuf, '/', shellend - shellbuf);\n+ else /* shellend != NULL && shellbuf == NULL */\n+ nextshell = (char *) DEFAULT_SHELLS;\n }\n \n-static char **\n-initshells (void)\n+void\n+endusershell (void)\n {\n-\tchar **sp, *cp;\n-\tFILE *fp;\n-\tstruct __stat64_t64 statb;\n-\tsize_t flen;\n-\n-\tfree(shells);\n-\tshells = NULL;\n-\tfree(strings);\n-\tstrings = NULL;\n-\tif ((fp = fopen(_PATH_SHELLS, \"rce\")) == NULL)\n-\t\tgoto init_okshells_noclose;\n-\tif (__fstat64_time64(fileno(fp), &statb) == -1) {\n-\tinit_okshells:\n-\t\t(void)fclose(fp);\n-\tinit_okshells_noclose:\n-\t\tokshells[0] = _PATH_BSHELL;\n-\t\tokshells[1] = _PATH_CSHELL;\n-\t\treturn (char **) okshells;\n-\t}\n-\tif (statb.st_size > ~(size_t)0 / sizeof (char *) * 3)\n-\t\tgoto init_okshells;\n-\tflen = statb.st_size + 3;\n-\tif ((strings = malloc(flen)) == NULL)\n-\t\tgoto init_okshells;\n-\tshells = malloc(statb.st_size / 3 * sizeof (char *));\n-\tif (shells == NULL) {\n-\t\tfree(strings);\n-\t\tstrings = NULL;\n-\t\tgoto init_okshells;\n-\t}\n-\tsp = shells;\n-\tcp = strings;\n-\twhile (fgets_unlocked(cp, flen - (cp - strings), fp) != NULL) {\n-\t\twhile (*cp != '#' && *cp != '/' && *cp != '\\0')\n-\t\t\tcp++;\n-\t\tif (*cp == '#' || *cp == '\\0' || cp[1] == '\\0')\n-\t\t\tcontinue;\n-\t\t*sp++ = cp;\n-\t\twhile (!isspace(*cp) && *cp != '#' && *cp != '\\0')\n-\t\t\tcp++;\n-\t\t*cp++ = '\\0';\n-\t}\n-\t*sp = NULL;\n-\t(void)fclose(fp);\n-\treturn (shells);\n+ free (shellbuf);\n+ shellbuf = NULL;\n+ shellend = NULL;\n+ nextshell = NULL;\n }\ndiff --git a/misc/tst-getusershell.c b/misc/tst-getusershell.c\nnew file mode 100644\nindex 0000000000..e60cabce3a\n--- /dev/null\n+++ b/misc/tst-getusershell.c\n@@ -0,0 +1,75 @@\n+/* Test the getusershell series functions.\n+ Copyright (C) 2026 The GNU Toolchain Authors.\n+ This file is part of the GNU C Library.\n+\n+ The GNU C Library is free software; you can redistribute it and/or\n+ modify it under the terms of the GNU Lesser General Public\n+ License as published by the Free Software Foundation; either\n+ version 2.1 of the License, or (at your option) any later version.\n+\n+ The GNU C Library is distributed in the hope that it will be useful,\n+ but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n+ Lesser General Public License for more details.\n+\n+ You should have received a copy of the GNU Lesser General Public\n+ License along with the GNU C Library; if not, see\n+ <https://www.gnu.org/licenses/>. */\n+\n+#include \"support/temp_file.h\"\n+#include <stdlib.h>\n+#include <unistd.h>\n+#include <paths.h>\n+#include <support/check.h>\n+#include <support/xunistd.h>\n+#include <support/support.h>\n+#include <support/namespace.h>\n+#include <support/test-driver.h>\n+\n+static void\n+test_in_chroot (void *chroot_path)\n+{\n+ xchroot (chroot_path);\n+\n+ TEST_COMPARE_STRING (getusershell (), \"/bin/sh\");\n+ TEST_COMPARE_STRING (getusershell (), \"/bin/bash\");\n+ TEST_COMPARE_STRING (getusershell (), \"/usr/bin/zsh\");\n+ TEST_COMPARE_STRING (getusershell (), NULL);\n+ TEST_COMPARE_STRING (getusershell (), NULL);\n+\n+ setusershell ();\n+ TEST_COMPARE_STRING (getusershell (), \"/bin/sh\");\n+ endusershell ();\n+\n+ xunlink (\"/etc/shells\");\n+ TEST_COMPARE_STRING (getusershell (), \"/bin/sh\");\n+ TEST_COMPARE_STRING (getusershell (), \"/bin/csh\");\n+ TEST_COMPARE_STRING (getusershell (), NULL);\n+ endusershell ();\n+}\n+\n+static int\n+do_test (void)\n+{\n+ support_become_root ();\n+ if (!support_can_chroot ())\n+ return EXIT_UNSUPPORTED;\n+\n+ char *chroot_dir = support_create_temp_directory (\"tst-getusershell-\");\n+ char *etc = xasprintf (\"%s/etc\", chroot_dir);\n+ add_temp_file (etc);\n+ xmkdir (etc, 0777);\n+ /* Don't add shells to file list as it will be deleted in test. */\n+ char *shells = xasprintf (\"%s/shells\", etc);\n+ support_copy_file (\"tst-getusershell.shells\", shells);\n+\n+ support_isolate_in_subprocess (test_in_chroot, chroot_dir);\n+\n+ free (etc);\n+ free (shells);\n+ free (chroot_dir);\n+\n+ return 0;\n+}\n+\n+#include <support/test-driver.c>\ndiff --git a/misc/tst-getusershell.shells b/misc/tst-getusershell.shells\nnew file mode 100644\nindex 0000000000..fcc7675398\n--- /dev/null\n+++ b/misc/tst-getusershell.shells\n@@ -0,0 +1,7 @@\n+# test hash\n+ # indentation\n+\n+/bin/sh\n+\t/bin/bash # ...\n+\n+xx /usr/bin/zsh#...\n\\ No newline at end of file\n", "prefixes": [ "v3" ] }