mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 01:55:51 +02:00
Merge patch series "Exposing case folding behavior"
Chuck Lever <cel@kernel.org> says: I'm attempting to implement enough support in the Linux VFS to enable file services like NFSD and ksmbd (and user space equivalents) to provide the actual status of case folding support in local file systems. The default behavior for local file systems not explicitly supported in this series is to reflect the usual POSIX behaviors: case-insensitive = false case-nonpreserving = false The case-insensitivity and case-nonpreserving booleans can be consumed immediately by NFSD. These two attributes have been part of the NFSv3 and NFSv4 protocols for decades, in order to support NFS client implementations on non-POSIX systems. Support for user space file servers is why this series exposes case folding information via a user-space API. I don't know of any other category of user-space application that requires access to case folding info. The Linux NFS community has a growing interest in supporting NFS clients on Windows and MacOS platforms, where file name behavior does not align with traditional POSIX semantics. One example of a Windows-based NFS client is [1]. This client implementation explicitly requires servers to report FATTR4_WORD0_CASE_INSENSITIVE = TRUE for proper operation, a hard requirement for Windows client interoperability because Windows applications expect case-insensitive behavior. When an NFS client knows the server is case-insensitive, it can avoid issuing multiple LOOKUP/READDIR requests to search for case variants, and applications like Win32 programs work correctly without manual workarounds or code changes. Even the Linux client can take advantage of this information. Trond merged patches 4 years ago [2] that introduce support for case insensitivity, in support of the Hammerspace NFS server. In particular, when a client detects a case-insensitive NFS share, negative dentry caching must be disabled (a lookup for "FILE.TXT" failing shouldn't cache a negative entry when "file.txt" exists) and directory change invalidation must clear all cached case-folded file name variants. Hammerspace servers and several other NFS server implementations operate in multi-protocol environments, where a single file service instance caters to both NFS and SMB clients. In those cases, things work more smoothly for everyone when the NFS client can see and adapt to the case folding behavior that SMB users rely on and expect. NFSD needs to support the case-insensitivity and case-nonpreserving booleans properly in order to participate as a first-class citizen in such environments. [1] https://github.com/kofemann/ms-nfs41-client [2] https://patchwork.kernel.org/project/linux-nfs/cover/20211217203658.439352-1-trondmy@kernel.org/ * patches from https://patch.msgid.link/20260507-case-sensitivity-v14-0-e62cc8200435@oracle.com: ksmbd: Report filesystem case sensitivity via FS_ATTRIBUTE_INFORMATION nfsd: Implement NFSv4 FATTR4_CASE_INSENSITIVE and FATTR4_CASE_PRESERVING nfsd: Report export case-folding via NFSv3 PATHCONF isofs: Implement fileattr_get for case sensitivity vboxsf: Implement fileattr_get for case sensitivity nfs: Implement fileattr_get for case sensitivity cifs: Implement fileattr_get for case sensitivity xfs: Report case sensitivity in fileattr_get hfsplus: Report case sensitivity in fileattr_get hfs: Implement fileattr_get for case sensitivity ntfs3: Implement fileattr_get for case sensitivity exfat: Implement fileattr_get for case sensitivity fat: Implement fileattr_get for case sensitivity fs: Add case sensitivity flags to file_kattr fs: Move file_kattr initialization to callers Link: https://patch.msgid.link/20260507-case-sensitivity-v14-0-e62cc8200435@oracle.com Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
commit
1633e6ea1a
|
|
@ -496,6 +496,8 @@ int exfat_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
|||
int exfat_getattr(struct mnt_idmap *idmap, const struct path *path,
|
||||
struct kstat *stat, unsigned int request_mask,
|
||||
unsigned int query_flags);
|
||||
struct file_kattr;
|
||||
int exfat_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
|
||||
int exfat_file_fsync(struct file *file, loff_t start, loff_t end, int datasync);
|
||||
long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
|
||||
long exfat_compat_ioctl(struct file *filp, unsigned int cmd,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/writeback.h>
|
||||
#include <linux/filelock.h>
|
||||
#include <linux/falloc.h>
|
||||
#include <linux/fileattr.h>
|
||||
|
||||
#include "exfat_raw.h"
|
||||
#include "exfat_fs.h"
|
||||
|
|
@ -323,6 +324,18 @@ int exfat_getattr(struct mnt_idmap *idmap, const struct path *path,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int exfat_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
|
||||
{
|
||||
/*
|
||||
* exFAT compares filenames through an upcase table, so lookup
|
||||
* is always case-insensitive. Long names are stored in UTF-16
|
||||
* with case intact; CASENONPRESERVING stays clear.
|
||||
*/
|
||||
fa->fsx_xflags |= FS_XFLAG_CASEFOLD;
|
||||
fa->flags |= FS_CASEFOLD_FL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exfat_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
struct iattr *attr)
|
||||
{
|
||||
|
|
@ -817,6 +830,7 @@ const struct file_operations exfat_file_operations = {
|
|||
};
|
||||
|
||||
const struct inode_operations exfat_file_inode_operations = {
|
||||
.setattr = exfat_setattr,
|
||||
.getattr = exfat_getattr,
|
||||
.setattr = exfat_setattr,
|
||||
.getattr = exfat_getattr,
|
||||
.fileattr_get = exfat_fileattr_get,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1311,4 +1311,5 @@ const struct inode_operations exfat_dir_inode_operations = {
|
|||
.rename = exfat_rename,
|
||||
.setattr = exfat_setattr,
|
||||
.getattr = exfat_getattr,
|
||||
.fileattr_get = exfat_fileattr_get,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
#include <linux/fs_context.h>
|
||||
#include <linux/fs_parser.h>
|
||||
|
||||
struct file_kattr;
|
||||
|
||||
/*
|
||||
* vfat shortname flags
|
||||
*/
|
||||
|
|
@ -408,6 +410,7 @@ extern void fat_truncate_blocks(struct inode *inode, loff_t offset);
|
|||
extern int fat_getattr(struct mnt_idmap *idmap,
|
||||
const struct path *path, struct kstat *stat,
|
||||
u32 request_mask, unsigned int flags);
|
||||
int fat_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
|
||||
extern int fat_file_fsync(struct file *file, loff_t start, loff_t end,
|
||||
int datasync);
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/fsnotify.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/falloc.h>
|
||||
#include <linux/fileattr.h>
|
||||
#include "fat.h"
|
||||
|
||||
static long fat_fallocate(struct file *file, int mode,
|
||||
|
|
@ -398,6 +399,40 @@ void fat_truncate_blocks(struct inode *inode, loff_t offset)
|
|||
fat_flush_inodes(inode->i_sb, inode, NULL);
|
||||
}
|
||||
|
||||
int fat_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
|
||||
{
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb);
|
||||
bool case_sensitive;
|
||||
|
||||
/*
|
||||
* FAT filesystems are case-insensitive by default. VFAT
|
||||
* becomes case-sensitive when mounted with 'check=strict',
|
||||
* which installs vfat_dentry_ops. MSDOS has no such option;
|
||||
* its 'nocase' mount option selects case-sensitive matching.
|
||||
*
|
||||
* VFAT long filename entries preserve case. Without VFAT, only
|
||||
* uppercased 8.3 short names are stored. MSDOS with 'nocase'
|
||||
* also preserves case.
|
||||
*/
|
||||
if (sbi->options.isvfat)
|
||||
case_sensitive = sbi->options.name_check == 's';
|
||||
else
|
||||
case_sensitive = sbi->options.nocase;
|
||||
|
||||
if (!case_sensitive) {
|
||||
fa->fsx_xflags |= FS_XFLAG_CASEFOLD;
|
||||
fa->flags |= FS_CASEFOLD_FL;
|
||||
if (!sbi->options.isvfat)
|
||||
fa->fsx_xflags |= FS_XFLAG_CASENONPRESERVING;
|
||||
}
|
||||
if (d_inode(dentry)->i_flags & S_IMMUTABLE) {
|
||||
fa->fsx_xflags |= FS_XFLAG_IMMUTABLE;
|
||||
fa->flags |= FS_IMMUTABLE_FL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fat_fileattr_get);
|
||||
|
||||
int fat_getattr(struct mnt_idmap *idmap, const struct path *path,
|
||||
struct kstat *stat, u32 request_mask, unsigned int flags)
|
||||
{
|
||||
|
|
@ -575,5 +610,6 @@ EXPORT_SYMBOL_GPL(fat_setattr);
|
|||
const struct inode_operations fat_file_inode_operations = {
|
||||
.setattr = fat_setattr,
|
||||
.getattr = fat_getattr,
|
||||
.fileattr_get = fat_fileattr_get,
|
||||
.update_time = fat_update_time,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -644,6 +644,7 @@ static const struct inode_operations msdos_dir_inode_operations = {
|
|||
.rename = msdos_rename,
|
||||
.setattr = fat_setattr,
|
||||
.getattr = fat_getattr,
|
||||
.fileattr_get = fat_fileattr_get,
|
||||
.update_time = fat_update_time,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1185,6 +1185,7 @@ static const struct inode_operations vfat_dir_inode_operations = {
|
|||
.rename = vfat_rename2,
|
||||
.setattr = fat_setattr,
|
||||
.getattr = fat_getattr,
|
||||
.fileattr_get = fat_fileattr_get,
|
||||
.update_time = fat_update_time,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -15,12 +15,10 @@
|
|||
* @fa: fileattr pointer
|
||||
* @xflags: FS_XFLAG_* flags
|
||||
*
|
||||
* Set ->fsx_xflags, ->fsx_valid and ->flags (translated xflags). All
|
||||
* other fields are zeroed.
|
||||
* Set ->fsx_xflags, ->fsx_valid and ->flags (translated xflags).
|
||||
*/
|
||||
void fileattr_fill_xflags(struct file_kattr *fa, u32 xflags)
|
||||
{
|
||||
memset(fa, 0, sizeof(*fa));
|
||||
fa->fsx_valid = true;
|
||||
fa->fsx_xflags = xflags;
|
||||
if (fa->fsx_xflags & FS_XFLAG_IMMUTABLE)
|
||||
|
|
@ -39,6 +37,8 @@ void fileattr_fill_xflags(struct file_kattr *fa, u32 xflags)
|
|||
fa->flags |= FS_PROJINHERIT_FL;
|
||||
if (fa->fsx_xflags & FS_XFLAG_VERITY)
|
||||
fa->flags |= FS_VERITY_FL;
|
||||
if (fa->fsx_xflags & FS_XFLAG_CASEFOLD)
|
||||
fa->flags |= FS_CASEFOLD_FL;
|
||||
}
|
||||
EXPORT_SYMBOL(fileattr_fill_xflags);
|
||||
|
||||
|
|
@ -48,11 +48,9 @@ EXPORT_SYMBOL(fileattr_fill_xflags);
|
|||
* @flags: FS_*_FL flags
|
||||
*
|
||||
* Set ->flags, ->flags_valid and ->fsx_xflags (translated flags).
|
||||
* All other fields are zeroed.
|
||||
*/
|
||||
void fileattr_fill_flags(struct file_kattr *fa, u32 flags)
|
||||
{
|
||||
memset(fa, 0, sizeof(*fa));
|
||||
fa->flags_valid = true;
|
||||
fa->flags = flags;
|
||||
if (fa->flags & FS_SYNC_FL)
|
||||
|
|
@ -71,6 +69,8 @@ void fileattr_fill_flags(struct file_kattr *fa, u32 flags)
|
|||
fa->fsx_xflags |= FS_XFLAG_PROJINHERIT;
|
||||
if (fa->flags & FS_VERITY_FL)
|
||||
fa->fsx_xflags |= FS_XFLAG_VERITY;
|
||||
if (fa->flags & FS_CASEFOLD_FL)
|
||||
fa->fsx_xflags |= FS_XFLAG_CASEFOLD;
|
||||
}
|
||||
EXPORT_SYMBOL(fileattr_fill_flags);
|
||||
|
||||
|
|
@ -325,7 +325,7 @@ int ioctl_setflags(struct file *file, unsigned int __user *argp)
|
|||
{
|
||||
struct mnt_idmap *idmap = file_mnt_idmap(file);
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
struct file_kattr fa;
|
||||
struct file_kattr fa = {};
|
||||
unsigned int flags;
|
||||
int err;
|
||||
|
||||
|
|
@ -357,7 +357,7 @@ int ioctl_fssetxattr(struct file *file, void __user *argp)
|
|||
{
|
||||
struct mnt_idmap *idmap = file_mnt_idmap(file);
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
struct file_kattr fa;
|
||||
struct file_kattr fa = {};
|
||||
int err;
|
||||
|
||||
err = copy_fsxattr_from_user(&fa, argp);
|
||||
|
|
@ -431,7 +431,7 @@ SYSCALL_DEFINE5(file_setattr, int, dfd, const char __user *, filename,
|
|||
struct path filepath __free(path_put) = {};
|
||||
unsigned int lookup_flags = 0;
|
||||
struct file_attr fattr;
|
||||
struct file_kattr fa;
|
||||
struct file_kattr fa = {};
|
||||
int error;
|
||||
|
||||
BUILD_BUG_ON(sizeof(struct file_attr) < FILE_ATTR_SIZE_VER0);
|
||||
|
|
|
|||
|
|
@ -328,4 +328,5 @@ const struct inode_operations hfs_dir_inode_operations = {
|
|||
.rmdir = hfs_remove,
|
||||
.rename = hfs_rename,
|
||||
.setattr = hfs_inode_setattr,
|
||||
.fileattr_get = hfs_fileattr_get,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -177,6 +177,8 @@ extern int hfs_get_block(struct inode *inode, sector_t block,
|
|||
extern const struct address_space_operations hfs_aops;
|
||||
extern const struct address_space_operations hfs_btree_aops;
|
||||
|
||||
struct file_kattr;
|
||||
int hfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
|
||||
int hfs_write_begin(const struct kiocb *iocb, struct address_space *mapping,
|
||||
loff_t pos, unsigned int len, struct folio **foliop,
|
||||
void **fsdata);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/uio.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/fileattr.h>
|
||||
|
||||
#include "hfs_fs.h"
|
||||
#include "btree.h"
|
||||
|
|
@ -699,6 +700,18 @@ static int hfs_file_fsync(struct file *filp, loff_t start, loff_t end,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int hfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
|
||||
{
|
||||
/*
|
||||
* HFS compares filenames using Mac OS Roman case folding, so
|
||||
* lookup is always case-insensitive. Names are stored on disk
|
||||
* with case intact; CASENONPRESERVING stays clear.
|
||||
*/
|
||||
fa->fsx_xflags |= FS_XFLAG_CASEFOLD;
|
||||
fa->flags |= FS_CASEFOLD_FL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations hfs_file_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read_iter = generic_file_read_iter,
|
||||
|
|
@ -715,4 +728,5 @@ static const struct inode_operations hfs_file_inode_operations = {
|
|||
.lookup = hfs_file_lookup,
|
||||
.setattr = hfs_inode_setattr,
|
||||
.listxattr = generic_listxattr,
|
||||
.fileattr_get = hfs_fileattr_get,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -740,6 +740,7 @@ int hfsplus_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
|
|||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
|
||||
struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb);
|
||||
unsigned int flags = 0;
|
||||
|
||||
if (inode->i_flags & S_IMMUTABLE)
|
||||
|
|
@ -748,6 +749,8 @@ int hfsplus_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
|
|||
flags |= FS_APPEND_FL;
|
||||
if (hip->userflags & HFSPLUS_FLG_NODUMP)
|
||||
flags |= FS_NODUMP_FL;
|
||||
if (test_bit(HFSPLUS_SB_CASEFOLD, &sbi->flags))
|
||||
flags |= FS_CASEFOLD_FL;
|
||||
|
||||
fileattr_fill_flags(fa, flags);
|
||||
|
||||
|
|
@ -759,13 +762,24 @@ int hfsplus_fileattr_set(struct mnt_idmap *idmap,
|
|||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
|
||||
struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb);
|
||||
unsigned int allowed = FS_IMMUTABLE_FL | FS_APPEND_FL | FS_NODUMP_FL;
|
||||
unsigned int new_fl = 0;
|
||||
|
||||
if (fileattr_has_fsx(fa))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/*
|
||||
* FS_CASEFOLD_FL reflects HFSPLUS_SB_CASEFOLD, a mount-time
|
||||
* property. Accept it as a no-op so chattr's RMW round-trip
|
||||
* succeeds; reject any attempt to enable it on a volume that
|
||||
* was not formatted case-insensitive.
|
||||
*/
|
||||
if (test_bit(HFSPLUS_SB_CASEFOLD, &sbi->flags))
|
||||
allowed |= FS_CASEFOLD_FL;
|
||||
|
||||
/* don't silently ignore unsupported ext2 flags */
|
||||
if (fa->flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL))
|
||||
if (fa->flags & ~allowed)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (fa->flags & FS_IMMUTABLE_FL)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/gfp.h>
|
||||
#include <linux/filelock.h>
|
||||
#include "isofs.h"
|
||||
#include <linux/fileattr.h>
|
||||
|
||||
int isofs_name_translate(struct iso_directory_record *de, char *new, struct inode *inode)
|
||||
{
|
||||
|
|
@ -267,6 +268,20 @@ static int isofs_readdir(struct file *file, struct dir_context *ctx)
|
|||
return result;
|
||||
}
|
||||
|
||||
int isofs_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
|
||||
{
|
||||
struct isofs_sb_info *sbi = ISOFS_SB(dentry->d_sb);
|
||||
|
||||
if (sbi->s_check == 'r') {
|
||||
fa->fsx_xflags |= FS_XFLAG_CASEFOLD;
|
||||
fa->flags |= FS_CASEFOLD_FL;
|
||||
}
|
||||
if (!sbi->s_joliet_level && !sbi->s_rock &&
|
||||
(sbi->s_mapping == 'n' || sbi->s_mapping == 'a'))
|
||||
fa->fsx_xflags |= FS_XFLAG_CASENONPRESERVING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct file_operations isofs_dir_operations =
|
||||
{
|
||||
.llseek = generic_file_llseek,
|
||||
|
|
@ -281,6 +296,7 @@ const struct file_operations isofs_dir_operations =
|
|||
const struct inode_operations isofs_dir_inode_operations =
|
||||
{
|
||||
.lookup = isofs_lookup,
|
||||
.fileattr_get = isofs_fileattr_get,
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -197,6 +197,9 @@ isofs_normalize_block_and_offset(struct iso_directory_record* de,
|
|||
}
|
||||
}
|
||||
|
||||
struct file_kattr;
|
||||
int isofs_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
|
||||
|
||||
extern const struct inode_operations isofs_dir_inode_operations;
|
||||
extern const struct file_operations isofs_dir_operations;
|
||||
extern const struct address_space_operations isofs_symlink_aops;
|
||||
|
|
|
|||
|
|
@ -914,6 +914,7 @@ static void nfs_server_set_fsinfo(struct nfs_server *server,
|
|||
*/
|
||||
static int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr)
|
||||
{
|
||||
struct nfs_pathconf pathinfo = { };
|
||||
struct nfs_fsinfo fsinfo;
|
||||
struct nfs_client *clp = server->nfs_client;
|
||||
int error;
|
||||
|
|
@ -933,15 +934,23 @@ static int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, str
|
|||
|
||||
nfs_server_set_fsinfo(server, &fsinfo);
|
||||
|
||||
/* Get some general file system info */
|
||||
if (server->namelen == 0) {
|
||||
struct nfs_pathconf pathinfo;
|
||||
pathinfo.fattr = fattr;
|
||||
nfs_fattr_init(fattr);
|
||||
|
||||
pathinfo.fattr = fattr;
|
||||
nfs_fattr_init(fattr);
|
||||
/* Clear before probing so a failed RPC does not retain stale bits. */
|
||||
if (clp->rpc_ops->version < 4)
|
||||
server->caps &= ~(NFS_CAP_CASE_INSENSITIVE |
|
||||
NFS_CAP_CASE_NONPRESERVING);
|
||||
|
||||
if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0)
|
||||
if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0) {
|
||||
if (server->namelen == 0)
|
||||
server->namelen = pathinfo.max_namelen;
|
||||
if (clp->rpc_ops->version < 4) {
|
||||
if (pathinfo.case_insensitive)
|
||||
server->caps |= NFS_CAP_CASE_INSENSITIVE;
|
||||
if (!pathinfo.case_preserving)
|
||||
server->caps |= NFS_CAP_CASE_NONPRESERVING;
|
||||
}
|
||||
}
|
||||
|
||||
if (clp->rpc_ops->discover_trunking != NULL &&
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
#include <linux/freezer.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/iversion.h>
|
||||
#include <linux/fileattr.h>
|
||||
|
||||
#include "nfs4_fs.h"
|
||||
#include "callback.h"
|
||||
|
|
@ -1095,6 +1096,20 @@ int nfs_getattr(struct mnt_idmap *idmap, const struct path *path,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_getattr);
|
||||
|
||||
int nfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
|
||||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
|
||||
if (nfs_server_capable(inode, NFS_CAP_CASE_INSENSITIVE)) {
|
||||
fa->fsx_xflags |= FS_XFLAG_CASEFOLD;
|
||||
fa->flags |= FS_CASEFOLD_FL;
|
||||
}
|
||||
if (nfs_server_capable(inode, NFS_CAP_CASE_NONPRESERVING))
|
||||
fa->fsx_xflags |= FS_XFLAG_CASENONPRESERVING;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_fileattr_get);
|
||||
|
||||
static void nfs_init_lock_context(struct nfs_lock_context *l_ctx)
|
||||
{
|
||||
refcount_set(&l_ctx->count, 1);
|
||||
|
|
|
|||
|
|
@ -451,6 +451,9 @@ extern void nfs_set_cache_invalid(struct inode *inode, unsigned long flags);
|
|||
extern bool nfs_check_cache_invalid(struct inode *, unsigned long);
|
||||
extern int nfs_wait_bit_killable(struct wait_bit_key *key, int mode);
|
||||
|
||||
struct file_kattr;
|
||||
int nfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
|
||||
|
||||
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
|
||||
/* localio.c */
|
||||
struct nfs_local_dio {
|
||||
|
|
|
|||
|
|
@ -246,11 +246,13 @@ nfs_namespace_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
|||
const struct inode_operations nfs_mountpoint_inode_operations = {
|
||||
.getattr = nfs_getattr,
|
||||
.setattr = nfs_setattr,
|
||||
.fileattr_get = nfs_fileattr_get,
|
||||
};
|
||||
|
||||
const struct inode_operations nfs_referral_inode_operations = {
|
||||
.getattr = nfs_namespace_getattr,
|
||||
.setattr = nfs_namespace_setattr,
|
||||
.fileattr_get = nfs_fileattr_get,
|
||||
};
|
||||
|
||||
static void nfs_expire_automounts(struct work_struct *work)
|
||||
|
|
|
|||
|
|
@ -1053,6 +1053,7 @@ static const struct inode_operations nfs3_dir_inode_operations = {
|
|||
.permission = nfs_permission,
|
||||
.getattr = nfs_getattr,
|
||||
.setattr = nfs_setattr,
|
||||
.fileattr_get = nfs_fileattr_get,
|
||||
#ifdef CONFIG_NFS_V3_ACL
|
||||
.listxattr = nfs3_listxattr,
|
||||
.get_inode_acl = nfs3_get_acl,
|
||||
|
|
@ -1064,6 +1065,7 @@ static const struct inode_operations nfs3_file_inode_operations = {
|
|||
.permission = nfs_permission,
|
||||
.getattr = nfs_getattr,
|
||||
.setattr = nfs_setattr,
|
||||
.fileattr_get = nfs_fileattr_get,
|
||||
#ifdef CONFIG_NFS_V3_ACL
|
||||
.listxattr = nfs3_listxattr,
|
||||
.get_inode_acl = nfs3_get_acl,
|
||||
|
|
|
|||
|
|
@ -2276,8 +2276,11 @@ static int decode_pathconf3resok(struct xdr_stream *xdr,
|
|||
if (unlikely(!p))
|
||||
return -EIO;
|
||||
result->max_link = be32_to_cpup(p++);
|
||||
result->max_namelen = be32_to_cpup(p);
|
||||
/* ignore remaining fields */
|
||||
result->max_namelen = be32_to_cpup(p++);
|
||||
p++; /* ignore no_trunc */
|
||||
p++; /* ignore chown_restricted */
|
||||
result->case_insensitive = be32_to_cpup(p++) != 0;
|
||||
result->case_preserving = be32_to_cpup(p) != 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3933,7 +3933,8 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
|
|||
server->caps &=
|
||||
~(NFS_CAP_ACLS | NFS_CAP_HARDLINKS | NFS_CAP_SYMLINKS |
|
||||
NFS_CAP_SECURITY_LABEL | NFS_CAP_FS_LOCATIONS |
|
||||
NFS_CAP_OPEN_XOR | NFS_CAP_DELEGTIME);
|
||||
NFS_CAP_OPEN_XOR | NFS_CAP_DELEGTIME |
|
||||
NFS_CAP_CASE_INSENSITIVE | NFS_CAP_CASE_NONPRESERVING);
|
||||
server->fattr_valid = NFS_ATTR_FATTR_V4;
|
||||
if (res.attr_bitmask[0] & FATTR4_WORD0_ACL &&
|
||||
res.acl_bitmask & ACL4_SUPPORT_ALLOW_ACL)
|
||||
|
|
@ -3944,8 +3945,9 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
|
|||
server->caps |= NFS_CAP_SYMLINKS;
|
||||
if (res.case_insensitive)
|
||||
server->caps |= NFS_CAP_CASE_INSENSITIVE;
|
||||
if (res.case_preserving)
|
||||
server->caps |= NFS_CAP_CASE_PRESERVING;
|
||||
if ((res.attr_bitmask[0] & FATTR4_WORD0_CASE_PRESERVING) &&
|
||||
!res.case_preserving)
|
||||
server->caps |= NFS_CAP_CASE_NONPRESERVING;
|
||||
#ifdef CONFIG_NFS_V4_SECURITY_LABEL
|
||||
if (res.attr_bitmask[2] & FATTR4_WORD2_SECURITY_LABEL)
|
||||
server->caps |= NFS_CAP_SECURITY_LABEL;
|
||||
|
|
@ -10617,6 +10619,7 @@ static const struct inode_operations nfs4_dir_inode_operations = {
|
|||
.getattr = nfs_getattr,
|
||||
.setattr = nfs_setattr,
|
||||
.listxattr = nfs4_listxattr,
|
||||
.fileattr_get = nfs_fileattr_get,
|
||||
};
|
||||
|
||||
static const struct inode_operations nfs4_file_inode_operations = {
|
||||
|
|
@ -10624,6 +10627,7 @@ static const struct inode_operations nfs4_file_inode_operations = {
|
|||
.getattr = nfs_getattr,
|
||||
.setattr = nfs_setattr,
|
||||
.listxattr = nfs4_listxattr,
|
||||
.fileattr_get = nfs_fileattr_get,
|
||||
};
|
||||
|
||||
static struct nfs_server *nfs4_clone_server(struct nfs_server *source,
|
||||
|
|
|
|||
|
|
@ -598,6 +598,7 @@ nfs_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
|
|||
{
|
||||
info->max_link = 0;
|
||||
info->max_namelen = NFS2_MAXNAMLEN;
|
||||
info->case_preserving = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -718,12 +719,14 @@ static const struct inode_operations nfs_dir_inode_operations = {
|
|||
.permission = nfs_permission,
|
||||
.getattr = nfs_getattr,
|
||||
.setattr = nfs_setattr,
|
||||
.fileattr_get = nfs_fileattr_get,
|
||||
};
|
||||
|
||||
static const struct inode_operations nfs_file_inode_operations = {
|
||||
.permission = nfs_permission,
|
||||
.getattr = nfs_getattr,
|
||||
.setattr = nfs_setattr,
|
||||
.fileattr_get = nfs_fileattr_get,
|
||||
};
|
||||
|
||||
const struct nfs_rpc_ops nfs_v2_clientops = {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@
|
|||
#include <linux/mm.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
/* Symlink caching in the page cache is even more simplistic
|
||||
* and straight-forward than readdir caching.
|
||||
*/
|
||||
|
|
@ -74,4 +76,5 @@ const struct inode_operations nfs_symlink_inode_operations = {
|
|||
.get_link = nfs_get_link,
|
||||
.getattr = nfs_getattr,
|
||||
.setattr = nfs_setattr,
|
||||
.fileattr_get = nfs_fileattr_get,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -710,23 +710,43 @@ nfsd3_proc_pathconf(struct svc_rqst *rqstp)
|
|||
resp->p_name_max = 255; /* at least */
|
||||
resp->p_no_trunc = 0;
|
||||
resp->p_chown_restricted = 1;
|
||||
resp->p_case_insensitive = 0;
|
||||
resp->p_case_preserving = 1;
|
||||
resp->p_case_insensitive = false;
|
||||
resp->p_case_preserving = true;
|
||||
|
||||
resp->status = fh_verify(rqstp, &argp->fh, 0, NFSD_MAY_NOP);
|
||||
|
||||
if (resp->status == nfs_ok) {
|
||||
struct super_block *sb = argp->fh.fh_dentry->d_sb;
|
||||
int err;
|
||||
|
||||
/* Note that we don't care for remote fs's here */
|
||||
switch (sb->s_magic) {
|
||||
case EXT2_SUPER_MAGIC:
|
||||
if (sb->s_magic == EXT2_SUPER_MAGIC) {
|
||||
resp->p_link_max = EXT2_LINK_MAX;
|
||||
resp->p_name_max = EXT2_NAME_LEN;
|
||||
}
|
||||
|
||||
err = nfsd_get_case_info(argp->fh.fh_dentry,
|
||||
&resp->p_case_insensitive,
|
||||
&resp->p_case_preserving);
|
||||
/*
|
||||
* RFC 1813 lists NFS3ERR_STALE, NFS3ERR_BADHANDLE, and
|
||||
* NFS3ERR_SERVERFAULT as the only PATHCONF errors.
|
||||
*/
|
||||
switch (err) {
|
||||
case 0:
|
||||
case -EOPNOTSUPP:
|
||||
/* Both arms leave the output booleans valid. */
|
||||
break;
|
||||
case MSDOS_SUPER_MAGIC:
|
||||
resp->p_case_insensitive = 1;
|
||||
resp->p_case_preserving = 0;
|
||||
case -EACCES:
|
||||
case -EPERM:
|
||||
/*
|
||||
* Policy denied the query. Report STALE so the
|
||||
* handle is unusable without implying a server
|
||||
* malfunction.
|
||||
*/
|
||||
resp->status = nfserr_stale;
|
||||
break;
|
||||
default:
|
||||
resp->status = nfserr_serverfault;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3158,6 +3158,8 @@ struct nfsd4_fattr_args {
|
|||
u32 rdattr_err;
|
||||
bool contextsupport;
|
||||
bool ignore_crossmnt;
|
||||
bool case_insensitive;
|
||||
bool case_preserving;
|
||||
};
|
||||
|
||||
typedef __be32(*nfsd4_enc_attr)(struct xdr_stream *xdr,
|
||||
|
|
@ -3356,6 +3358,33 @@ static __be32 nfsd4_encode_fattr4_acl(struct xdr_stream *xdr,
|
|||
return nfs_ok;
|
||||
}
|
||||
|
||||
static __be32 nfsd4_encode_fattr4_case_insensitive(struct xdr_stream *xdr,
|
||||
const struct nfsd4_fattr_args *args)
|
||||
{
|
||||
return nfsd4_encode_bool(xdr, args->case_insensitive);
|
||||
}
|
||||
|
||||
static __be32 nfsd4_encode_fattr4_case_preserving(struct xdr_stream *xdr,
|
||||
const struct nfsd4_fattr_args *args)
|
||||
{
|
||||
return nfsd4_encode_bool(xdr, args->case_preserving);
|
||||
}
|
||||
|
||||
static __be32 nfsd4_encode_fattr4_homogeneous(struct xdr_stream *xdr,
|
||||
const struct nfsd4_fattr_args *args)
|
||||
{
|
||||
/*
|
||||
* Casefold-capable filesystems (e.g. ext4 or f2fs with the
|
||||
* casefold feature) attach a Unicode encoding at mount time
|
||||
* but apply case folding per directory. The per-file-system
|
||||
* case_insensitive and case_preserving values can therefore
|
||||
* legitimately differ across objects that share the same fsid.
|
||||
* Report FATTR4_HOMOGENEOUS = FALSE on such filesystems to
|
||||
* keep that variation consistent with RFC 8881 Section 5.8.2.16.
|
||||
*/
|
||||
return nfsd4_encode_bool(xdr, !sb_has_encoding(args->dentry->d_sb));
|
||||
}
|
||||
|
||||
static __be32 nfsd4_encode_fattr4_filehandle(struct xdr_stream *xdr,
|
||||
const struct nfsd4_fattr_args *args)
|
||||
{
|
||||
|
|
@ -3748,8 +3777,8 @@ static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = {
|
|||
[FATTR4_ACLSUPPORT] = nfsd4_encode_fattr4_aclsupport,
|
||||
[FATTR4_ARCHIVE] = nfsd4_encode_fattr4__noop,
|
||||
[FATTR4_CANSETTIME] = nfsd4_encode_fattr4__true,
|
||||
[FATTR4_CASE_INSENSITIVE] = nfsd4_encode_fattr4__false,
|
||||
[FATTR4_CASE_PRESERVING] = nfsd4_encode_fattr4__true,
|
||||
[FATTR4_CASE_INSENSITIVE] = nfsd4_encode_fattr4_case_insensitive,
|
||||
[FATTR4_CASE_PRESERVING] = nfsd4_encode_fattr4_case_preserving,
|
||||
[FATTR4_CHOWN_RESTRICTED] = nfsd4_encode_fattr4__true,
|
||||
[FATTR4_FILEHANDLE] = nfsd4_encode_fattr4_filehandle,
|
||||
[FATTR4_FILEID] = nfsd4_encode_fattr4_fileid,
|
||||
|
|
@ -3758,7 +3787,7 @@ static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = {
|
|||
[FATTR4_FILES_TOTAL] = nfsd4_encode_fattr4_files_total,
|
||||
[FATTR4_FS_LOCATIONS] = nfsd4_encode_fattr4_fs_locations,
|
||||
[FATTR4_HIDDEN] = nfsd4_encode_fattr4__noop,
|
||||
[FATTR4_HOMOGENEOUS] = nfsd4_encode_fattr4__true,
|
||||
[FATTR4_HOMOGENEOUS] = nfsd4_encode_fattr4_homogeneous,
|
||||
[FATTR4_MAXFILESIZE] = nfsd4_encode_fattr4_maxfilesize,
|
||||
[FATTR4_MAXLINK] = nfsd4_encode_fattr4_maxlink,
|
||||
[FATTR4_MAXNAME] = nfsd4_encode_fattr4_maxname,
|
||||
|
|
@ -3968,6 +3997,23 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
|
|||
args.fhp = tempfh;
|
||||
} else
|
||||
args.fhp = fhp;
|
||||
if (attrmask[0] & (FATTR4_WORD0_CASE_INSENSITIVE |
|
||||
FATTR4_WORD0_CASE_PRESERVING)) {
|
||||
err = nfsd_get_case_info(dentry, &args.case_insensitive,
|
||||
&args.case_preserving);
|
||||
/*
|
||||
* Per RFC 8881 Section 18.7.3, an attribute advertised
|
||||
* in SUPPORTED_ATTRS must come back with a value or the
|
||||
* GETATTR must fail. nfsd_get_case_info() fills POSIX
|
||||
* defaults and returns -EOPNOTSUPP when the underlying
|
||||
* filesystem does not expose case state; encode those
|
||||
* defaults so the reply agrees with what SUPPORTED_ATTRS
|
||||
* advertises. Other errors fail the operation as the
|
||||
* spec requires.
|
||||
*/
|
||||
if (err && err != -EOPNOTSUPP)
|
||||
goto out_nfserr;
|
||||
}
|
||||
|
||||
if (attrmask[0] & FATTR4_WORD0_ACL) {
|
||||
err = nfsd4_get_nfs4_acl(rqstp, dentry, &args.acl);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include <linux/writeback.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/sunrpc/xdr.h>
|
||||
#include <linux/fileattr.h>
|
||||
|
||||
#include "xdr3.h"
|
||||
|
||||
|
|
@ -2891,3 +2892,90 @@ nfsd_permission(struct svc_cred *cred, struct svc_export *exp,
|
|||
|
||||
return err? nfserrno(err) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfsd_get_case_info - get case sensitivity info for a dentry
|
||||
* @dentry: dentry to query
|
||||
* @case_insensitive: set to true if name comparison ignores case
|
||||
* @case_preserving: set to true if case is preserved on disk
|
||||
*
|
||||
* On casefold-capable filesystems the flag lives on the directory,
|
||||
* not on its entries, so for a non-directory @dentry the parent is
|
||||
* queried instead. A directory (including an export root, whose
|
||||
* parent lies outside the export) is queried as-is so its own
|
||||
* contents' lookup behavior is reported. NFSD advertises
|
||||
* fattr4_homogeneous as FALSE, so per-directory answers may differ
|
||||
* within an export.
|
||||
*
|
||||
* The probe runs with kernel credentials. case_insensitive and
|
||||
* case_preserving describe the directory's structural lookup
|
||||
* behavior, not the caller's identity; running under the calling
|
||||
* client's mapped credentials would let per-client MAC policy on
|
||||
* the parent directory turn this query into NFS4ERR_ACCESS even
|
||||
* though the underlying property is the same for every client.
|
||||
*
|
||||
* When the filesystem does not expose case-folding state (no
|
||||
* ->fileattr_get, or the callback returns -EOPNOTSUPP /
|
||||
* -ENOIOCTLCMD / -ENOTTY / -EINVAL), the outputs are filled with
|
||||
* POSIX defaults (case-sensitive, case-preserving) on the premise
|
||||
* that a filesystem with case-folding support wires up
|
||||
* fileattr_get.
|
||||
*
|
||||
* Return: 0 with outputs filled, -EOPNOTSUPP with outputs filled
|
||||
* to POSIX defaults, or a negative errno (e.g., -EIO,
|
||||
* -ESTALE, -ENOMEM) with outputs unmodified.
|
||||
*/
|
||||
int
|
||||
nfsd_get_case_info(struct dentry *dentry, bool *case_insensitive,
|
||||
bool *case_preserving)
|
||||
{
|
||||
struct file_kattr fa = {};
|
||||
const struct cred *saved;
|
||||
struct cred *probe;
|
||||
struct dentry *cd;
|
||||
bool put = false;
|
||||
int err;
|
||||
|
||||
if (d_is_dir(dentry)) {
|
||||
cd = dentry;
|
||||
} else {
|
||||
cd = dget_parent(dentry);
|
||||
put = true;
|
||||
}
|
||||
|
||||
probe = prepare_creds();
|
||||
if (!probe) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
probe->fsuid = GLOBAL_ROOT_UID;
|
||||
probe->fsgid = GLOBAL_ROOT_GID;
|
||||
saved = override_creds(probe);
|
||||
|
||||
err = vfs_fileattr_get(cd, &fa);
|
||||
|
||||
put_cred(revert_creds(saved));
|
||||
out:
|
||||
if (put)
|
||||
dput(cd);
|
||||
switch (err) {
|
||||
case 0:
|
||||
*case_insensitive = fa.fsx_xflags & FS_XFLAG_CASEFOLD;
|
||||
*case_preserving =
|
||||
!(fa.fsx_xflags & FS_XFLAG_CASENONPRESERVING);
|
||||
return 0;
|
||||
case -EINVAL:
|
||||
case -ENOTTY:
|
||||
case -ENOIOCTLCMD:
|
||||
case -EOPNOTSUPP:
|
||||
/*
|
||||
* Filesystem does not expose case state.
|
||||
* Report POSIX defaults.
|
||||
*/
|
||||
*case_insensitive = false;
|
||||
*case_preserving = true;
|
||||
return -EOPNOTSUPP;
|
||||
default:
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -156,6 +156,9 @@ __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);
|
||||
|
|
|
|||
|
|
@ -209,8 +209,8 @@ struct nfsd3_pathconfres {
|
|||
__u32 p_name_max;
|
||||
__u32 p_no_trunc;
|
||||
__u32 p_chown_restricted;
|
||||
__u32 p_case_insensitive;
|
||||
__u32 p_case_preserving;
|
||||
bool p_case_insensitive;
|
||||
bool p_case_preserving;
|
||||
};
|
||||
|
||||
struct nfsd3_commitres {
|
||||
|
|
|
|||
|
|
@ -180,6 +180,34 @@ long ntfs_compat_ioctl(struct file *filp, u32 cmd, unsigned long arg)
|
|||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ntfs_fileattr_get - inode_operations::fileattr_get
|
||||
*/
|
||||
int ntfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
|
||||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct ntfs_sb_info *sbi = inode->i_sb->s_fs_info;
|
||||
|
||||
/* Avoid any operation if inode is bad. */
|
||||
if (unlikely(is_bad_ni(ntfs_i(inode))))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* NTFS preserves case (the default). Case sensitivity depends on
|
||||
* mount options: with "nocase", NTFS is case-insensitive;
|
||||
* otherwise it is case-sensitive.
|
||||
*/
|
||||
if (sbi->options->nocase) {
|
||||
fa->fsx_xflags |= FS_XFLAG_CASEFOLD;
|
||||
fa->flags |= FS_CASEFOLD_FL;
|
||||
}
|
||||
if (inode->i_flags & S_IMMUTABLE) {
|
||||
fa->fsx_xflags |= FS_XFLAG_IMMUTABLE;
|
||||
fa->flags |= FS_IMMUTABLE_FL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ntfs_getattr - inode_operations::getattr
|
||||
*/
|
||||
|
|
@ -1547,6 +1575,7 @@ const struct inode_operations ntfs_file_inode_operations = {
|
|||
.get_acl = ntfs_get_acl,
|
||||
.set_acl = ntfs_set_acl,
|
||||
.fiemap = ntfs_fiemap,
|
||||
.fileattr_get = ntfs_fileattr_get,
|
||||
};
|
||||
|
||||
const struct file_operations ntfs_file_operations = {
|
||||
|
|
|
|||
|
|
@ -518,6 +518,7 @@ const struct inode_operations ntfs_dir_inode_operations = {
|
|||
.getattr = ntfs_getattr,
|
||||
.listxattr = ntfs_listxattr,
|
||||
.fiemap = ntfs_fiemap,
|
||||
.fileattr_get = ntfs_fileattr_get,
|
||||
};
|
||||
|
||||
const struct inode_operations ntfs_special_inode_operations = {
|
||||
|
|
|
|||
|
|
@ -529,6 +529,7 @@ bool dir_is_empty(struct inode *dir);
|
|||
extern const struct file_operations ntfs_dir_operations;
|
||||
|
||||
/* Globals from file.c */
|
||||
int ntfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
|
||||
int ntfs_getattr(struct mnt_idmap *idmap, const struct path *path,
|
||||
struct kstat *stat, u32 request_mask, u32 flags);
|
||||
int ntfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include <linux/xattr.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/key-type.h>
|
||||
#include <linux/fileattr.h>
|
||||
#include <uapi/linux/magic.h>
|
||||
#include <net/ipv6.h>
|
||||
#include "cifsfs.h"
|
||||
|
|
@ -1162,6 +1163,56 @@ struct file_system_type smb3_fs_type = {
|
|||
MODULE_ALIAS_FS("smb3");
|
||||
MODULE_ALIAS("smb3");
|
||||
|
||||
int cifs_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
|
||||
{
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(dentry->d_sb);
|
||||
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
|
||||
struct inode *inode = d_inode(dentry);
|
||||
u32 attrs;
|
||||
|
||||
/* Preserve FS_COMPR_FL previously reported by cifs_ioctl(). */
|
||||
if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED)
|
||||
fa->flags |= FS_COMPR_FL;
|
||||
|
||||
/*
|
||||
* FS_CASEFOLD_FL is defined by UAPI as a folder attribute,
|
||||
* and userspace tools (e.g., lsattr) display it only on
|
||||
* directories. Confine the case-handling bits to directories
|
||||
* to match that convention; for non-directories the share's
|
||||
* case semantics are still discoverable through the parent.
|
||||
*/
|
||||
if (!S_ISDIR(inode->i_mode))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* The server's FS_ATTRIBUTE_INFORMATION response, cached on
|
||||
* the tcon at mount, reflects the share's case-handling
|
||||
* semantics after any POSIX extensions negotiation. Prefer
|
||||
* it over the client-local nocase mount option, which only
|
||||
* governs dentry comparison on this superblock.
|
||||
*
|
||||
* QueryFSInfo is best-effort at mount; when it did not
|
||||
* populate fsAttrInfo, MaxPathNameComponentLength remains
|
||||
* zero. In that case fall back to nocase so the reporting
|
||||
* matches the comparison behavior installed on the sb.
|
||||
*/
|
||||
if (le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength) == 0) {
|
||||
if (tcon->nocase) {
|
||||
fa->fsx_xflags |= FS_XFLAG_CASEFOLD;
|
||||
fa->flags |= FS_CASEFOLD_FL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
attrs = le32_to_cpu(tcon->fsAttrInfo.Attributes);
|
||||
if (!(attrs & FILE_CASE_SENSITIVE_SEARCH)) {
|
||||
fa->fsx_xflags |= FS_XFLAG_CASEFOLD;
|
||||
fa->flags |= FS_CASEFOLD_FL;
|
||||
}
|
||||
if (!(attrs & FILE_CASE_PRESERVED_NAMES))
|
||||
fa->fsx_xflags |= FS_XFLAG_CASENONPRESERVING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct inode_operations cifs_dir_inode_ops = {
|
||||
.create = cifs_create,
|
||||
.atomic_open = cifs_atomic_open,
|
||||
|
|
@ -1180,6 +1231,7 @@ const struct inode_operations cifs_dir_inode_ops = {
|
|||
.listxattr = cifs_listxattr,
|
||||
.get_acl = cifs_get_acl,
|
||||
.set_acl = cifs_set_acl,
|
||||
.fileattr_get = cifs_fileattr_get,
|
||||
};
|
||||
|
||||
const struct inode_operations cifs_file_inode_ops = {
|
||||
|
|
@ -1190,6 +1242,7 @@ const struct inode_operations cifs_file_inode_ops = {
|
|||
.fiemap = cifs_fiemap,
|
||||
.get_acl = cifs_get_acl,
|
||||
.set_acl = cifs_set_acl,
|
||||
.fileattr_get = cifs_fileattr_get,
|
||||
};
|
||||
|
||||
const char *cifs_get_link(struct dentry *dentry, struct inode *inode,
|
||||
|
|
|
|||
|
|
@ -89,6 +89,9 @@ extern const struct inode_operations cifs_file_inode_ops;
|
|||
extern const struct inode_operations cifs_symlink_inode_ops;
|
||||
extern const struct inode_operations cifs_namespace_inode_operations;
|
||||
|
||||
struct file_kattr;
|
||||
int cifs_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
|
||||
|
||||
|
||||
/* Functions related to files and directories */
|
||||
extern const struct netfs_request_ops cifs_req_ops;
|
||||
|
|
|
|||
|
|
@ -294,4 +294,5 @@ struct vfsmount *cifs_d_automount(struct path *path)
|
|||
}
|
||||
|
||||
const struct inode_operations cifs_namespace_inode_operations = {
|
||||
.fileattr_get = cifs_fileattr_get,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/falloc.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/filelock.h>
|
||||
#include <linux/fileattr.h>
|
||||
|
||||
#include "glob.h"
|
||||
#include "smbfsctl.h"
|
||||
|
|
@ -5542,16 +5543,33 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
|
|||
case FS_ATTRIBUTE_INFORMATION:
|
||||
{
|
||||
FILE_SYSTEM_ATTRIBUTE_INFO *info;
|
||||
struct file_kattr fa = {};
|
||||
size_t sz;
|
||||
u32 attrs;
|
||||
int err;
|
||||
|
||||
info = (FILE_SYSTEM_ATTRIBUTE_INFO *)rsp->Buffer;
|
||||
info->Attributes = cpu_to_le32(FILE_SUPPORTS_OBJECT_IDS |
|
||||
FILE_PERSISTENT_ACLS |
|
||||
FILE_UNICODE_ON_DISK |
|
||||
FILE_CASE_PRESERVED_NAMES |
|
||||
FILE_CASE_SENSITIVE_SEARCH |
|
||||
FILE_SUPPORTS_BLOCK_REFCOUNTING);
|
||||
attrs = FILE_SUPPORTS_OBJECT_IDS |
|
||||
FILE_PERSISTENT_ACLS |
|
||||
FILE_UNICODE_ON_DISK |
|
||||
FILE_SUPPORTS_BLOCK_REFCOUNTING;
|
||||
|
||||
err = vfs_fileattr_get(path.dentry, &fa);
|
||||
/*
|
||||
* -EINVAL, -EOPNOTSUPP: ntfs-3g and other FUSE
|
||||
* filesystems that lack FS_IOC_FSGETXATTR support.
|
||||
*/
|
||||
if (err && err != -ENOIOCTLCMD && err != -ENOTTY &&
|
||||
err != -EINVAL && err != -EOPNOTSUPP) {
|
||||
path_put(&path);
|
||||
return err;
|
||||
}
|
||||
if (!(fa.fsx_xflags & FS_XFLAG_CASEFOLD))
|
||||
attrs |= FILE_CASE_SENSITIVE_SEARCH;
|
||||
if (!(fa.fsx_xflags & FS_XFLAG_CASENONPRESERVING))
|
||||
attrs |= FILE_CASE_PRESERVED_NAMES;
|
||||
|
||||
info->Attributes = cpu_to_le32(attrs);
|
||||
info->Attributes |= cpu_to_le32(server_conf.share_fake_fscaps);
|
||||
|
||||
if (test_share_config_flag(work->tcon->share_conf,
|
||||
|
|
|
|||
|
|
@ -477,4 +477,5 @@ const struct inode_operations vboxsf_dir_iops = {
|
|||
.symlink = vboxsf_dir_symlink,
|
||||
.getattr = vboxsf_getattr,
|
||||
.setattr = vboxsf_setattr,
|
||||
.fileattr_get = vboxsf_fileattr_get,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -222,7 +222,8 @@ const struct file_operations vboxsf_reg_fops = {
|
|||
|
||||
const struct inode_operations vboxsf_reg_iops = {
|
||||
.getattr = vboxsf_getattr,
|
||||
.setattr = vboxsf_setattr
|
||||
.setattr = vboxsf_setattr,
|
||||
.fileattr_get = vboxsf_fileattr_get,
|
||||
};
|
||||
|
||||
static int vboxsf_read_folio(struct file *file, struct folio *folio)
|
||||
|
|
@ -389,5 +390,6 @@ static const char *vboxsf_get_link(struct dentry *dentry, struct inode *inode,
|
|||
}
|
||||
|
||||
const struct inode_operations vboxsf_lnk_iops = {
|
||||
.get_link = vboxsf_get_link
|
||||
.get_link = vboxsf_get_link,
|
||||
.fileattr_get = vboxsf_fileattr_get,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -185,6 +185,13 @@ static int vboxsf_fill_super(struct super_block *sb, struct fs_context *fc)
|
|||
if (err)
|
||||
goto fail_unmap;
|
||||
|
||||
/*
|
||||
* A failed query leaves sbi->case_insensitive false, so the
|
||||
* mount defaults to reporting case-sensitive behavior. Do not
|
||||
* fail the mount over an advisory attribute.
|
||||
*/
|
||||
vboxsf_query_case_sensitive(sbi);
|
||||
|
||||
sb->s_magic = VBOXSF_SUPER_MAGIC;
|
||||
sb->s_blocksize = 1024;
|
||||
sb->s_maxbytes = MAX_LFS_FILESIZE;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/sizes.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/vfs.h>
|
||||
#include <linux/fileattr.h>
|
||||
#include "vfsmod.h"
|
||||
|
||||
struct inode *vboxsf_new_inode(struct super_block *sb)
|
||||
|
|
@ -567,3 +568,32 @@ int vboxsf_dir_read_all(struct vboxsf_sbi *sbi, struct vboxsf_dir_info *sf_d,
|
|||
|
||||
return err;
|
||||
}
|
||||
|
||||
int vboxsf_query_case_sensitive(struct vboxsf_sbi *sbi)
|
||||
{
|
||||
struct shfl_volinfo volinfo = {};
|
||||
u32 buf_len;
|
||||
int err;
|
||||
|
||||
buf_len = sizeof(volinfo);
|
||||
err = vboxsf_fsinfo(sbi->root, 0, SHFL_INFO_GET | SHFL_INFO_VOLUME,
|
||||
&buf_len, &volinfo);
|
||||
if (err)
|
||||
return err;
|
||||
if (buf_len < sizeof(volinfo))
|
||||
return 0;
|
||||
|
||||
sbi->case_insensitive = !volinfo.properties.case_sensitive;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vboxsf_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
|
||||
{
|
||||
struct vboxsf_sbi *sbi = VBOXSF_SBI(dentry->d_sb);
|
||||
|
||||
if (sbi->case_insensitive) {
|
||||
fa->fsx_xflags |= FS_XFLAG_CASEFOLD;
|
||||
fa->flags |= FS_CASEFOLD_FL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ struct vboxsf_sbi {
|
|||
u32 next_generation;
|
||||
u32 root;
|
||||
int bdi_id;
|
||||
bool case_insensitive;
|
||||
};
|
||||
|
||||
/* per-inode information */
|
||||
|
|
@ -111,6 +112,11 @@ void vboxsf_dir_info_free(struct vboxsf_dir_info *p);
|
|||
int vboxsf_dir_read_all(struct vboxsf_sbi *sbi, struct vboxsf_dir_info *sf_d,
|
||||
u64 handle);
|
||||
|
||||
int vboxsf_query_case_sensitive(struct vboxsf_sbi *sbi);
|
||||
|
||||
struct file_kattr;
|
||||
int vboxsf_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
|
||||
|
||||
/* from vboxsf_wrappers.c */
|
||||
int vboxsf_connect(void);
|
||||
void vboxsf_disconnect(void);
|
||||
|
|
|
|||
|
|
@ -130,6 +130,8 @@ xfs_ip2xflags(
|
|||
|
||||
if (xfs_inode_has_attr_fork(ip))
|
||||
flags |= FS_XFLAG_HASATTR;
|
||||
if (xfs_has_asciici(ip->i_mount))
|
||||
flags |= FS_XFLAG_CASEFOLD;
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -517,7 +517,7 @@ xfs_ioc_fsgetxattra(
|
|||
xfs_inode_t *ip,
|
||||
void __user *arg)
|
||||
{
|
||||
struct file_kattr fa;
|
||||
struct file_kattr fa = {};
|
||||
|
||||
xfs_ilock(ip, XFS_ILOCK_SHARED);
|
||||
xfs_fill_fsxattr(ip, XFS_ATTR_FORK, &fa);
|
||||
|
|
@ -755,9 +755,23 @@ xfs_fileattr_set(
|
|||
trace_xfs_ioctl_setattr(ip);
|
||||
|
||||
if (!fa->fsx_valid) {
|
||||
if (fa->flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL |
|
||||
FS_NOATIME_FL | FS_NODUMP_FL |
|
||||
FS_SYNC_FL | FS_DAX_FL | FS_PROJINHERIT_FL))
|
||||
unsigned int allowed = FS_IMMUTABLE_FL | FS_APPEND_FL |
|
||||
FS_NOATIME_FL | FS_NODUMP_FL |
|
||||
FS_SYNC_FL | FS_DAX_FL |
|
||||
FS_PROJINHERIT_FL;
|
||||
|
||||
/*
|
||||
* FS_CASEFOLD_FL reflects the ASCIICI superblock feature,
|
||||
* a read-only property. Accept it as a no-op so chattr's
|
||||
* RMW round-trip succeeds; reject any attempt to enable
|
||||
* it on a non-ASCIICI filesystem. xfs_flags2diflags()
|
||||
* has no clause for CASEFOLD, so the bit is dropped from
|
||||
* the on-disk diflags regardless.
|
||||
*/
|
||||
if (xfs_has_asciici(mp))
|
||||
allowed |= FS_CASEFOLD_FL;
|
||||
|
||||
if (fa->flags & ~allowed)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@
|
|||
|
||||
/* Read-only inode flags */
|
||||
#define FS_XFLAG_RDONLY_MASK \
|
||||
(FS_XFLAG_PREALLOC | FS_XFLAG_HASATTR | FS_XFLAG_VERITY)
|
||||
(FS_XFLAG_PREALLOC | FS_XFLAG_HASATTR | FS_XFLAG_VERITY | \
|
||||
FS_XFLAG_CASEFOLD | FS_XFLAG_CASENONPRESERVING)
|
||||
|
||||
/* Flags to indicate valid value of fsx_ fields */
|
||||
#define FS_XFLAG_VALUES_MASK \
|
||||
|
|
|
|||
|
|
@ -306,7 +306,7 @@ struct nfs_server {
|
|||
#define NFS_CAP_ATOMIC_OPEN (1U << 4)
|
||||
#define NFS_CAP_LGOPEN (1U << 5)
|
||||
#define NFS_CAP_CASE_INSENSITIVE (1U << 6)
|
||||
#define NFS_CAP_CASE_PRESERVING (1U << 7)
|
||||
#define NFS_CAP_CASE_NONPRESERVING (1U << 7)
|
||||
#define NFS_CAP_REBOOT_LAYOUTRETURN (1U << 8)
|
||||
#define NFS_CAP_OFFLOAD_STATUS (1U << 9)
|
||||
#define NFS_CAP_ZERO_RANGE (1U << 10)
|
||||
|
|
|
|||
|
|
@ -182,6 +182,8 @@ struct nfs_pathconf {
|
|||
struct nfs_fattr *fattr; /* Post-op attributes */
|
||||
__u32 max_link; /* max # of hard links */
|
||||
__u32 max_namelen; /* max name length */
|
||||
bool case_insensitive;
|
||||
bool case_preserving;
|
||||
};
|
||||
|
||||
struct nfs4_change_info {
|
||||
|
|
|
|||
|
|
@ -254,6 +254,13 @@ struct file_attr {
|
|||
#define FS_XFLAG_DAX 0x00008000 /* use DAX for IO */
|
||||
#define FS_XFLAG_COWEXTSIZE 0x00010000 /* CoW extent size allocator hint */
|
||||
#define FS_XFLAG_VERITY 0x00020000 /* fs-verity enabled */
|
||||
/*
|
||||
* Case handling flags (read-only, cannot be set via ioctl).
|
||||
* Default (neither set) indicates POSIX semantics: case-sensitive
|
||||
* lookups and case-preserving storage.
|
||||
*/
|
||||
#define FS_XFLAG_CASEFOLD 0x00040000 /* case-insensitive lookups */
|
||||
#define FS_XFLAG_CASENONPRESERVING 0x00080000 /* case not preserved */
|
||||
#define FS_XFLAG_HASATTR 0x80000000 /* no DIFLAG for this */
|
||||
|
||||
/* the read-only stuff doesn't really belong here, but any other place is
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user