From nobody Sat Feb 21 17:05:00 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4fJD4r6Tz6z6Svmq for ; Sat, 21 Feb 2026 17:05:00 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R12" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4fJD4r2BTNz3whV for ; Sat, 21 Feb 2026 17:05:00 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1771693500; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=1LW9wh2zlP7zH/byx7cpTjVAU/D+Awny6yaJHUI6IJ0=; b=iwkhDd1B/zTCm5mK4T+QXbfTbrp/s9TQ/E9GDxqxMgZ4jQi3Ou3ddWml9Y4FL5nAtxZSnu 2H9GijIW4QLsc7fxxHCiE6xihp0Cp7N2qGtEKcfU9ItyahMdoCpnUf23+IICrdzASSa1vw bqbKqOMD2y/UEfjNHski9gZvaLoPKTs9eHo7RBenIPKY3Ek41PWi22VjvaI2PXGTFzcBIm Pjzxg4GHXr1VVLhu5uVynVMDeFFW28iCeVeripFpOvHnN44b3g0P5nVpI2Z5cXV7qnFtEm mmgsRHzwRVbKCmrLHutHdWkxG+uaOETa35vFOk1L6x2pVV+t2LW8rfQc3qIecg== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1771693500; a=rsa-sha256; cv=none; b=Ex9Wr/TGZwR/oWydIPGedLUnSxDWpWQdhk25iZY2nrneEB+7X4nI5VTfrxaLPNE2Vfn0Iy Oed0n+Yhl7akN2S4S73XTT2P8t3PmxpBya7pLTFKzbXfvhD5AITxG7Yu8yTHzVXQ5uVw2F a5JfD1zUMNQ7P3soGHn//KtRpngaOkJ1ibX9BmAx7E2sWv3uyZ1crkbeeqRpu6w6aMi7Ai xMXKvdpExzpV0ar18b3ypBvsSsXtGXHEuv41GZraNkF9PUCInL4eb5WJzdRur4uT+K0Ngp SDyYxuviIXu+WRIuZPuUv/DQaAIpMLlrP3i7NbKjoR+WOXSuc43D3hpBw971ZQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1771693500; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=1LW9wh2zlP7zH/byx7cpTjVAU/D+Awny6yaJHUI6IJ0=; b=YD6Db3Z5Gc9p0l9ayzvXYZAMOBomyit1hza+VuWlQoWANWOf7aQlQKwZKbxAbvj11fPqwH /u22Qnf2VvVnHRaO2Vz24efuM0vrC0ObLQOvJ7/1xc5dB2lGh6pRX2HPJ+hd+Or6Vrh1pZ dWT9Qd89UDgylY+/j88ejQxiCohdbcww8saPa0LoyqXVIj1k+trUBboN69ki160JmuQf9Y VGn9nebooNYpNhbrc3OAbs8nHM4conaXVu1z2f0wsKH/EFAIWQn9OFulJrBNgMbSQakN9Z 6DsDeNwxkVg+Twu63ofcWzmuUtijW89VBVwoJwsbUWQoO85GPwMuORcnSgH0LQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4fJD4r1C2Mz1LtT for ; Sat, 21 Feb 2026 17:05:00 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 34ae3 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Sat, 21 Feb 2026 17:05:00 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Rick Macklem Subject: git: 86a6407d028d - stable/15 - linux_file.c: Fix handling of NFS getdents() emulation List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: rmacklem X-Git-Repository: src X-Git-Refname: refs/heads/stable/15 X-Git-Reftype: branch X-Git-Commit: 86a6407d028d9a58c93c7d1ac54e737d78b1aaaf Auto-Submitted: auto-generated Date: Sat, 21 Feb 2026 17:05:00 +0000 Message-Id: <6999e5bc.34ae3.1b88ca7d@gitrepo.freebsd.org> The branch stable/15 has been updated by rmacklem: URL: https://cgit.FreeBSD.org/src/commit/?id=86a6407d028d9a58c93c7d1ac54e737d78b1aaaf commit 86a6407d028d9a58c93c7d1ac54e737d78b1aaaf Author: Rick Macklem AuthorDate: 2026-02-07 22:12:55 +0000 Commit: Rick Macklem CommitDate: 2026-02-21 17:03:38 +0000 linux_file.c: Fix handling of NFS getdents() emulation Bugzilla PR#292282 reports a problem, where a Linux binary running in the Linuxulator gets bogus entries in a readdir()/getdents() reply when the directory is an NFS mount. This appears to be caused by the NFS client including entries with d_fileno == 0, which are always ignored by BSD, but are not ignored by Linux. This patch filters out the "d_fileno == 0" entries and the reporter of the bugzilla PR notes that it fixes the problem for him. It could be argued that the NFS client should filter out the "d_fileno == 0" entries, but the NFS client readdir code is "fragile" and any change to it runs a significant risk of causing regression type problems. As such, since the LInuxulator is already broken for this case, it seems safer to filter them out there. PR: 292282 (cherry picked from commit 110f2567cb51f1eeddbd5d9937000ad64f6dc746) --- sys/compat/linux/linux_file.c | 178 ++++++++++++++++++++++++++---------------- 1 file changed, 110 insertions(+), 68 deletions(-) diff --git a/sys/compat/linux/linux_file.c b/sys/compat/linux/linux_file.c index 8413a481c5a9..ca089585bb95 100644 --- a/sys/compat/linux/linux_file.c +++ b/sys/compat/linux/linux_file.c @@ -398,12 +398,50 @@ struct l_dirent64 { roundup(offsetof(struct l_dirent64, d_name) + (namlen) + 1, \ sizeof(uint64_t)) +/* + * Do kern_getdirentries() and then skip over any invalid entries. + * (Repeat, if there are no valid entries.) + * Adjust bufp and lenp. + */ +static int +linux_getdirentries(struct thread *td, int fd, caddr_t *bufp, int buflen, + off_t *basep, int *lenp) +{ + struct dirent *bdp; + caddr_t buf; + int error, len; + + /* Loop around until a valid entry is found or at EOF. */ + for (;;) { + error = kern_getdirentries(td, fd, *bufp, buflen, + basep, NULL, UIO_SYSSPACE); + if (error != 0) + return (error); + len = td->td_retval[0]; + if (len == 0) { + *lenp = 0; + return (0); + } + buf = *bufp; + while (len > 0) { + bdp = (struct dirent *)buf; + if (bdp->d_fileno != 0) { + *bufp = buf; + *lenp = len; + return (0); + } + buf += bdp->d_reclen; + len -= bdp->d_reclen; + } + } +} + #ifdef LINUX_LEGACY_SYSCALLS int linux_getdents(struct thread *td, struct linux_getdents_args *args) { struct dirent *bdp; - caddr_t inp, buf; /* BSD-format */ + caddr_t inp, buf, bufsav; /* BSD-format */ int len, reclen; /* BSD-format */ caddr_t outp; /* Linux-format */ int resid, linuxreclen; /* Linux-format */ @@ -413,11 +451,11 @@ linux_getdents(struct thread *td, struct linux_getdents_args *args) int buflen, error; size_t retval; - buflen = min(args->count, MAXBSIZE); - buf = malloc(buflen, M_LINUX, M_WAITOK); + buflen = min(roundup2(args->count, DEV_BSIZE), MAXBSIZE); + bufsav = buf = malloc(buflen, M_LINUX, M_WAITOK); - error = kern_getdirentries(td, args->fd, buf, buflen, - &base, NULL, UIO_SYSSPACE); + error = linux_getdirentries(td, args->fd, &buf, buflen, + &base, &len); if (error != 0) { error = linux_getdents_error(td, args->fd, error); goto out1; @@ -425,7 +463,6 @@ linux_getdents(struct thread *td, struct linux_getdents_args *args) lbuf = malloc(LINUX_RECLEN(LINUX_NAME_MAX), M_LINUX, M_WAITOK | M_ZERO); - len = td->td_retval[0]; inp = buf; outp = (caddr_t)args->dent; resid = args->count; @@ -434,44 +471,47 @@ linux_getdents(struct thread *td, struct linux_getdents_args *args) while (len > 0) { bdp = (struct dirent *) inp; reclen = bdp->d_reclen; - linuxreclen = LINUX_RECLEN(bdp->d_namlen); - /* - * No more space in the user supplied dirent buffer. - * Return EINVAL. - */ - if (resid < linuxreclen) { - error = EINVAL; - goto out; + /* Copy a valid entry out. */ + if (bdp->d_fileno != 0) { + linuxreclen = LINUX_RECLEN(bdp->d_namlen); + /* + * No more space in the user supplied dirent buffer. + * Return EINVAL. + */ + if (resid < linuxreclen) { + error = EINVAL; + goto out; + } + + linux_dirent = (struct l_dirent*)lbuf; + linux_dirent->d_ino = bdp->d_fileno; + linux_dirent->d_off = bdp->d_off; + linux_dirent->d_reclen = linuxreclen; + /* + * Copy d_type to last byte of l_dirent buffer + */ + lbuf[linuxreclen - 1] = bdp->d_type; + strlcpy(linux_dirent->d_name, bdp->d_name, + linuxreclen - offsetof(struct l_dirent, d_name)-1); + error = copyout(linux_dirent, outp, linuxreclen); + if (error != 0) + goto out; + retval += linuxreclen; + outp += linuxreclen; + resid -= linuxreclen; } - linux_dirent = (struct l_dirent*)lbuf; - linux_dirent->d_ino = bdp->d_fileno; - linux_dirent->d_off = bdp->d_off; - linux_dirent->d_reclen = linuxreclen; - /* - * Copy d_type to last byte of l_dirent buffer - */ - lbuf[linuxreclen - 1] = bdp->d_type; - strlcpy(linux_dirent->d_name, bdp->d_name, - linuxreclen - offsetof(struct l_dirent, d_name)-1); - error = copyout(linux_dirent, outp, linuxreclen); - if (error != 0) - goto out; - inp += reclen; base += reclen; len -= reclen; - retval += linuxreclen; - outp += linuxreclen; - resid -= linuxreclen; } td->td_retval[0] = retval; out: free(lbuf, M_LINUX); out1: - free(buf, M_LINUX); + free(bufsav, M_LINUX); return (error); } #endif @@ -480,7 +520,7 @@ int linux_getdents64(struct thread *td, struct linux_getdents64_args *args) { struct dirent *bdp; - caddr_t inp, buf; /* BSD-format */ + caddr_t inp, buf, bufsav; /* BSD-format */ int len, reclen; /* BSD-format */ caddr_t outp; /* Linux-format */ int resid, linuxreclen; /* Linux-format */ @@ -489,11 +529,11 @@ linux_getdents64(struct thread *td, struct linux_getdents64_args *args) int buflen, error; size_t retval; - buflen = min(args->count, MAXBSIZE); - buf = malloc(buflen, M_LINUX, M_WAITOK); + buflen = min(roundup2(args->count, DEV_BSIZE), MAXBSIZE); + bufsav = buf = malloc(buflen, M_LINUX, M_WAITOK); - error = kern_getdirentries(td, args->fd, buf, buflen, - &base, NULL, UIO_SYSSPACE); + error = linux_getdirentries(td, args->fd, &buf, buflen, + &base, &len); if (error != 0) { error = linux_getdents_error(td, args->fd, error); goto out1; @@ -502,7 +542,6 @@ linux_getdents64(struct thread *td, struct linux_getdents64_args *args) linux_dirent64 = malloc(LINUX_RECLEN64(LINUX_NAME_MAX), M_LINUX, M_WAITOK | M_ZERO); - len = td->td_retval[0]; inp = buf; outp = (caddr_t)args->dirent; resid = args->count; @@ -511,40 +550,43 @@ linux_getdents64(struct thread *td, struct linux_getdents64_args *args) while (len > 0) { bdp = (struct dirent *) inp; reclen = bdp->d_reclen; - linuxreclen = LINUX_RECLEN64(bdp->d_namlen); - /* - * No more space in the user supplied dirent buffer. - * Return EINVAL. - */ - if (resid < linuxreclen) { - error = EINVAL; - goto out; + /* Copy a valid entry out. */ + if (bdp->d_fileno != 0) { + linuxreclen = LINUX_RECLEN64(bdp->d_namlen); + /* + * No more space in the user supplied dirent buffer. + * Return EINVAL. + */ + if (resid < linuxreclen) { + error = EINVAL; + goto out; + } + + linux_dirent64->d_ino = bdp->d_fileno; + linux_dirent64->d_off = bdp->d_off; + linux_dirent64->d_reclen = linuxreclen; + linux_dirent64->d_type = bdp->d_type; + strlcpy(linux_dirent64->d_name, bdp->d_name, + linuxreclen - offsetof(struct l_dirent64, d_name)); + error = copyout(linux_dirent64, outp, linuxreclen); + if (error != 0) + goto out; + retval += linuxreclen; + outp += linuxreclen; + resid -= linuxreclen; } - linux_dirent64->d_ino = bdp->d_fileno; - linux_dirent64->d_off = bdp->d_off; - linux_dirent64->d_reclen = linuxreclen; - linux_dirent64->d_type = bdp->d_type; - strlcpy(linux_dirent64->d_name, bdp->d_name, - linuxreclen - offsetof(struct l_dirent64, d_name)); - error = copyout(linux_dirent64, outp, linuxreclen); - if (error != 0) - goto out; - inp += reclen; base += reclen; len -= reclen; - retval += linuxreclen; - outp += linuxreclen; - resid -= linuxreclen; } td->td_retval[0] = retval; out: free(linux_dirent64, M_LINUX); out1: - free(buf, M_LINUX); + free(bufsav, M_LINUX); return (error); } @@ -553,22 +595,22 @@ int linux_readdir(struct thread *td, struct linux_readdir_args *args) { struct dirent *bdp; - caddr_t buf; /* BSD-format */ + caddr_t buf, bufsav; /* BSD-format */ int linuxreclen; /* Linux-format */ off_t base; struct l_dirent *linux_dirent; /* Linux-format */ - int buflen, error; + int buflen, error, len; - buflen = sizeof(*bdp); - buf = malloc(buflen, M_LINUX, M_WAITOK); + buflen = DEV_BSIZE; + bufsav = buf = malloc(buflen, M_LINUX, M_WAITOK); - error = kern_getdirentries(td, args->fd, buf, buflen, - &base, NULL, UIO_SYSSPACE); + error = linux_getdirentries(td, args->fd, &buf, buflen, + &base, &len); if (error != 0) { error = linux_getdents_error(td, args->fd, error); goto out; } - if (td->td_retval[0] == 0) + if (len == 0) goto out; linux_dirent = malloc(LINUX_RECLEN(LINUX_NAME_MAX), M_LINUX, @@ -588,7 +630,7 @@ linux_readdir(struct thread *td, struct linux_readdir_args *args) free(linux_dirent, M_LINUX); out: - free(buf, M_LINUX); + free(bufsav, M_LINUX); return (error); } #endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */