From nobody Sat Feb 7 22:15:09 2026 X-Original-To: dev-commits-src-main@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 4f7ldB3Xp1z6RWpH for ; Sat, 07 Feb 2026 22:15:10 +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 "R13" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4f7ld96y0xz3bKN for ; Sat, 07 Feb 2026 22:15:09 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1770502510; 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=MHbVfTmwo/R9HcenZXusy6dsLKMqO4O0J0sIb7Y0dHw=; b=XURGjisY2ZgmVO634C/5ejA3vQ3GI8id9ByM2xvhmpKs/NYRVcHGBziocq5hKd2JXTcPxa kZtfTYfWnQj5zSXAd2+vSKWMniRw+GhmP8UZ0m2Oa++9mqqq9Nbvy1say0vZLKRADWeV7f fZZ1MsxlkT/VSv6jsDK85KfO1BLhZxCb2Wt0sgbx8BRJNvP/U9NgtFup4DOY7QXrKEssm+ 71urn3dEhnTOw+6far05Hjsin3D+raHWJ7SCyWHgsSxNb1X+SNh2qkNxFW/tA8oR3InUWp C8jd8yg7VHgpmzzfk4K58Lht3esLF6Hztr38X62E2fucoVmhzTD6lLOfkDfNfQ== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1770502510; a=rsa-sha256; cv=none; b=UtdrSCjWIYRlzdCpXb7n2+D3ZoI0BueZRZwI0RNUdPBd3btjzg1u8X5XfPSkhl5Ru4s3FZ leOEhuIp+blPcPm1+IGl1i75VW2M7KtUVWV5EAskFln8XAi6mcMWL5EdU3kn3j67hRtOYK VilLVe8z9vORaNkqGpdXFwljYodHoHMVfZea0Nkv8NKzHKCjpfed5qPqxXW8a8ZfzyOfjZ ACjGso35sIH0amQKK9vqdokek5r4ygS8OOnz93NX201K2fwKx+YvDDx75xuYSPPjYlb6q4 Lowz9pSeZdezLYg5ZcnV9zsiMaWEeKv5p/rOP24hp8IdvMaBucd3Uih69S1lkQ== 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=1770502510; 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=MHbVfTmwo/R9HcenZXusy6dsLKMqO4O0J0sIb7Y0dHw=; b=L7GINsplPwOBseRq2fVTt7DB5Y0XUgKiWDTgJomuv8plQMz9KMsxGCy/VYgfPqoWBJgV/o B0Hh7bDx9zg0dq8i5G4KD9jxvQKqj16v1cNzhpF2+ZO4BLaXjU1iaJ6iEdQH3k6CGiekIZ K7RR2xye7d5bygO9/W00UPF/WNMRnzSlwI7vbQw3Crd/GfBJhC8hM0+PNEy273KsR/lIYR /cjYVIJk1VK8ReKrNCQfjiDbjwKNfAowKHNp1SK9QjWxrbbTUyltQPu5i6nHBJKDozImyV 2v4kkx/Ke2AE+yAFlVkRgP6HlstZPpUKBjUv52wWL5ri0T8WVMsgbm3q6Rd1Mg== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4f7ld95frMz4lj for ; Sat, 07 Feb 2026 22:15:09 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 1bcdf by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Sat, 07 Feb 2026 22:15:09 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Rick Macklem Subject: git: 110f2567cb51 - main - linux_file.c: Fix handling of NFS getdents() emulation List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@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/main X-Git-Reftype: branch X-Git-Commit: 110f2567cb51f1eeddbd5d9937000ad64f6dc746 Auto-Submitted: auto-generated Date: Sat, 07 Feb 2026 22:15:09 +0000 Message-Id: <6987b96d.1bcdf.6467a437@gitrepo.freebsd.org> The branch main has been updated by rmacklem: URL: https://cgit.FreeBSD.org/src/commit/?id=110f2567cb51f1eeddbd5d9937000ad64f6dc746 commit 110f2567cb51f1eeddbd5d9937000ad64f6dc746 Author: Rick Macklem AuthorDate: 2026-02-07 22:12:55 +0000 Commit: Rick Macklem CommitDate: 2026-02-07 22:12:55 +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 Tested by: Jerry Williams Reviewed by: markj MFC after: 2 weeks Differential Revision: https://reviews.freebsd.org/D54679 --- 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 daddafa325ad..96326b894d05 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) */