From: Jeff Layton <jlayton@kernel.org>
To: NeilBrown <neilb@suse.de>
Cc: Chuck Lever <chuck.lever@oracle.com>,
linux-nfs@vger.kernel.org, Olga Kornievskaia <kolga@netapp.com>,
Dai Ngo <Dai.Ngo@oracle.com>, Tom Talpey <tom@talpey.com>
Subject: Re: [PATCH 3/3] nfsd: move error choice for incorrect object types to version-specific code.
Date: Fri, 09 Aug 2024 08:34:43 -0400 [thread overview]
Message-ID: <6da76065c364e3585f89b13e57f9727ffd59ef3f.camel@kernel.org> (raw)
In-Reply-To: <172315440855.6062.9482773808089698009@noble.neil.brown.name>
On Fri, 2024-08-09 at 08:00 +1000, NeilBrown wrote:
> On Thu, 08 Aug 2024, Jeff Layton wrote:
> > On Thu, 2024-08-08 at 21:40 +1000, NeilBrown wrote:
> > > On Thu, 08 Aug 2024, Jeff Layton wrote:
> > > > On Mon, 2024-07-29 at 11:47 +1000, NeilBrown wrote:
> > > > > If an NFS operation expect a particular sort of object (file, dir, link,
> > > > > etc) but gets a file handle for a different sort of object, it must
> > > > > return an error. The actual error varies among version in no-trivial
> > > > > ways.
> > > > >
> > > > > For v2 and v3 there are ISDIR and NOTDIR errors, and for any else, only
> > > > > INVAL is suitable.
> > > > >
> > > > > For v4.0 there is also NFS4ERR_SYMLINK which should be used if a SYMLINK
> > > > > was found when not expected. This take precedence over NOTDIR.
> > > > >
> > > > > For v4.1+ there is also NFS4ERR_WRONG_TYPE which should be used in
> > > > > preference to EINVAL when none of the specific error codes apply.
> > > > >
> > > > > When nfsd_mode_check() finds a symlink where it expect a directory it
> > > > > needs to return an error code that can be converted to NOTDIR for v2 or
> > > > > v3 but will be SYMLINK for v4. It must be different from the error
> > > > > code returns when it finds a symlink but expects a regular file - that
> > > > > must be converted to EINVAL or SYMLINK.
> > > > >
> > > > > So we introduce an internal error code nfserr_symlink_not_dir which each
> > > > > version converts as appropriate.
> > > > >
> > > > > We also allow nfserr_wrong_type to be returned by
> > > > > nfsd_check_obj_is_reg() in nfsv4 code) and nfsd_mode_check(). This a
> > > > > behavioural change as nfsd_check_obj_is_reg() would previously return
> > > > > nfserr_symiink for non-directory objects that aren't regular files. Now
> > > > > it will return nfserr_wrong_type for objects that aren't regular,
> > > > > directory, symlink (so char-special, block-special, sockets), which is
> > > > > mapped to nfserr_inval for NFSv4.0. This should not be a problem as the
> > > > > behaviour is supported by RFCs.
> > > > >
> > > > > As a result of these changes, nfsd_mode_check() doesn't need an rqstp
> > > > > arg any more.
> > > > >
> > > > > Signed-off-by: NeilBrown <neilb@suse.de>
> > > > > ---
> > > > > fs/nfsd/nfs3proc.c | 8 ++++++++
> > > > > fs/nfsd/nfs4proc.c | 24 ++++++++++++++++--------
> > > > > fs/nfsd/nfsd.h | 5 +++++
> > > > > fs/nfsd/nfsfh.c | 16 +++++++---------
> > > > > fs/nfsd/nfsproc.c | 7 +++++++
> > > > > 5 files changed, 43 insertions(+), 17 deletions(-)
> > > > >
> > > > > diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
> > > > > index 31bd9bcf8687..ac7ee24415a3 100644
> > > > > --- a/fs/nfsd/nfs3proc.c
> > > > > +++ b/fs/nfsd/nfs3proc.c
> > > > > @@ -38,6 +38,14 @@ static __be32 map_status(__be32 status)
> > > > > case nfserr_file_open:
> > > > > status = nfserr_acces;
> > > > > break;
> > > > > +
> > > > > + case nfserr_symlink_not_dir:
> > > > > + status = nfserr_notdir;
> > > > > + break;
> > > > > + case nfserr_symlink:
> > > > > + case nfserr_wrong_type:
> > > > > + status = nfserr_inval;
> > > > > + break;
> > > > > }
> > > > > return status;
> > > > > }
> > > > > diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
> > > > > index 46bd20fe5c0f..cc715438e77a 100644
> > > > > --- a/fs/nfsd/nfs4proc.c
> > > > > +++ b/fs/nfsd/nfs4proc.c
> > > > > @@ -166,14 +166,9 @@ static __be32 nfsd_check_obj_isreg(struct svc_fh *fh)
> > > > > return nfs_ok;
> > > > > if (S_ISDIR(mode))
> > > > > return nfserr_isdir;
> > > > > - /*
> > > > > - * Using err_symlink as our catch-all case may look odd; but
> > > > > - * there's no other obvious error for this case in 4.0, and we
> > > > > - * happen to know that it will cause the linux v4 client to do
> > > > > - * the right thing on attempts to open something other than a
> > > > > - * regular file.
> > > > > - */
> > > > > - return nfserr_symlink;
> > > > > + if (S_ISLNK(mode))
> > > > > + return nfserr_symlink;
> > > > > + return nfserr_wrong_type;
> > > > > }
> > > > >
> > > > > static void nfsd4_set_open_owner_reply_cache(struct nfsd4_compound_state *cstate, struct nfsd4_open *open, struct svc_fh *resfh)
> > > > > @@ -184,6 +179,17 @@ static void nfsd4_set_open_owner_reply_cache(struct nfsd4_compound_state *cstate
> > > > > &resfh->fh_handle);
> > > > > }
> > > > >
> > > > > +static __be32 map_status(__be32 status, int minor)
> > > > > +{
> > > > > + if (status == nfserr_wrong_type &&
> > > > > + minor == 0)
> > > > > + /* RFC5661 - 15.1.2.9 */
> > > > > + status = nfserr_inval;
> > > > > +
> > > > > + if (status == nfserr_symlink_not_dir)
> > > > > + status = nfserr_symlink;
> > > > > + return status;
> > > > > +}
> > > > > static inline bool nfsd4_create_is_exclusive(int createmode)
> > > > > {
> > > > > return createmode == NFS4_CREATE_EXCLUSIVE ||
> > > > > @@ -2798,6 +2804,8 @@ nfsd4_proc_compound(struct svc_rqst *rqstp)
> > > > > nfsd4_encode_replay(resp->xdr, op);
> > > > > status = op->status = op->replay->rp_status;
> > > > > } else {
> > > > > + op->status = map_status(op->status,
> > > > > + cstate->minorversion);
> > > > > nfsd4_encode_operation(resp, op);
> > > > > status = op->status;
> > > > > }
> > > > > diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
> > > > > index 593c34fd325a..3c8c8da063b0 100644
> > > > > --- a/fs/nfsd/nfsd.h
> > > > > +++ b/fs/nfsd/nfsd.h
> > > > > @@ -349,6 +349,11 @@ enum {
> > > > > NFSERR_REPLAY_CACHE,
> > > > > #define nfserr_replay_cache cpu_to_be32(NFSERR_REPLAY_CACHE)
> > > > >
> > > > > +/* symlink found where dir expected - handled differently to
> > > > > + * other symlink found errors by NFSv3.
> > > > > + */
> > > > > + NFSERR_SYMLINK_NOT_DIR,
> > > > > +#define nfserr_symlink_not_dir cpu_to_be32(NFSERR_SYMLINK_NOT_DIR)
> > > > > };
> > > > >
> > > > > /* Check for dir entries '.' and '..' */
> > > > > diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
> > > > > index 0130103833e5..8cd70f93827c 100644
> > > > > --- a/fs/nfsd/nfsfh.c
> > > > > +++ b/fs/nfsd/nfsfh.c
> > > > > @@ -62,8 +62,7 @@ static int nfsd_acceptable(void *expv, struct dentry *dentry)
> > > > > * the write call).
> > > > > */
> > > > > static inline __be32
> > > > > -nfsd_mode_check(struct svc_rqst *rqstp, struct dentry *dentry,
> > > > > - umode_t requested)
> > > > > +nfsd_mode_check(struct dentry *dentry, umode_t requested)
> > > > > {
> > > > > umode_t mode = d_inode(dentry)->i_mode & S_IFMT;
> > > > >
> > > > > @@ -76,17 +75,16 @@ nfsd_mode_check(struct svc_rqst *rqstp, struct dentry *dentry,
> > > > > }
> > > > > return nfs_ok;
> > > > > }
> > > > > - /*
> > > > > - * v4 has an error more specific than err_notdir which we should
> > > > > - * return in preference to err_notdir:
> > > > > - */
> > > > > - if (rqstp->rq_vers == 4 && mode == S_IFLNK)
> > > > > + if (mode == S_IFLNK) {
> > > > > + if (requested == S_IFDIR)
> > > > > + return nfserr_symlink_not_dir;
> > > > > return nfserr_symlink;
> > > > > + }
> > > > > if (requested == S_IFDIR)
> > > > > return nfserr_notdir;
> > > > > if (mode == S_IFDIR)
> > > > > return nfserr_isdir;
> > > > > - return nfserr_inval;
> > > > > + return nfserr_wrong_type;
> > > > > }
> > > > >
> > > > > static bool nfsd_originating_port_ok(struct svc_rqst *rqstp, int flags)
> > > > > @@ -362,7 +360,7 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, int access)
> > > > > if (error)
> > > > > goto out;
> > > > >
> > > > > - error = nfsd_mode_check(rqstp, dentry, type);
> > > > > + error = nfsd_mode_check(dentry, type);
> > > > > if (error)
> > > > > goto out;
> > > > >
> > > > > diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
> > > > > index cb7099c6dc78..3d65ab558091 100644
> > > > > --- a/fs/nfsd/nfsproc.c
> > > > > +++ b/fs/nfsd/nfsproc.c
> > > > > @@ -25,6 +25,13 @@ static __be32 map_status(__be32 status)
> > > > > case nfserr_file_open:
> > > > > status = nfserr_acces;
> > > > > break;
> > > > > + case nfserr_symlink_not_dir:
> > > > > + status = nfserr_notdir;
> > > > > + break;
> > > > > + case nfserr_symlink:
> > > > > + case nfserr_wrong_type:
> > > > > + status = nfserr_inval;
> > > > > + break;
> > > > > }
> > > > > return status;
> > > > > }
> > > >
> > > > Hi Neil,
> > > >
> > > > I'm seeing a set of failures in pynfs with this patch (json results
> > > > attached). I haven't looked in detail yet, but we should probably drop
> > > > this one for now.
> > >
> > > Most of the complaints are because pynfs is expecting the incorrect
> > > error code that nfsd currently returns, rather than the correct one that
> > > my patch makes it return.
> > >
> > > There is one where the internal error code of 10101 leaks out. That is
> > > NFSERR_SYMLNK_NOT_DIR.
> > > That certainly requires investigation.
> > >
> >
> > I'm not sure these error code changes are correct, now that I look. For
> > instance:
> >
> > {
> > "classname": "st_readlink",
> > "code": "RDLK2r",
> > "failure": {
> > "err": "Traceback (most recent call last):\n File \"/data/pynfs/nfs4.0/lib/testmod.py\", line 234, in run\n self.runtest(self, environment)\n File \"/data/pynfs/nfs4.0/servertests/st_readlink.py\", line 29, in testFile\n check(res, NFS4ERR_INVAL, \"READLINK on non-symlink objects\")\n File \"/data/pynfs/nfs4.0/servertests/environment.py\", line 270, in check\n raise testmod.FailureException(msg)\ntestmod.FailureException: READLINK on non-symlink objects should return NFS4ERR_INVAL, instead got NFS4ERR_WRONG_TYPE\n",
> > "message": "READLINK on non-symlink objects should return NFS4ERR_INVAL, instead got NFS4ERR_WRONG_TYPE"
> > },
> > "name": "testFile",
> > "time": "0.003440380096435547"
> > },
> >
> >
> > Looking at RFC7530, the READLINK section (16.25.5) says:
> >
> > The READLINK operation is only allowed on objects of type NF4LNK.
> > The server should return the error NFS4ERR_INVAL if the object is not
> > of type NF4LNK.
>
> RFC7530 is for NFSv4.0 It doesn't have NFS4ERR_WRONG_TYPE. So if this
> is a 4.0 test then that is indeed a bug. If the "nfs4.0" in the File
> path the only hit that this was 4.0 rather than 4.1?
> (RFC8881 declares this should be NFS4ERR_WRONG_TYPE for 4.1)
>
This was a v4.0 pynfs run, so yes it appears this was leaking out
somewhere.
> >
> > Several OPEN cases have errors similar to this one:
> >
> > {
> > "classname": "st_open",
> > "code": "OPEN7s",
> > "failure": {
> > "err": "Traceback (most recent call last):\n File \"/data/pynfs/nfs4.0/lib/testmod.py\", line 234, in run\n self.runtest(self, environment)\n File \"/data/pynfs/nfs4.0/servertests/st_open.py\", line 183, in testSocket\n check(res, NFS4ERR_SYMLINK, \"Trying to OPEN socket\")\n File \"/data/pynfs/nfs4.0/servertests/environment.py\", line 270, in check\n raise testmod.FailureException(msg)\ntestmod.FailureException: Trying to OPEN socket should return NFS4ERR_SYMLINK, instead got NFS4ERR_INVAL\n",
> > "message": "Trying to OPEN socket should return NFS4ERR_SYMLINK, instead got NFS4ERR_INVAL"
> > },
> > "name": "testSocket",
> > "time": "0.006421566009521484"
> > },
> >
> > RFC7530, 16.16.6:
> >
> > If the component provided to OPEN resolves to something other than a
> > regular file (or a named attribute), an error will be returned to the
> > client. If it is a directory, NFS4ERR_ISDIR is returned; otherwise,
> > NFS4ERR_SYMLINK is returned. Note that NFS4ERR_SYMLINK is returned
> > for both symlinks and for special files of other types; NFS4ERR_INVAL
> > would be inappropriate, since the arguments provided by the client
> > were correct, and the client cannot necessarily know at the time it
> > sent the OPEN that the component would resolve to a non-regular file.
>
> Ahhh - thanks. The comments in the code seemed to suggest that the RFC
> wasn't specific. I guess I misread them.
>
> Thanks for the more careful analysis. I'll aim to have an update on
> Monday.
>
Sounds great!
> >
> > --
> > Jeff Layton <jlayton@kernel.org>
> >
> >
>
--
Jeff Layton <jlayton@kernel.org>
next prev parent reply other threads:[~2024-08-09 12:34 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-07-29 1:47 [PATCH 0/3] nfsd: move error code mapping to per-version code NeilBrown
2024-07-29 1:47 ` [PATCH 1/3] nfsd: Move error code mapping to per-version proc code NeilBrown
2024-07-29 15:04 ` Christoph Hellwig
2024-07-29 1:47 ` [PATCH 2/3] nfsd: be more systematic about selecting error codes for internal use NeilBrown
2024-07-29 15:05 ` Christoph Hellwig
2024-07-29 1:47 ` [PATCH 3/3] nfsd: move error choice for incorrect object types to version-specific code NeilBrown
2024-07-29 15:06 ` Christoph Hellwig
2024-08-08 10:37 ` Jeff Layton
2024-08-08 11:40 ` NeilBrown
2024-08-08 12:01 ` Jeff Layton
2024-08-08 22:00 ` NeilBrown
2024-08-09 12:34 ` Jeff Layton [this message]
2024-07-29 12:36 ` [PATCH 0/3] nfsd: move error code mapping to per-version code Jeff Layton
2024-07-29 15:16 ` Chuck Lever
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=6da76065c364e3585f89b13e57f9727ffd59ef3f.camel@kernel.org \
--to=jlayton@kernel.org \
--cc=Dai.Ngo@oracle.com \
--cc=chuck.lever@oracle.com \
--cc=kolga@netapp.com \
--cc=linux-nfs@vger.kernel.org \
--cc=neilb@suse.de \
--cc=tom@talpey.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox