mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-08-09 06:14:34 +02:00
The hard-coded MSDOS_SUPER_MAGIC check in nfsd3_proc_pathconf() only recognizes FAT filesystems as case-insensitive. Modern filesystems like F2FS, exFAT, and CIFS support case-insensitive directories, but NFSv3 clients cannot discover this capability. Query the export's actual case behavior through ->fileattr_get instead. This allows NFSv3 clients to correctly handle case sensitivity for any filesystem that implements the fileattr interface. Filesystems without ->fileattr_get continue to report the default POSIX behavior (case-sensitive, case-preserving). This change depends on the earlier "fat: Implement fileattr_get for case sensitivity" patch in this series, which ensures FAT filesystems report their case behavior correctly via the fileattr interface. Case-folding is a per-directory property, so nfsd_get_case_info() queries the parent dentry for non-directory filehandles. Three inherent corner cases follow: a single-file export's parent lies outside the exported subtree, so the LSM hook evaluates against an unexported directory; a disconnected dentry from fh_verify() has d_parent == itself, so the file's own attributes are reported until the dentry connects; and a hardlinked file resolves through the alias the dcache currently holds, so when the inode is linked into both case-folded and case-sensitive directories the reported value tracks whichever parent is active. These limitations are not addressable without redefining the protocol attribute as per-parent rather than per-object. RFC 1813 restricts PATHCONF errors to NFS3ERR_STALE, NFS3ERR_BADHANDLE, and NFS3ERR_SERVERFAULT. When an LSM hook denies the case-folding query on the parent, NFS3ERR_STALE is the only correct mapping: NFS3ERR_SERVERFAULT misrepresents a working server as broken, and NFS3ERR_BADHANDLE implies a decoding failure that did not occur. A client purging the filehandle on receipt is the desired outcome, since the server has refused to read attributes through it. Substituting POSIX defaults instead would let the same handle report casefold=false now and casefold=true once policy permits, opening a silent name-collision window on case-insensitive exports. Reviewed-by: Roland Mainz <roland.mainz@nrubsig.org> Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Link: https://patch.msgid.link/20260507-case-sensitivity-v14-13-e62cc8200435@oracle.com Signed-off-by: Christian Brauner <brauner@kernel.org>
169 lines
6.0 KiB
C
169 lines
6.0 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
|
|
*/
|
|
|
|
#ifndef LINUX_NFSD_VFS_H
|
|
#define LINUX_NFSD_VFS_H
|
|
|
|
#include <linux/fs.h>
|
|
#include <linux/posix_acl.h>
|
|
#include "nfsfh.h"
|
|
#include "nfsd.h"
|
|
|
|
/*
|
|
* Flags for nfsd_permission
|
|
*/
|
|
#define NFSD_MAY_NOP 0
|
|
#define NFSD_MAY_EXEC 0x001 /* == MAY_EXEC */
|
|
#define NFSD_MAY_WRITE 0x002 /* == MAY_WRITE */
|
|
#define NFSD_MAY_READ 0x004 /* == MAY_READ */
|
|
#define NFSD_MAY_SATTR 0x008
|
|
#define NFSD_MAY_TRUNC 0x010
|
|
#define NFSD_MAY_NLM 0x020 /* request is from lockd */
|
|
#define NFSD_MAY_MASK 0x03f
|
|
|
|
/* extra hints to permission and open routines: */
|
|
#define NFSD_MAY_OWNER_OVERRIDE 0x040
|
|
#define NFSD_MAY_LOCAL_ACCESS 0x080 /* for device special files */
|
|
#define NFSD_MAY_BYPASS_GSS_ON_ROOT 0x100
|
|
#define NFSD_MAY_NOT_BREAK_LEASE 0x200
|
|
#define NFSD_MAY_BYPASS_GSS 0x400
|
|
#define NFSD_MAY_READ_IF_EXEC 0x800
|
|
|
|
#define NFSD_MAY_64BIT_COOKIE 0x1000 /* 64 bit readdir cookies for >= NFSv3 */
|
|
|
|
#define NFSD_MAY_LOCALIO 0x2000 /* for tracing, reflects when localio used */
|
|
|
|
#define NFSD_MAY_CREATE (NFSD_MAY_EXEC|NFSD_MAY_WRITE)
|
|
#define NFSD_MAY_REMOVE (NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC)
|
|
|
|
struct nfsd_file;
|
|
|
|
/*
|
|
* Callback function for readdir
|
|
*/
|
|
typedef int (*nfsd_filldir_t)(void *, const char *, int, loff_t, u64, unsigned);
|
|
|
|
/* nfsd/vfs.c */
|
|
struct nfsd_attrs {
|
|
struct iattr *na_iattr; /* input */
|
|
struct xdr_netobj *na_seclabel; /* input */
|
|
struct posix_acl *na_pacl; /* input */
|
|
struct posix_acl *na_dpacl; /* input */
|
|
|
|
int na_labelerr; /* output */
|
|
int na_dpaclerr; /* output */
|
|
int na_paclerr; /* output */
|
|
};
|
|
|
|
static inline void nfsd_attrs_free(struct nfsd_attrs *attrs)
|
|
{
|
|
posix_acl_release(attrs->na_pacl);
|
|
posix_acl_release(attrs->na_dpacl);
|
|
}
|
|
|
|
static inline bool nfsd_attrs_valid(struct nfsd_attrs *attrs)
|
|
{
|
|
struct iattr *iap = attrs->na_iattr;
|
|
|
|
return (iap->ia_valid || (attrs->na_seclabel &&
|
|
attrs->na_seclabel->len) ||
|
|
attrs->na_pacl || attrs->na_dpacl);
|
|
}
|
|
|
|
__be32 nfserrno (int errno);
|
|
int nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
|
|
struct svc_export **expp);
|
|
__be32 nfsd_lookup(struct svc_rqst *, struct svc_fh *,
|
|
const char *, unsigned int, struct svc_fh *);
|
|
__be32 nfsd_lookup_dentry(struct svc_rqst *, struct svc_fh *,
|
|
const char *, unsigned int,
|
|
struct svc_export **, struct dentry **);
|
|
__be32 nfsd_setattr(struct svc_rqst *, struct svc_fh *,
|
|
struct nfsd_attrs *, const struct timespec64 *);
|
|
int nfsd_mountpoint(struct dentry *, struct svc_export *);
|
|
#ifdef CONFIG_NFSD_V4
|
|
__be32 nfsd4_vfs_fallocate(struct svc_rqst *, struct svc_fh *,
|
|
struct file *, loff_t, loff_t, int);
|
|
__be32 nfsd4_clone_file_range(struct svc_rqst *rqstp,
|
|
struct nfsd_file *nf_src, u64 src_pos,
|
|
struct nfsd_file *nf_dst, u64 dst_pos,
|
|
u64 count, bool sync);
|
|
#endif /* CONFIG_NFSD_V4 */
|
|
__be32 nfsd_create_locked(struct svc_rqst *, struct svc_fh *,
|
|
struct nfsd_attrs *attrs, int type, dev_t rdev,
|
|
struct svc_fh *res);
|
|
__be32 nfsd_create(struct svc_rqst *, struct svc_fh *,
|
|
char *name, int len, struct nfsd_attrs *attrs,
|
|
int type, dev_t rdev, struct svc_fh *res);
|
|
__be32 nfsd_access(struct svc_rqst *, struct svc_fh *, u32 *, u32 *);
|
|
__be32 nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
|
struct svc_fh *resfhp, struct nfsd_attrs *iap);
|
|
__be32 nfsd_commit(struct svc_rqst *rqst, struct svc_fh *fhp,
|
|
struct nfsd_file *nf, u64 offset, u32 count,
|
|
__be32 *verf);
|
|
#ifdef CONFIG_NFSD_V4
|
|
__be32 nfsd_getxattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
|
char *name, void **bufp, int *lenp);
|
|
__be32 nfsd_listxattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
|
char **bufp, int *lenp);
|
|
__be32 nfsd_removexattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
|
char *name);
|
|
__be32 nfsd_setxattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
|
char *name, void *buf, u32 len, u32 flags);
|
|
#endif
|
|
int nfsd_open_break_lease(struct inode *, int);
|
|
__be32 nfsd_open(struct svc_rqst *, struct svc_fh *, umode_t,
|
|
int, struct file **);
|
|
int nfsd_open_verified(struct svc_fh *fhp, umode_t type, int may_flags,
|
|
struct file **filp);
|
|
__be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
|
struct file *file, loff_t offset,
|
|
unsigned long *count,
|
|
u32 *eof);
|
|
__be32 nfsd_iter_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
|
struct nfsd_file *nf, loff_t offset,
|
|
unsigned long *count, unsigned int base,
|
|
u32 *eof);
|
|
bool nfsd_read_splice_ok(struct svc_rqst *rqstp);
|
|
__be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
|
loff_t offset, unsigned long *count,
|
|
u32 *eof);
|
|
__be32 nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
|
loff_t offset, const struct xdr_buf *payload,
|
|
unsigned long *cnt, int stable, __be32 *verf);
|
|
__be32 nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
|
struct nfsd_file *nf, loff_t offset,
|
|
const struct xdr_buf *payload,
|
|
unsigned long *cnt, int stable, __be32 *verf);
|
|
__be32 nfsd_readlink(struct svc_rqst *, struct svc_fh *,
|
|
char *, int *);
|
|
__be32 nfsd_symlink(struct svc_rqst *, struct svc_fh *,
|
|
char *name, int len, char *path,
|
|
struct nfsd_attrs *attrs,
|
|
struct svc_fh *res);
|
|
__be32 nfsd_link(struct svc_rqst *, struct svc_fh *,
|
|
char *, int, struct svc_fh *);
|
|
ssize_t nfsd_copy_file_range(struct file *, u64,
|
|
struct file *, u64, u64);
|
|
__be32 nfsd_rename(struct svc_rqst *,
|
|
struct svc_fh *, char *, int,
|
|
struct svc_fh *, char *, int);
|
|
__be32 nfsd_unlink(struct svc_rqst *, struct svc_fh *, int type,
|
|
char *name, int len);
|
|
__be32 nfsd_readdir(struct svc_rqst *, struct svc_fh *,
|
|
loff_t *, struct readdir_cd *, nfsd_filldir_t);
|
|
__be32 nfsd_statfs(struct svc_rqst *, struct svc_fh *,
|
|
struct kstatfs *, int access);
|
|
int nfsd_get_case_info(struct dentry *dentry,
|
|
bool *case_insensitive,
|
|
bool *case_preserving);
|
|
|
|
__be32 nfsd_permission(struct svc_cred *cred, struct svc_export *exp,
|
|
struct dentry *dentry, int acc);
|
|
|
|
void nfsd_filp_close(struct file *fp);
|
|
|
|
#endif /* LINUX_NFSD_VFS_H */
|