From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E0F0241837E for ; Thu, 24 Sep 2026 17:10:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790269816; cv=none; b=K3htEcP2nmyNzerw3Pxmhd6IUq+x3g5Fb3yef0Po9jrX0sRkjRlYIgVDEFODnvlxNwkoPHEk0M4jlSv1A5CVlweCvIZwjISKoUSpjDazcwBbyj8bUEw1ZNrwVUEtiju7OIEZQReZ9rnG+KRyJmEOwEk5OfZ0+FInduVWMxnAB4Y= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790269816; c=relaxed/simple; bh=sd7URNQwVLUaNrGCogVVMghxSBWCbocCzg0QhT2QDBY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=FgnIKQhUnZVBgSBnktwmoElYGzgQy8s9Z9sGH6GYHJxrlo2OQRpOF+nyGdyVzkyBQn12AVJe5XVwGUCqTT29HPTZlXBSJ1DWDkePT2XKkMlSsqPAjMLYYFYuGyXVRuw9T1Ik3XzDdtstf1Y9pMYpqPAI0qKlPXCkbYVds+Iz+oI= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=F5/Gln0w; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="F5/Gln0w" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 499C71F00898; Thu, 24 Sep 2026 17:10:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1790269814; bh=RB7Uz0Sp1o1u2EPrk3gSN487kl3ugVDMkpkwUdQUBSg=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=F5/Gln0wc4QjHlEXoFcEMkErX+frtMlIazw+FwAd9Els8W4luV8FdRl3JoiQqi8l2 NkQlBhTvHNdRYhot8pJq1ZAWUXBMsHW2bBpPMAD5QIXe5cSFzwz0TdwswDsmUtDiUP iT5b2z5K3nSahj+iY+SuJ+fDMOXhkbtjfQq6alX1qfXVAcZciv3geK/apsajjbfF9+ MM3/easZsW/vjVAEY15SHLZj9yRfJvW87/4X3MZl4edSbkYKJnT7myGZ++JuMfoqZb Oe+dWjQJDve+fCdWP5z+C/7QpKX0Ind5X4R+SuFzg4meIA9D9D3CV2i+GeaC4ut91u wkVGzZMLSLOGQ== From: Chuck Lever To: NeilBrown , Jeff Layton , Olga Kornievskaia , Dai Ngo , Tom Talpey Cc: Subject: [PATCH v2 01/33] NFSD: Report a first-batch readdir error in the reply status Date: Thu, 24 Sep 2026 13:09:40 -0400 Message-ID: <20260924171012.3978-2-cel@kernel.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260924171012.3978-1-cel@kernel.org> References: <20260924171012.3978-1-cel@kernel.org> Precedence: bulk X-Mailing-List: linux-nfs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit The NFSv2 READDIR encoder pulls directory entries while the reply is encoded, after the status word is on the wire. nfsd_readdir_open() does not read from the directory, so an iterate_dir() failure on the first batch surfaces only in nfsd_readdir_next(), where nothing can turn it into a status. The reply carries NFS_OK, no entries, and eof false. The client resends the same cookie and receives the same reply, so a directory that fails with -EIO or -ENOENT traps it in a loop. Read the first batch in nfsd_readdir_open() so the error selects the reply status before any of it is committed. An error on a later batch still cuts the list short with eof false. The client's follow-up request then starts from that cookie and gets the error in its status. Signed-off-by: Chuck Lever --- fs/nfsd/vfs.c | 76 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 4584d5b94fee..d46257dd8c61 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -2376,6 +2376,33 @@ static bool nfsd_buffered_filldir(struct dir_context *ctx, const char *name, return true; } +/* + * Read the next batch of entries. Returns false at end of directory + * (@iter->eof set) or on a filesystem error (@iter->host_err set). + */ +static bool nfsd_readdir_fill(struct nfsd_readdir_iter *iter) +{ + /* Between batches the resume cookie advances past the last. */ + if (iter->batched) + iter->offset = vfs_llseek(iter->file, 0, SEEK_CUR); + + iter->used = 0; + iter->full = 0; + iter->host_err = iterate_dir(iter->file, &iter->ctx); + if (iter->full) + iter->host_err = 0; + if (iter->host_err < 0) + return false; + if (!iter->used) { + iter->eof = true; + return false; + } + iter->batched = true; + iter->pos = iter->page; + iter->remaining = iter->used; + return true; +} + /** * nfsd_readdir_open - open a directory for streaming readdir * @rqstp: RPC transaction context @@ -2383,13 +2410,15 @@ static bool nfsd_buffered_filldir(struct dir_context *ctx, const char *name, * @offsetp: seek offset at which to resume reading * @iter: OUT: directory reader to initialize * - * On success the directory is open and positioned at @offsetp, and - * @iter is ready for nfsd_readdir_next(). The caller must release - * @iter with nfsd_readdir_close() once done, including on error paths - * that follow a successful open. + * On success the directory is open and positioned at @offsetp, the + * first batch of entries has been read, and @iter is ready for + * nfsd_readdir_next(). The caller must release @iter with + * nfsd_readdir_close() once done, including on error paths that + * follow a successful open. * - * Return: nfs_ok on success, otherwise an nfsstat code. On error @iter - * is left safe to pass to nfsd_readdir_close(). + * Return: nfs_ok on success, otherwise an nfsstat code, including the + * error from reading the first batch. On error @iter is left safe to + * pass to nfsd_readdir_close(). */ __be32 nfsd_readdir_open(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp, struct nfsd_readdir_iter *iter) @@ -2425,6 +2454,17 @@ __be32 nfsd_readdir_open(struct svc_rqst *rqstp, struct svc_fh *fhp, iter->file = file; iter->fhp = fhp; iter->offset = offset; + + /* + * The first batch is read here so a filesystem error surfaces + * in the reply status. Once the encoder has committed nfs_ok, + * an error can only be reported as a short list. + */ + if (!nfsd_readdir_fill(iter) && iter->host_err) { + err = nfserrno(iter->host_err); + nfsd_readdir_close(iter); + return err; + } return nfs_ok; out_close: @@ -2449,26 +2489,10 @@ struct buffered_dirent *nfsd_readdir_next(struct nfsd_readdir_iter *iter) struct buffered_dirent *de; unsigned int reclen; - while (iter->remaining <= 0) { - /* Between batches the resume cookie advances past the last. */ - if (iter->batched) - iter->offset = vfs_llseek(iter->file, 0, SEEK_CUR); - - iter->used = 0; - iter->full = 0; - iter->host_err = iterate_dir(iter->file, &iter->ctx); - if (iter->full) - iter->host_err = 0; - if (iter->host_err < 0) - return NULL; - if (!iter->used) { - iter->eof = true; - return NULL; - } - iter->batched = true; - iter->pos = iter->page; - iter->remaining = iter->used; - } + if (iter->eof || iter->host_err) + return NULL; + if (iter->remaining <= 0 && !nfsd_readdir_fill(iter)) + return NULL; de = (struct buffered_dirent *)iter->pos; iter->offset = de->offset; -- 2.55.0