Merge remote-tracking branches 'vfs/vfs-7.2.casefold', 'vfs/vfs-7.2.directory.delegations' and 'vfs/vfs-7.2.exportfs' into vfs-7.2-merge

This commit is contained in:
Chuck Lever 2026-06-01 11:06:45 -04:00
commit 1cd8fad24a
70 changed files with 1119 additions and 208 deletions

View File

@ -9916,7 +9916,7 @@ S: Supported
F: Documentation/filesystems/nfs/exporting.rst
F: fs/exportfs/
F: fs/fhandle.c
F: include/linux/exportfs.h
F: include/linux/exportfs*.h
FILESYSTEMS [IDMAPPED MOUNTS]
M: Christian Brauner <brauner@kernel.org>

View File

@ -547,7 +547,7 @@ int notify_change(struct mnt_idmap *idmap, struct dentry *dentry,
* breaking the delegation in this case.
*/
if (!(ia_valid & ATTR_DELEG)) {
error = try_break_deleg(inode, delegated_inode);
error = try_break_deleg(inode, 0, delegated_inode);
if (error)
return error;
}

View File

@ -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,

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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);

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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);

View File

@ -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,
};

View File

@ -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);

View File

@ -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,
};

View File

@ -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)

View File

@ -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,
};

View File

@ -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;

View File

@ -1582,30 +1582,96 @@ static bool leases_conflict(struct file_lock_core *lc, struct file_lock_core *bc
return rc;
}
static bool
any_leases_conflict(struct inode *inode, struct file_lease *breaker)
#define IGNORE_MASK (FL_IGN_DIR_CREATE | FL_IGN_DIR_DELETE | FL_IGN_DIR_RENAME)
/**
* inode_lease_ignore_mask - return union of all ignored inode events for this inode
* @inode: inode of which to get ignore mask
*
* Walk the list of leases, and return the result of all of
* their FL_IGN_DIR_* bits or'ed together.
*/
u32
inode_lease_ignore_mask(struct inode *inode)
{
struct file_lock_context *ctx = inode->i_flctx;
struct file_lock_context *ctx;
struct file_lock_core *flc;
u32 mask = 0;
lockdep_assert_held(&ctx->flc_lock);
ctx = locks_inode_context(inode);
if (!ctx)
return 0;
spin_lock(&ctx->flc_lock);
list_for_each_entry(flc, &ctx->flc_lease, flc_list) {
if (leases_conflict(flc, &breaker->c))
return true;
mask |= flc->flc_flags & IGNORE_MASK;
/* If we already have everything, we can stop */
if (mask == IGNORE_MASK)
break;
}
spin_unlock(&ctx->flc_lock);
return mask;
}
EXPORT_SYMBOL_GPL(inode_lease_ignore_mask);
static bool
ignore_dir_deleg_break(struct file_lease *fl, unsigned int flags)
{
if ((flags & LEASE_BREAK_DIR_CREATE) && (fl->c.flc_flags & FL_IGN_DIR_CREATE))
return true;
if ((flags & LEASE_BREAK_DIR_DELETE) && (fl->c.flc_flags & FL_IGN_DIR_DELETE))
return true;
if ((flags & LEASE_BREAK_DIR_RENAME) && (fl->c.flc_flags & FL_IGN_DIR_RENAME))
return true;
return false;
}
static unsigned int
break_lease_flags_to_type(unsigned int flags)
{
if (flags & LEASE_BREAK_LEASE)
return FL_LEASE;
else if (flags & LEASE_BREAK_DELEG)
return FL_DELEG;
else if (flags & LEASE_BREAK_LAYOUT)
return FL_LAYOUT;
else
return 0;
}
static struct file_lease *
first_visible_lease(struct inode *inode, struct file_lease *new_fl, unsigned int flags)
{
struct file_lock_context *ctx = locks_inode_context(inode);
struct file_lease *fl;
lockdep_assert_held(&ctx->flc_lock);
list_for_each_entry(fl, &ctx->flc_lease, c.flc_list) {
if (!leases_conflict(&fl->c, &new_fl->c))
continue;
if (S_ISDIR(inode->i_mode) && ignore_dir_deleg_break(fl, flags))
continue;
return fl;
}
return NULL;
}
/**
* __break_lease - revoke all outstanding leases on file
* @inode: the inode of the file to return
* @flags: LEASE_BREAK_* flags
* __break_lease - revoke all outstanding leases on file
* @inode: the inode of the file to return
* @flags: LEASE_BREAK_* flags
*
* break_lease (inlined for speed) has checked there already is at least
* some kind of lock (maybe a lease) on this file. Leases are broken on
* a call to open() or truncate(). This function can block waiting for the
* lease break unless you specify LEASE_BREAK_NONBLOCK.
* break_lease (inlined for speed) has checked there already is at least
* some kind of lock (maybe a lease) on this file. Leases and Delegations
* are broken on a call to open() or truncate(). Delegations are also
* broken on any event that would change the ctime. Directory delegations
* are broken whenever the directory changes (unless the delegation is set
* up to ignore the event). This function can block waiting for the lease
* break unless you specify LEASE_BREAK_NONBLOCK.
*/
int __break_lease(struct inode *inode, unsigned int flags)
{
@ -1617,13 +1683,10 @@ int __break_lease(struct inode *inode, unsigned int flags)
bool want_write = !(flags & LEASE_BREAK_OPEN_RDONLY);
int error = 0;
if (flags & LEASE_BREAK_LEASE)
type = FL_LEASE;
else if (flags & LEASE_BREAK_DELEG)
type = FL_DELEG;
else if (flags & LEASE_BREAK_LAYOUT)
type = FL_LAYOUT;
else
trace_break_lease(inode, flags);
type = break_lease_flags_to_type(flags);
if (!type)
return -EINVAL;
new_fl = lease_alloc(NULL, type, want_write ? F_WRLCK : F_RDLCK);
@ -1642,7 +1705,7 @@ int __break_lease(struct inode *inode, unsigned int flags)
time_out_leases(inode, &dispose);
if (!any_leases_conflict(inode, new_fl))
if (!first_visible_lease(inode, new_fl, flags))
goto out;
break_time = 0;
@ -1655,6 +1718,8 @@ int __break_lease(struct inode *inode, unsigned int flags)
list_for_each_entry_safe(fl, tmp, &ctx->flc_lease, c.flc_list) {
if (!leases_conflict(&fl->c, &new_fl->c))
continue;
if (S_ISDIR(inode->i_mode) && ignore_dir_deleg_break(fl, flags))
continue;
if (want_write) {
if (fl->c.flc_flags & FL_UNLOCK_PENDING)
continue;
@ -1670,7 +1735,8 @@ int __break_lease(struct inode *inode, unsigned int flags)
locks_delete_lock_ctx(&fl->c, &dispose);
}
if (list_empty(&ctx->flc_lease))
fl = first_visible_lease(inode, new_fl, flags);
if (!fl)
goto out;
if (flags & LEASE_BREAK_NONBLOCK) {
@ -1680,7 +1746,6 @@ int __break_lease(struct inode *inode, unsigned int flags)
}
restart:
fl = list_first_entry(&ctx->flc_lease, struct file_lease, c.flc_list);
break_time = fl->fl_break_time;
if (break_time != 0) {
if (time_after(jiffies, break_time)) {
@ -1691,7 +1756,7 @@ int __break_lease(struct inode *inode, unsigned int flags)
} else
break_time++;
locks_insert_block(&fl->c, &new_fl->c, leases_conflict);
trace_break_lease_block(inode, new_fl);
trace_break_lease_block(inode, fl);
spin_unlock(&ctx->flc_lock);
percpu_up_read(&file_rwsem);
@ -1702,7 +1767,7 @@ int __break_lease(struct inode *inode, unsigned int flags)
percpu_down_read(&file_rwsem);
spin_lock(&ctx->flc_lock);
trace_break_lease_unblock(inode, new_fl);
trace_break_lease_unblock(inode, NULL);
__locks_delete_block(&new_fl->c);
if (error >= 0) {
/*
@ -1711,7 +1776,8 @@ int __break_lease(struct inode *inode, unsigned int flags)
*/
if (error == 0)
time_out_leases(inode, &dispose);
if (any_leases_conflict(inode, new_fl))
fl = first_visible_lease(inode, new_fl, flags);
if (fl)
goto restart;
error = 0;
}

View File

@ -4198,7 +4198,7 @@ int vfs_create(struct mnt_idmap *idmap, struct dentry *dentry, umode_t mode,
error = security_inode_create(dir, dentry, mode);
if (error)
return error;
error = try_break_deleg(dir, di);
error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, di);
if (error)
return error;
error = dir->i_op->create(idmap, dir, dentry, mode, true);
@ -4497,7 +4497,7 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
/* Negative dentry, just create the file */
if (!dentry->d_inode && (open_flag & O_CREAT)) {
/* but break the directory lease first! */
error = try_break_deleg(dir_inode, delegated_inode);
error = try_break_deleg(dir_inode, LEASE_BREAK_DIR_CREATE, delegated_inode);
if (error)
goto out_dput;
@ -5113,7 +5113,7 @@ int vfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
if (error)
return error;
error = try_break_deleg(dir, delegated_inode);
error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, delegated_inode);
if (error)
return error;
@ -5254,7 +5254,7 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
if (max_links && dir->i_nlink >= max_links)
goto err;
error = try_break_deleg(dir, delegated_inode);
error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, delegated_inode);
if (error)
goto err;
@ -5359,7 +5359,7 @@ int vfs_rmdir(struct mnt_idmap *idmap, struct inode *dir,
if (error)
goto out;
error = try_break_deleg(dir, delegated_inode);
error = try_break_deleg(dir, LEASE_BREAK_DIR_DELETE, delegated_inode);
if (error)
goto out;
@ -5489,10 +5489,10 @@ int vfs_unlink(struct mnt_idmap *idmap, struct inode *dir,
else {
error = security_inode_unlink(dir, dentry);
if (!error) {
error = try_break_deleg(dir, delegated_inode);
error = try_break_deleg(dir, LEASE_BREAK_DIR_DELETE, delegated_inode);
if (error)
goto out;
error = try_break_deleg(target, delegated_inode);
error = try_break_deleg(target, 0, delegated_inode);
if (error)
goto out;
error = dir->i_op->unlink(dir, dentry);
@ -5636,7 +5636,7 @@ int vfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
if (error)
return error;
error = try_break_deleg(dir, delegated_inode);
error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, delegated_inode);
if (error)
return error;
@ -5767,9 +5767,9 @@ int vfs_link(struct dentry *old_dentry, struct mnt_idmap *idmap,
else if (max_links && inode->i_nlink >= max_links)
error = -EMLINK;
else {
error = try_break_deleg(dir, delegated_inode);
error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, delegated_inode);
if (!error)
error = try_break_deleg(inode, delegated_inode);
error = try_break_deleg(inode, 0, delegated_inode);
if (!error)
error = dir->i_op->link(old_dentry, dir, new_dentry);
}
@ -6033,21 +6033,24 @@ int vfs_rename(struct renamedata *rd)
old_dir->i_nlink >= max_links)
goto out;
}
error = try_break_deleg(old_dir, delegated_inode);
error = try_break_deleg(old_dir,
old_dir == new_dir ? LEASE_BREAK_DIR_RENAME :
LEASE_BREAK_DIR_DELETE,
delegated_inode);
if (error)
goto out;
if (new_dir != old_dir) {
error = try_break_deleg(new_dir, delegated_inode);
error = try_break_deleg(new_dir, LEASE_BREAK_DIR_CREATE, delegated_inode);
if (error)
goto out;
}
if (!is_dir) {
error = try_break_deleg(source, delegated_inode);
error = try_break_deleg(source, 0, delegated_inode);
if (error)
goto out;
}
if (target && !new_is_dir) {
error = try_break_deleg(target, delegated_inode);
error = try_break_deleg(target, 0, delegated_inode);
if (error)
goto out;
}

View File

@ -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,28 @@ 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);
if (clp->rpc_ops->version < 4 || server->namelen == 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) {
unsigned int caps = server->caps;
if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0)
server->namelen = pathinfo.max_namelen;
caps &= ~(NFS_CAP_CASE_INSENSITIVE |
NFS_CAP_CASE_NONPRESERVING);
if (pathinfo.case_insensitive)
caps |= NFS_CAP_CASE_INSENSITIVE;
if (!pathinfo.case_preserving)
caps |= NFS_CAP_CASE_NONPRESERVING;
server->caps = caps;
}
} else if (clp->rpc_ops->version < 4) {
server->caps &= ~(NFS_CAP_CASE_INSENSITIVE |
NFS_CAP_CASE_NONPRESERVING);
}
}
if (clp->rpc_ops->discover_trunking != NULL &&

View File

@ -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);

View File

@ -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 {

View File

@ -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)

View File

@ -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,

View File

@ -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;
}

View File

@ -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,

View File

@ -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 = {

View File

@ -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,
};

View File

@ -2,7 +2,7 @@
/*
* Copyright (c) 2014-2016 Christoph Hellwig.
*/
#include <linux/exportfs.h>
#include <linux/exportfs_block.h>
#include <linux/iomap.h>
#include <linux/slab.h>
#include <linux/pr.h>
@ -32,8 +32,8 @@ nfsd4_block_map_extent(struct inode *inode, const struct svc_fh *fhp,
u32 device_generation = 0;
int error;
error = sb->s_export_op->map_blocks(inode, offset, length, &iomap,
iomode != IOMODE_READ, &device_generation);
error = sb->s_export_op->block_ops->map_blocks(inode, offset, length,
&iomap, iomode != IOMODE_READ, &device_generation);
if (error) {
if (error == -ENXIO)
return nfserr_layoutunavailable;
@ -179,23 +179,20 @@ static __be32
nfsd4_block_commit_blocks(struct inode *inode, struct nfsd4_layoutcommit *lcp,
struct iomap *iomaps, int nr_iomaps)
{
struct timespec64 mtime = inode_get_mtime(inode);
struct iattr iattr = { .ia_valid = 0 };
int error;
if (lcp->lc_mtime.tv_nsec == UTIME_NOW ||
timespec64_compare(&lcp->lc_mtime, &mtime) < 0)
lcp->lc_mtime = current_time(inode);
iattr.ia_valid |= ATTR_ATIME | ATTR_CTIME | ATTR_MTIME;
iattr.ia_atime = iattr.ia_ctime = iattr.ia_mtime = lcp->lc_mtime;
if (lcp->lc_size_chg) {
iattr.ia_valid |= ATTR_SIZE;
iattr.ia_size = lcp->lc_newsize;
}
error = inode->i_sb->s_export_op->commit_blocks(inode, iomaps,
nr_iomaps, &iattr);
/*
* This ignores the client provided mtime in loca_time_modify, as a
* fully client specified mtime doesn't really fit into the Linux
* multi-grain timestamp architecture.
*
* RFC 8881 Section 18.42 makes it clear that the client provided
* timestamp is a "may" condition, and clients that want to force a
* specific timestamp should send a separate SETATTR in the compound.
*/
error = inode->i_sb->s_export_op->block_ops->commit_blocks(inode,
iomaps, nr_iomaps,
lcp->lc_size_chg ? lcp->lc_newsize : 0);
kfree(iomaps);
return nfserrno(error);
}
@ -218,8 +215,8 @@ nfsd4_block_get_device_info_simple(struct super_block *sb,
b->type = PNFS_BLOCK_VOLUME_SIMPLE;
b->simple.sig_len = PNFS_BLOCK_UUID_LEN;
return sb->s_export_op->get_uuid(sb, b->simple.sig, &b->simple.sig_len,
&b->simple.offset);
return sb->s_export_op->block_ops->get_uuid(sb, b->simple.sig,
&b->simple.sig_len, &b->simple.offset);
}
static __be32

View File

@ -735,7 +735,8 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
goto out4;
err = 0;
nfsd4_setup_layout_type(&exp);
if (exp.ex_flags & NFSEXP_PNFS)
nfsd4_setup_layout_type(&exp);
}
expp = svc_export_lookup(&exp);

View File

@ -710,23 +710,46 @@ 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;
case -ESTALE:
resp->status = nfserr_stale;
break;
default:
resp->status = nfserr_serverfault;
break;
}
}

View File

@ -2,7 +2,7 @@
/*
* Copyright (c) 2014 Christoph Hellwig.
*/
#include <linux/blkdev.h>
#include <linux/exportfs_block.h>
#include <linux/kmod.h>
#include <linux/file.h>
#include <linux/jhash.h>
@ -127,30 +127,17 @@ nfsd4_set_deviceid(struct nfsd4_deviceid *id, const struct svc_fh *fhp,
void nfsd4_setup_layout_type(struct svc_export *exp)
{
#if defined(CONFIG_NFSD_BLOCKLAYOUT) || defined(CONFIG_NFSD_SCSILAYOUT)
struct super_block *sb = exp->ex_path.mnt->mnt_sb;
#endif
expfs_block_layouts_t block_supported = exportfs_layouts_supported(sb);
if (!(exp->ex_flags & NFSEXP_PNFS))
return;
#ifdef CONFIG_NFSD_FLEXFILELAYOUT
exp->ex_layout_types |= 1 << LAYOUT_FLEX_FILES;
#endif
#ifdef CONFIG_NFSD_BLOCKLAYOUT
if (sb->s_export_op->get_uuid &&
sb->s_export_op->map_blocks &&
sb->s_export_op->commit_blocks)
if (IS_ENABLED(CONFIG_NFSD_FLEXFILELAYOUT))
exp->ex_layout_types |= 1 << LAYOUT_FLEX_FILES;
if (IS_ENABLED(CONFIG_NFSD_BLOCKLAYOUT) &&
(block_supported & EXPFS_BLOCK_IN_BAND_ID))
exp->ex_layout_types |= 1 << LAYOUT_BLOCK_VOLUME;
#endif
#ifdef CONFIG_NFSD_SCSILAYOUT
if (sb->s_export_op->map_blocks &&
sb->s_export_op->commit_blocks &&
sb->s_bdev &&
sb->s_bdev->bd_disk->fops->pr_ops &&
sb->s_bdev->bd_disk->fops->get_unique_id)
if (IS_ENABLED(CONFIG_NFSD_SCSILAYOUT) &&
(block_supported & EXPFS_BLOCK_OUT_OF_BAND_ID))
exp->ex_layout_types |= 1 << LAYOUT_SCSI;
#endif
}
void nfsd4_close_layout(struct nfs4_layout_stateid *ls)

View File

@ -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,
@ -3854,13 +3883,16 @@ static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = {
/*
* Note: @fhp can be NULL; in this case, we might have to compose the filehandle
* ourselves.
* ourselves. @case_cache is NULL for callers that encode a single dentry
* (GETATTR, the buffer wrapper); READDIR passes a per-request cache so
* non-directory children share the parent's case-folding probe result.
*/
static __be32
nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
struct svc_fh *fhp, struct svc_export *exp,
struct dentry *dentry, const u32 *bmval,
int ignore_crossmnt)
int ignore_crossmnt,
struct nfsd_case_attrs_cache *case_cache)
{
DECLARE_BITMAP(attr_bitmap, ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops));
struct nfs4_delegation *dp = NULL;
@ -3968,6 +4000,47 @@ 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)) {
/*
* In a batched encoder (READDIR) every non-directory
* child shares the same case-folding answer, so the
* directory being read is probed once and the result is
* cached. The probe targets case_cache->dir, the held
* readdir filehandle's dentry, instead of the child's
* locklessly-acquired dentry, which a concurrent rename
* could move under an unrelated parent. Directory
* entries are queried directly because casefold-capable
* filesystems answer per directory.
*
* 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 (case_cache && !d_is_dir(dentry)) {
if (!case_cache->valid) {
err = nfsd_get_case_info(case_cache->dir,
&case_cache->insensitive,
&case_cache->preserving);
if (err && err != -EOPNOTSUPP)
goto out_nfserr;
case_cache->valid = true;
}
args.case_insensitive = case_cache->insensitive;
args.case_preserving = case_cache->preserving;
} else {
err = nfsd_get_case_info(dentry,
&args.case_insensitive,
&args.case_preserving);
if (err && err != -EOPNOTSUPP)
goto out_nfserr;
}
}
if (attrmask[0] & FATTR4_WORD0_ACL) {
err = nfsd4_get_nfs4_acl(rqstp, dentry, &args.acl);
@ -4124,7 +4197,7 @@ __be32 nfsd4_encode_fattr_to_buf(__be32 **p, int words,
svcxdr_init_encode_from_buffer(&xdr, &dummy, *p, words << 2);
ret = nfsd4_encode_fattr4(rqstp, &xdr, fhp, exp, dentry, bmval,
ignore_crossmnt);
ignore_crossmnt, NULL);
*p = xdr.p;
return ret;
}
@ -4162,6 +4235,7 @@ nfsd4_encode_entry4_fattr(struct nfsd4_readdir *cd, const char *name,
struct dentry *dentry;
__be32 nfserr;
int ignore_crossmnt = 0;
bool crossed = false;
dentry = lookup_one_positive_unlocked(&nop_mnt_idmap,
&QSTR_LEN(name, namlen),
@ -4198,11 +4272,18 @@ nfsd4_encode_entry4_fattr(struct nfsd4_readdir *cd, const char *name,
nfserr = check_nfsd_access(exp, cd->rd_rqstp, false);
if (nfserr)
goto out_put;
crossed = true;
}
out_encode:
/*
* A crossed entry no longer shares a parent with the directory
* being read, so it must neither consume nor populate the
* per-readdir case-folding cache.
*/
nfserr = nfsd4_encode_fattr4(cd->rd_rqstp, cd->xdr, NULL, exp, dentry,
cd->rd_bmval, ignore_crossmnt);
cd->rd_bmval, ignore_crossmnt,
crossed ? NULL : &cd->rd_case_cache);
out_put:
dput(dentry);
exp_put(exp);
@ -4449,7 +4530,7 @@ nfsd4_encode_getattr(struct nfsd4_compoundres *resp, __be32 nfserr,
/* obj_attributes */
return nfsd4_encode_fattr4(resp->rqstp, xdr, fhp, fhp->fh_export,
fhp->fh_dentry, getattr->ga_bmval, 0);
fhp->fh_dentry, getattr->ga_bmval, 0, NULL);
}
static __be32
@ -4976,6 +5057,8 @@ static __be32 nfsd4_encode_dirlist4(struct xdr_stream *xdr,
readdir->rd_maxcount = maxcount;
readdir->common.err = 0;
readdir->cookie_offset = 0;
readdir->rd_case_cache.dir = readdir->rd_fhp->fh_dentry;
readdir->rd_case_cache.valid = false;
offset = readdir->rd_cookie;
status = nfsd_readdir(readdir->rd_rqstp, readdir->rd_fhp, &offset,
&readdir->common, nfsd4_encode_entry4);

View File

@ -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,88 @@ 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_kernel_cred(&init_task);
if (!probe) {
err = -ENOMEM;
goto out;
}
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;
}
}

View File

@ -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);

View File

@ -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 {

View File

@ -432,6 +432,19 @@ struct nfsd4_read {
u32 rd_eof; /* response */
};
/*
* Cache the case-folding properties of @dir so a batched encoder
* (e.g., READDIR) does not re-probe per child. @dir is the
* directory being read, held by the request, so it is stable
* against rename for the duration of the cache's lifetime.
*/
struct nfsd_case_attrs_cache {
struct dentry *dir;
bool valid;
bool insensitive;
bool preserving;
};
struct nfsd4_readdir {
u64 rd_cookie; /* request */
nfs4_verifier rd_verf; /* request */
@ -444,6 +457,7 @@ struct nfsd4_readdir {
struct readdir_cd common;
struct xdr_stream *xdr;
int cookie_offset;
struct nfsd_case_attrs_cache rd_case_cache;
};
struct nfsd4_release_lockowner {

View File

@ -14,6 +14,9 @@
#include <linux/fsnotify_backend.h>
#include "fsnotify.h"
#define CREATE_TRACE_POINTS
#include <trace/events/fsnotify.h>
/*
* Clear all of the marks on an inode when it is being evicted from core
*/
@ -504,6 +507,8 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
int ret = 0;
__u32 test_mask, marks_mask = 0;
trace_fsnotify(mask, data, data_type, dir, file_name, inode, cookie);
if (path)
mnt = real_mount(path->mnt);

View File

@ -343,6 +343,35 @@ void fsnotify_recalc_mask(struct fsnotify_mark_connector *conn)
fsnotify_conn_set_children_dentry_flags(conn);
}
/**
* fsnotify_modify_mark_mask - set and/or clear flags in a mark's mask
* @mark: mark to be modified
* @set: bits to be set in mask
* @clear: bits to be cleared in mask
*
* Modify a fsnotify_mark mask as directed, and update its associated conn.
* The caller is expected to hold a reference to the mark.
*/
void fsnotify_modify_mark_mask(struct fsnotify_mark *mark, u32 set, u32 clear)
{
bool recalc = false;
u32 mask;
WARN_ON_ONCE(clear & set);
spin_lock(&mark->lock);
mask = mark->mask;
mark->mask |= set;
mark->mask &= ~clear;
if (mark->mask != mask)
recalc = true;
spin_unlock(&mark->lock);
if (recalc)
fsnotify_recalc_mask(mark->connector);
}
EXPORT_SYMBOL_GPL(fsnotify_modify_mark_mask);
/* Free all connectors queued for freeing once SRCU period ends */
static void fsnotify_connector_destroy_workfn(struct work_struct *work)
{

View File

@ -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 = {

View File

@ -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 = {

View File

@ -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,

View File

@ -1126,7 +1126,7 @@ int vfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
if (error)
goto out_inode_unlock;
error = try_break_deleg(inode, &delegated_inode);
error = try_break_deleg(inode, 0, &delegated_inode);
if (error)
goto out_inode_unlock;
@ -1234,7 +1234,7 @@ int vfs_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
if (error)
goto out_inode_unlock;
error = try_break_deleg(inode, &delegated_inode);
error = try_break_deleg(inode, 0, &delegated_inode);
if (error)
goto out_inode_unlock;

View File

@ -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"
@ -1165,6 +1166,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,
@ -1183,6 +1234,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 = {
@ -1193,6 +1245,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,

View File

@ -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;

View File

@ -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,
};

View File

@ -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"
@ -5561,16 +5562,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,

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -306,7 +306,7 @@ __vfs_setxattr_locked(struct mnt_idmap *idmap, struct dentry *dentry,
if (error)
goto out;
error = try_break_deleg(inode, delegated_inode);
error = try_break_deleg(inode, 0, delegated_inode);
if (error)
goto out;
@ -564,7 +564,7 @@ __vfs_removexattr_locked(struct mnt_idmap *idmap,
if (error)
goto out;
error = try_break_deleg(inode, delegated_inode);
error = try_break_deleg(inode, 0, delegated_inode);
if (error)
goto out;

View File

@ -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;
}

View File

@ -244,8 +244,6 @@ const struct export_operations xfs_export_operations = {
.get_parent = xfs_fs_get_parent,
.commit_metadata = xfs_fs_nfs_commit_metadata,
#ifdef CONFIG_EXPORTFS_BLOCK_OPS
.get_uuid = xfs_fs_get_uuid,
.map_blocks = xfs_fs_map_blocks,
.commit_blocks = xfs_fs_commit_blocks,
.block_ops = &xfs_export_block_ops,
#endif
};

View File

@ -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;
}

View File

@ -13,6 +13,7 @@
#include "xfs_bmap.h"
#include "xfs_iomap.h"
#include "xfs_pnfs.h"
#include <linux/exportfs_block.h>
/*
* Ensure that we do not have any outstanding pNFS layouts that can be used by
@ -45,11 +46,22 @@ xfs_break_leased_layouts(
return error;
}
static expfs_block_layouts_t
xfs_fs_layouts_supported(
struct super_block *sb)
{
expfs_block_layouts_t supported = EXPFS_BLOCK_IN_BAND_ID;
if (exportfs_bdev_supports_out_of_band_id(sb->s_bdev))
supported |= EXPFS_BLOCK_OUT_OF_BAND_ID;
return supported;
}
/*
* Get a unique ID including its location so that the client can identify
* the exported device.
*/
int
static int
xfs_fs_get_uuid(
struct super_block *sb,
u8 *buf,
@ -104,7 +116,7 @@ xfs_fs_map_update_inode(
/*
* Get a layout for the pNFS client.
*/
int
static int
xfs_fs_map_blocks(
struct inode *inode,
loff_t offset,
@ -252,28 +264,27 @@ xfs_pnfs_validate_isize(
* to manually flush the cache here similar to what the fsync code path does
* for datasyncs on files that have no dirty metadata.
*/
int
static int
xfs_fs_commit_blocks(
struct inode *inode,
struct iomap *maps,
int nr_maps,
struct iattr *iattr)
loff_t new_size)
{
struct xfs_inode *ip = XFS_I(inode);
struct xfs_mount *mp = ip->i_mount;
struct xfs_trans *tp;
struct timespec64 now;
bool update_isize = false;
int error, i;
loff_t size;
ASSERT(iattr->ia_valid & (ATTR_ATIME|ATTR_CTIME|ATTR_MTIME));
xfs_ilock(ip, XFS_IOLOCK_EXCL);
size = i_size_read(inode);
if ((iattr->ia_valid & ATTR_SIZE) && iattr->ia_size > size) {
if (new_size > size) {
update_isize = true;
size = iattr->ia_size;
size = new_size;
}
for (i = 0; i < nr_maps; i++) {
@ -318,11 +329,13 @@ xfs_fs_commit_blocks(
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
ASSERT(!(iattr->ia_valid & (ATTR_UID | ATTR_GID)));
setattr_copy(&nop_mnt_idmap, inode, iattr);
now = inode_set_ctime_current(inode);
inode_set_atime_to_ts(inode, now);
inode_set_mtime_to_ts(inode, now);
if (update_isize) {
i_size_write(inode, iattr->ia_size);
ip->i_disk_size = iattr->ia_size;
i_size_write(inode, new_size);
ip->i_disk_size = new_size;
}
xfs_trans_set_sync(tp);
@ -332,3 +345,10 @@ xfs_fs_commit_blocks(
xfs_iunlock(ip, XFS_IOLOCK_EXCL);
return error;
}
const struct exportfs_block_ops xfs_export_block_ops = {
.layouts_supported = xfs_fs_layouts_supported,
.get_uuid = xfs_fs_get_uuid,
.map_blocks = xfs_fs_map_blocks,
.commit_blocks = xfs_fs_commit_blocks,
};

View File

@ -2,13 +2,9 @@
#ifndef _XFS_PNFS_H
#define _XFS_PNFS_H 1
#ifdef CONFIG_EXPORTFS_BLOCK_OPS
int xfs_fs_get_uuid(struct super_block *sb, u8 *buf, u32 *len, u64 *offset);
int xfs_fs_map_blocks(struct inode *inode, loff_t offset, u64 length,
struct iomap *iomap, bool write, u32 *device_generation);
int xfs_fs_commit_blocks(struct inode *inode, struct iomap *maps, int nr_maps,
struct iattr *iattr);
#include <linux/exportfs_block.h>
#ifdef CONFIG_EXPORTFS_BLOCK_OPS
int xfs_break_leased_layouts(struct inode *inode, uint *iolock,
bool *did_unlock);
#else
@ -18,4 +14,7 @@ xfs_break_leased_layouts(struct inode *inode, uint *iolock, bool *did_unlock)
return 0;
}
#endif /* CONFIG_EXPORTFS_BLOCK_OPS */
extern const struct exportfs_block_ops xfs_export_block_ops;
#endif /* _XFS_PNFS_H */

View File

@ -6,9 +6,8 @@
#include <linux/path.h>
struct dentry;
struct iattr;
struct exportfs_block_ops;
struct inode;
struct iomap;
struct super_block;
struct vfsmount;
@ -260,19 +259,13 @@ struct handle_to_path_ctx {
* @commit_metadata:
* @commit_metadata should commit metadata changes to stable storage.
*
* @get_uuid:
* Get a filesystem unique signature exposed to clients.
*
* @map_blocks:
* Map and, if necessary, allocate blocks for a layout.
*
* @commit_blocks:
* Commit blocks in a layout once the client is done with them.
*
* @flags:
* Allows the filesystem to communicate to nfsd that it may want to do things
* differently when dealing with it.
*
* @block_ops:
* Operations for layout grants to block on the underlying device.
*
* Locking rules:
* get_parent is called with child->d_inode->i_rwsem down
* get_name is not (which is possibly inconsistent)
@ -290,12 +283,6 @@ struct export_operations {
struct dentry * (*get_parent)(struct dentry *child);
int (*commit_metadata)(struct inode *inode);
int (*get_uuid)(struct super_block *sb, u8 *buf, u32 *len, u64 *offset);
int (*map_blocks)(struct inode *inode, loff_t offset,
u64 len, struct iomap *iomap,
bool write, u32 *device_generation);
int (*commit_blocks)(struct inode *inode, struct iomap *iomaps,
int nr_iomaps, struct iattr *iattr);
int (*permission)(struct handle_to_path_ctx *ctx, unsigned int oflags);
struct file * (*open)(const struct path *path, unsigned int oflags);
#define EXPORT_OP_NOWCC (0x1) /* don't collect v3 wcc data */
@ -308,6 +295,10 @@ struct export_operations {
#define EXPORT_OP_FLUSH_ON_CLOSE (0x20) /* fs flushes file data on close */
#define EXPORT_OP_NOLOCKS (0x40) /* no file locking support */
unsigned long flags;
#ifdef CONFIG_EXPORTFS_BLOCK_OPS
const struct exportfs_block_ops *block_ops;
#endif
};
/**

View File

@ -0,0 +1,88 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2014-2026 Christoph Hellwig.
*
* Support for exportfs-based layout grants for direct block device access.
*/
#ifndef LINUX_EXPORTFS_BLOCK_H
#define LINUX_EXPORTFS_BLOCK_H 1
#include <linux/blkdev.h>
#include <linux/exportfs.h>
#include <linux/fs.h>
struct inode;
struct iomap;
struct super_block;
/*
* There are the two types of block-style layout support:
* - In-band implies a device identified by a unique cookie inside the actual
* device address space checked by the ->get_uuid method as used by the pNFS
* block layout. This is a bit dangerous and deprecated.
* - Out of band implies identification by out of band unique identifiers
* specified by the storage protocol, which is much safer and used by the
* pNFS SCSI/NVMe layouts.
*/
typedef unsigned int __bitwise expfs_block_layouts_t;
#define EXPFS_BLOCK_FLAG(__bit) \
((__force expfs_block_layouts_t)(1u << __bit))
#define EXPFS_BLOCK_IN_BAND_ID EXPFS_BLOCK_FLAG(0)
#define EXPFS_BLOCK_OUT_OF_BAND_ID EXPFS_BLOCK_FLAG(1)
struct exportfs_block_ops {
/*
* Returns the EXPFS_BLOCK_* bitmap of supported layout types.
*/
expfs_block_layouts_t (*layouts_supported)(struct super_block *sb);
/*
* Get the in-band device unique signature exposed to clients.
*/
int (*get_uuid)(struct super_block *sb, u8 *buf, u32 *len, u64 *offset);
/*
* Map blocks for direct block access.
* If @write is %true, also allocate the blocks for the range if needed.
*/
int (*map_blocks)(struct inode *inode, loff_t offset, u64 len,
struct iomap *iomap, bool write,
u32 *device_generation);
/*
* Commit blocks previously handed out by ->map_blocks and written to by
* the client.
*/
int (*commit_blocks)(struct inode *inode, struct iomap *iomaps,
int nr_iomaps, loff_t new_size);
};
static inline bool
exportfs_bdev_supports_out_of_band_id(struct block_device *bdev)
{
return bdev->bd_disk->fops->pr_ops &&
bdev->bd_disk->fops->get_unique_id;
}
#ifdef CONFIG_EXPORTFS_BLOCK_OPS
static inline expfs_block_layouts_t
exportfs_layouts_supported(struct super_block *sb)
{
const struct exportfs_block_ops *bops = sb->s_export_op->block_ops;
if (!bops ||
!bops->layouts_supported ||
WARN_ON_ONCE(!bops->map_blocks) ||
WARN_ON_ONCE(!bops->commit_blocks))
return 0;
return bops->layouts_supported(sb);
}
#else
static inline expfs_block_layouts_t
exportfs_layouts_supported(struct super_block *sb)
{
return 0;
}
#endif /* CONFIG_EXPORTFS_BLOCK_OPS */
#endif /* LINUX_EXPORTFS_BLOCK_H */

View File

@ -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 \

View File

@ -4,19 +4,22 @@
#include <linux/fs.h>
#define FL_POSIX 1
#define FL_FLOCK 2
#define FL_DELEG 4 /* NFSv4 delegation */
#define FL_ACCESS 8 /* not trying to lock, just looking */
#define FL_EXISTS 16 /* when unlocking, test for existence */
#define FL_LEASE 32 /* lease held on this file */
#define FL_CLOSE 64 /* unlock on close */
#define FL_SLEEP 128 /* A blocking lock */
#define FL_DOWNGRADE_PENDING 256 /* Lease is being downgraded */
#define FL_UNLOCK_PENDING 512 /* Lease is being broken */
#define FL_OFDLCK 1024 /* lock is "owned" by struct file */
#define FL_LAYOUT 2048 /* outstanding pNFS layout */
#define FL_RECLAIM 4096 /* reclaiming from a reboot server */
#define FL_POSIX BIT(0) /* POSIX lock */
#define FL_FLOCK BIT(1) /* BSD lock */
#define FL_DELEG BIT(2) /* NFSv4 delegation */
#define FL_ACCESS BIT(3) /* not trying to lock, just looking */
#define FL_EXISTS BIT(4) /* when unlocking, test for existence */
#define FL_LEASE BIT(5) /* file lease */
#define FL_CLOSE BIT(6) /* unlock on close */
#define FL_SLEEP BIT(7) /* A blocking lock */
#define FL_DOWNGRADE_PENDING BIT(8) /* Lease is being downgraded */
#define FL_UNLOCK_PENDING BIT(9) /* Lease is being broken */
#define FL_OFDLCK BIT(10) /* POSIX lock "owned" by struct file */
#define FL_LAYOUT BIT(11) /* outstanding pNFS layout */
#define FL_RECLAIM BIT(12) /* reclaiming from a reboot server */
#define FL_IGN_DIR_CREATE BIT(13) /* ignore DIR_CREATE events */
#define FL_IGN_DIR_DELETE BIT(14) /* ignore DIR_DELETE events */
#define FL_IGN_DIR_RENAME BIT(15) /* ignore DIR_RENAME events */
#define FL_CLOSE_POSIX (FL_POSIX | FL_CLOSE)
@ -26,6 +29,15 @@
*/
#define FILE_LOCK_DEFERRED 1
#define LEASE_BREAK_LEASE BIT(0) // break leases and delegations
#define LEASE_BREAK_DELEG BIT(1) // break delegations only
#define LEASE_BREAK_LAYOUT BIT(2) // break layouts only
#define LEASE_BREAK_NONBLOCK BIT(3) // non-blocking break
#define LEASE_BREAK_OPEN_RDONLY BIT(4) // readonly open event
#define LEASE_BREAK_DIR_CREATE BIT(5) // dir deleg create event
#define LEASE_BREAK_DIR_DELETE BIT(6) // dir deleg delete event
#define LEASE_BREAK_DIR_RENAME BIT(7) // dir deleg rename event
struct file_lock;
struct file_lease;
@ -216,19 +228,13 @@ int locks_lock_inode_wait(struct inode *inode, struct file_lock *fl);
void locks_init_lease(struct file_lease *);
void locks_free_lease(struct file_lease *fl);
struct file_lease *locks_alloc_lease(void);
#define LEASE_BREAK_LEASE BIT(0) // break leases and delegations
#define LEASE_BREAK_DELEG BIT(1) // break delegations only
#define LEASE_BREAK_LAYOUT BIT(2) // break layouts only
#define LEASE_BREAK_NONBLOCK BIT(3) // non-blocking break
#define LEASE_BREAK_OPEN_RDONLY BIT(4) // readonly open event
int __break_lease(struct inode *inode, unsigned int flags);
void lease_get_mtime(struct inode *, struct timespec64 *time);
int generic_setlease(struct file *, int, struct file_lease **, void **priv);
int kernel_setlease(struct file *, int, struct file_lease **, void **);
int vfs_setlease(struct file *, int, struct file_lease **, void **);
int lease_modify(struct file_lease *, int, struct list_head *);
u32 inode_lease_ignore_mask(struct inode *inode);
struct notifier_block;
int lease_register_notifier(struct notifier_block *);
@ -516,12 +522,26 @@ static inline bool is_delegated(struct delegated_inode *di)
return di->di_inode;
}
static inline int try_break_deleg(struct inode *inode,
/**
* try_break_deleg - do a non-blocking delegation break
* @inode: inode that should have its delegations broken
* @flags: extra LEASE_BREAK_* flags to pass to break_deleg()
* @di: returns pointer to delegated inode (may be NULL)
*
* Break delegations in a non-blocking fashion. If there are
* outstanding delegations and @di is set, then an extra reference
* will be taken on @inode and @di->di_inode will be populated so
* that it may be waited upon.
*
* Returns 0 if there is no need to wait or an error. If -EWOULDBLOCK
* is returned, then @di will be populated (if non-NULL).
*/
static inline int try_break_deleg(struct inode *inode, unsigned int flags,
struct delegated_inode *di)
{
int ret;
ret = break_deleg(inode, LEASE_BREAK_NONBLOCK);
ret = break_deleg(inode, flags | LEASE_BREAK_NONBLOCK);
if (ret == -EWOULDBLOCK && di) {
di->di_inode = inode;
ihold(inode);
@ -574,7 +594,7 @@ static inline int break_deleg(struct inode *inode, unsigned int flags)
return 0;
}
static inline int try_break_deleg(struct inode *inode,
static inline int try_break_deleg(struct inode *inode, unsigned int flags,
struct delegated_inode *delegated_inode)
{
return 0;

View File

@ -257,6 +257,10 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
__u32 new_dir_mask = FS_MOVED_TO;
__u32 rename_mask = FS_RENAME;
const struct qstr *new_name = &moved->d_name;
struct fsnotify_rename_data rd = {
.moved = moved,
.target = target,
};
if (isdir) {
old_dir_mask |= FS_ISDIR;
@ -265,12 +269,12 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
}
/* Event with information about both old and new parent+name */
fsnotify_name(rename_mask, moved, FSNOTIFY_EVENT_DENTRY,
fsnotify_name(rename_mask, &rd, FSNOTIFY_EVENT_RENAME,
old_dir, old_name, 0);
fsnotify_name(old_dir_mask, source, FSNOTIFY_EVENT_INODE,
old_dir, old_name, fs_cookie);
fsnotify_name(new_dir_mask, source, FSNOTIFY_EVENT_INODE,
fsnotify_name(new_dir_mask, &rd, FSNOTIFY_EVENT_RENAME,
new_dir, new_name, fs_cookie);
if (target)

View File

@ -311,6 +311,7 @@ enum fsnotify_data_type {
FSNOTIFY_EVENT_DENTRY,
FSNOTIFY_EVENT_MNT,
FSNOTIFY_EVENT_ERROR,
FSNOTIFY_EVENT_RENAME,
};
struct fs_error_report {
@ -335,6 +336,11 @@ struct fsnotify_mnt {
u64 mnt_id;
};
struct fsnotify_rename_data {
struct dentry *moved; /* the dentry that was renamed */
struct inode *target; /* inode overwritten by rename, or NULL */
};
static inline struct inode *fsnotify_data_inode(const void *data, int data_type)
{
switch (data_type) {
@ -348,6 +354,8 @@ static inline struct inode *fsnotify_data_inode(const void *data, int data_type)
return d_inode(file_range_path(data)->dentry);
case FSNOTIFY_EVENT_ERROR:
return ((struct fs_error_report *)data)->inode;
case FSNOTIFY_EVENT_RENAME:
return d_inode(((const struct fsnotify_rename_data *)data)->moved);
default:
return NULL;
}
@ -363,6 +371,8 @@ static inline struct dentry *fsnotify_data_dentry(const void *data, int data_typ
return ((const struct path *)data)->dentry;
case FSNOTIFY_EVENT_FILE_RANGE:
return file_range_path(data)->dentry;
case FSNOTIFY_EVENT_RENAME:
return ((struct fsnotify_rename_data *)data)->moved;
default:
return NULL;
}
@ -395,6 +405,8 @@ static inline struct super_block *fsnotify_data_sb(const void *data,
return file_range_path(data)->dentry->d_sb;
case FSNOTIFY_EVENT_ERROR:
return ((struct fs_error_report *) data)->sb;
case FSNOTIFY_EVENT_RENAME:
return ((const struct fsnotify_rename_data *)data)->moved->d_sb;
default:
return NULL;
}
@ -430,6 +442,14 @@ static inline struct fs_error_report *fsnotify_data_error_report(
}
}
static inline struct inode *fsnotify_data_rename_target(const void *data,
int data_type)
{
if (data_type == FSNOTIFY_EVENT_RENAME)
return ((const struct fsnotify_rename_data *)data)->target;
return NULL;
}
static inline const struct file_range *fsnotify_data_file_range(
const void *data,
int data_type)
@ -918,6 +938,7 @@ extern void fsnotify_put_mark(struct fsnotify_mark *mark);
struct fsnotify_mark *fsnotify_next_mark(struct fsnotify_mark *mark);
extern void fsnotify_finish_user_wait(struct fsnotify_iter_info *iter_info);
extern bool fsnotify_prepare_user_wait(struct fsnotify_iter_info *iter_info);
extern void fsnotify_modify_mark_mask(struct fsnotify_mark *mark, u32 set, u32 clear);
static inline void fsnotify_init_event(struct fsnotify_event *event)
{

View File

@ -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)

View File

@ -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 {

View File

@ -28,7 +28,10 @@
{ FL_DOWNGRADE_PENDING, "FL_DOWNGRADE_PENDING" }, \
{ FL_UNLOCK_PENDING, "FL_UNLOCK_PENDING" }, \
{ FL_OFDLCK, "FL_OFDLCK" }, \
{ FL_RECLAIM, "FL_RECLAIM"})
{ FL_RECLAIM, "FL_RECLAIM" }, \
{ FL_IGN_DIR_CREATE, "FL_IGN_DIR_CREATE" }, \
{ FL_IGN_DIR_DELETE, "FL_IGN_DIR_DELETE" }, \
{ FL_IGN_DIR_RENAME, "FL_IGN_DIR_RENAME" })
#define show_fl_type(val) \
__print_symbolic(val, \
@ -117,6 +120,39 @@ DEFINE_EVENT(filelock_lock, flock_lock_inode,
TP_PROTO(struct inode *inode, struct file_lock *fl, int ret),
TP_ARGS(inode, fl, ret));
#define show_lease_break_flags(val) \
__print_flags(val, "|", \
{ LEASE_BREAK_LEASE, "LEASE" }, \
{ LEASE_BREAK_DELEG, "DELEG" }, \
{ LEASE_BREAK_LAYOUT, "LAYOUT" }, \
{ LEASE_BREAK_NONBLOCK, "NONBLOCK" }, \
{ LEASE_BREAK_OPEN_RDONLY, "OPEN_RDONLY" }, \
{ LEASE_BREAK_DIR_CREATE, "DIR_CREATE" }, \
{ LEASE_BREAK_DIR_DELETE, "DIR_DELETE" }, \
{ LEASE_BREAK_DIR_RENAME, "DIR_RENAME" })
TRACE_EVENT(break_lease,
TP_PROTO(struct inode *inode, unsigned int flags),
TP_ARGS(inode, flags),
TP_STRUCT__entry(
__field(unsigned long, i_ino)
__field(dev_t, s_dev)
__field(unsigned int, flags)
),
TP_fast_assign(
__entry->s_dev = inode->i_sb->s_dev;
__entry->i_ino = inode->i_ino;
__entry->flags = flags;
),
TP_printk("dev=0x%x:0x%x ino=0x%lx flags=%s",
MAJOR(__entry->s_dev), MINOR(__entry->s_dev),
__entry->i_ino, show_lease_break_flags(__entry->flags))
);
DECLARE_EVENT_CLASS(filelock_lease,
TP_PROTO(struct inode *inode, struct file_lease *fl),

View File

@ -0,0 +1,51 @@
/* SPDX-License-Identifier: GPL-2.0 */
#undef TRACE_SYSTEM
#define TRACE_SYSTEM fsnotify
#if !defined(_TRACE_FSNOTIFY_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_FSNOTIFY_H
#include <linux/tracepoint.h>
#include <trace/misc/fsnotify.h>
TRACE_EVENT(fsnotify,
TP_PROTO(__u32 mask, const void *data, int data_type,
struct inode *dir, const struct qstr *file_name,
struct inode *inode, u32 cookie),
TP_ARGS(mask, data, data_type, dir, file_name, inode, cookie),
TP_STRUCT__entry(
__field(__u32, mask)
__field(unsigned long, dir_ino)
__field(unsigned long, ino)
__field(dev_t, s_dev)
__field(int, data_type)
__field(u32, cookie)
__string(file_name, file_name ? (const char *)file_name->name : "")
),
TP_fast_assign(
__entry->mask = mask;
__entry->dir_ino = dir ? dir->i_ino : 0;
__entry->ino = inode ? inode->i_ino : 0;
__entry->s_dev = dir ? dir->i_sb->s_dev :
inode ? inode->i_sb->s_dev : 0;
__entry->data_type = data_type;
__entry->cookie = cookie;
__assign_str(file_name);
),
TP_printk("dev=%d:%d dir=%lu ino=%lu data_type=%d cookie=0x%x mask=0x%x %s name=%s",
MAJOR(__entry->s_dev), MINOR(__entry->s_dev),
__entry->dir_ino, __entry->ino,
__entry->data_type, __entry->cookie,
__entry->mask, show_fsnotify_mask(__entry->mask),
__get_str(file_name))
);
#endif /* _TRACE_FSNOTIFY_H */
/* This part must be outside protection */
#include <trace/define_trace.h>

View File

@ -0,0 +1,35 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Display helpers for fsnotify events
*/
#include <linux/fsnotify_backend.h>
#define show_fsnotify_mask(mask) \
__print_flags(mask, "|", \
{ FS_ACCESS, "ACCESS" }, \
{ FS_MODIFY, "MODIFY" }, \
{ FS_ATTRIB, "ATTRIB" }, \
{ FS_CLOSE_WRITE, "CLOSE_WRITE" }, \
{ FS_CLOSE_NOWRITE, "CLOSE_NOWRITE" }, \
{ FS_OPEN, "OPEN" }, \
{ FS_MOVED_FROM, "MOVED_FROM" }, \
{ FS_MOVED_TO, "MOVED_TO" }, \
{ FS_CREATE, "CREATE" }, \
{ FS_DELETE, "DELETE" }, \
{ FS_DELETE_SELF, "DELETE_SELF" }, \
{ FS_MOVE_SELF, "MOVE_SELF" }, \
{ FS_OPEN_EXEC, "OPEN_EXEC" }, \
{ FS_UNMOUNT, "UNMOUNT" }, \
{ FS_Q_OVERFLOW, "Q_OVERFLOW" }, \
{ FS_ERROR, "ERROR" }, \
{ FS_OPEN_PERM, "OPEN_PERM" }, \
{ FS_ACCESS_PERM, "ACCESS_PERM" }, \
{ FS_OPEN_EXEC_PERM, "OPEN_EXEC_PERM" }, \
{ FS_PRE_ACCESS, "PRE_ACCESS" }, \
{ FS_MNT_ATTACH, "MNT_ATTACH" }, \
{ FS_MNT_DETACH, "MNT_DETACH" }, \
{ FS_EVENT_ON_CHILD, "EVENT_ON_CHILD" }, \
{ FS_RENAME, "RENAME" }, \
{ FS_DN_MULTISHOT, "DN_MULTISHOT" }, \
{ FS_ISDIR, "ISDIR" })

View File

@ -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
@ -388,7 +395,16 @@ struct file_attr {
#define FS_DAX_FL 0x02000000 /* Inode is DAX */
#define FS_INLINE_DATA_FL 0x10000000 /* Reserved for ext4 */
#define FS_PROJINHERIT_FL 0x20000000 /* Create with parents projid */
#define FS_CASEFOLD_FL 0x40000000 /* Folder is case insensitive */
/*
* FS_CASEFOLD_FL indicates case-insensitive name lookup. The
* bit is most often reported on directories, where it controls
* lookups of entries within. Filesystems that derive
* case-insensitivity from mount or volume state may also report
* it on non-directory inodes; userspace must not assume the bit
* is directory-only. FS_XFLAG_CASEFOLD reports the same
* information read-only via FS_IOC_FSGETXATTR.
*/
#define FS_CASEFOLD_FL 0x40000000
#define FS_RESERVED_FL 0x80000000 /* reserved for ext2 lib */
#define FS_FL_USER_VISIBLE 0x0003DFFF /* User visible flags */

View File

@ -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