mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
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:
commit
1cd8fad24a
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
118
fs/locks.c
118
fs/locks.c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
31
fs/namei.c
31
fs/namei.c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 &&
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
88
include/linux/exportfs_block.h
Normal file
88
include/linux/exportfs_block.h
Normal 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 */
|
||||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
||||
|
|
|
|||
51
include/trace/events/fsnotify.h
Normal file
51
include/trace/events/fsnotify.h
Normal 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>
|
||||
35
include/trace/misc/fsnotify.h
Normal file
35
include/trace/misc/fsnotify.h
Normal 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" })
|
||||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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