mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 04:18:39 +08:00
fuse update for 6.6
-----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQSQHSd0lITzzeNWNm3h3BK/laaZPAUCZPYlzAAKCRDh3BK/laaZ PEcxAP4suFAlonGntKJ5ltR+7ZN+WYdiraQ+5c6ISBFc+pFXgQD7B0xhztV4umSF III+pbD6lE5gP5u7+Kw/pOnTI42yTQ8= =aPjn -----END PGP SIGNATURE----- Merge tag 'fuse-update-6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse Pull fuse updates from Miklos Szeredi: - Revert non-waiting FLUSH due to a regression - Fix a lookup counter leak in readdirplus - Add an option to allow shared mmaps in no-cache mode - Add btime support and statx intrastructure to the protocol - Invalidate positive/negative dentry on failed create/delete * tag 'fuse-update-6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse: fuse: conditionally fill kstat in fuse_do_statx() fuse: invalidate dentry on EEXIST creates or ENOENT deletes fuse: cache btime fuse: implement statx fuse: add ATTR_TIMEOUT macro fuse: add STATX request fuse: handle empty request_mask in statx fuse: write back dirty pages before direct write in direct_io_relax mode fuse: add a new fuse init flag to relax restrictions in no cache mode fuse: invalidate page cache pages before direct write fuse: nlookup missing decrement in fuse_direntplus_link Revert "fuse: in fuse_flush only wait if someone wants the return code"
This commit is contained in:
commit
9e310ea5c8
159
fs/fuse/dir.c
159
fs/fuse/dir.c
@ -92,7 +92,7 @@ static void fuse_dentry_settime(struct dentry *dentry, u64 time)
|
||||
/*
|
||||
* Calculate the time in jiffies until a dentry/attributes are valid
|
||||
*/
|
||||
static u64 time_to_jiffies(u64 sec, u32 nsec)
|
||||
u64 fuse_time_to_jiffies(u64 sec, u32 nsec)
|
||||
{
|
||||
if (sec || nsec) {
|
||||
struct timespec64 ts = {
|
||||
@ -112,17 +112,7 @@ static u64 time_to_jiffies(u64 sec, u32 nsec)
|
||||
void fuse_change_entry_timeout(struct dentry *entry, struct fuse_entry_out *o)
|
||||
{
|
||||
fuse_dentry_settime(entry,
|
||||
time_to_jiffies(o->entry_valid, o->entry_valid_nsec));
|
||||
}
|
||||
|
||||
static u64 attr_timeout(struct fuse_attr_out *o)
|
||||
{
|
||||
return time_to_jiffies(o->attr_valid, o->attr_valid_nsec);
|
||||
}
|
||||
|
||||
u64 entry_attr_timeout(struct fuse_entry_out *o)
|
||||
{
|
||||
return time_to_jiffies(o->attr_valid, o->attr_valid_nsec);
|
||||
fuse_time_to_jiffies(o->entry_valid, o->entry_valid_nsec));
|
||||
}
|
||||
|
||||
void fuse_invalidate_attr_mask(struct inode *inode, u32 mask)
|
||||
@ -265,8 +255,8 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
|
||||
goto invalid;
|
||||
|
||||
forget_all_cached_acls(inode);
|
||||
fuse_change_attributes(inode, &outarg.attr,
|
||||
entry_attr_timeout(&outarg),
|
||||
fuse_change_attributes(inode, &outarg.attr, NULL,
|
||||
ATTR_TIMEOUT(&outarg),
|
||||
attr_version);
|
||||
fuse_change_entry_timeout(entry, &outarg);
|
||||
} else if (inode) {
|
||||
@ -360,10 +350,14 @@ int fuse_valid_type(int m)
|
||||
S_ISBLK(m) || S_ISFIFO(m) || S_ISSOCK(m);
|
||||
}
|
||||
|
||||
static bool fuse_valid_size(u64 size)
|
||||
{
|
||||
return size <= LLONG_MAX;
|
||||
}
|
||||
|
||||
bool fuse_invalid_attr(struct fuse_attr *attr)
|
||||
{
|
||||
return !fuse_valid_type(attr->mode) ||
|
||||
attr->size > LLONG_MAX;
|
||||
return !fuse_valid_type(attr->mode) || !fuse_valid_size(attr->size);
|
||||
}
|
||||
|
||||
int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name,
|
||||
@ -399,7 +393,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name
|
||||
goto out_put_forget;
|
||||
|
||||
*inode = fuse_iget(sb, outarg->nodeid, outarg->generation,
|
||||
&outarg->attr, entry_attr_timeout(outarg),
|
||||
&outarg->attr, ATTR_TIMEOUT(outarg),
|
||||
attr_version);
|
||||
err = -ENOMEM;
|
||||
if (!*inode) {
|
||||
@ -686,7 +680,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
|
||||
ff->nodeid = outentry.nodeid;
|
||||
ff->open_flags = outopen.open_flags;
|
||||
inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation,
|
||||
&outentry.attr, entry_attr_timeout(&outentry), 0);
|
||||
&outentry.attr, ATTR_TIMEOUT(&outentry), 0);
|
||||
if (!inode) {
|
||||
flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
|
||||
fuse_sync_release(NULL, ff, flags);
|
||||
@ -755,7 +749,8 @@ static int fuse_atomic_open(struct inode *dir, struct dentry *entry,
|
||||
if (err == -ENOSYS) {
|
||||
fc->no_create = 1;
|
||||
goto mknod;
|
||||
}
|
||||
} else if (err == -EEXIST)
|
||||
fuse_invalidate_entry(entry);
|
||||
out_dput:
|
||||
dput(res);
|
||||
return err;
|
||||
@ -813,7 +808,7 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
|
||||
goto out_put_forget_req;
|
||||
|
||||
inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation,
|
||||
&outarg.attr, entry_attr_timeout(&outarg), 0);
|
||||
&outarg.attr, ATTR_TIMEOUT(&outarg), 0);
|
||||
if (!inode) {
|
||||
fuse_queue_forget(fm->fc, forget, outarg.nodeid, 1);
|
||||
return -ENOMEM;
|
||||
@ -835,6 +830,8 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
|
||||
return 0;
|
||||
|
||||
out_put_forget_req:
|
||||
if (err == -EEXIST)
|
||||
fuse_invalidate_entry(entry);
|
||||
kfree(forget);
|
||||
return err;
|
||||
}
|
||||
@ -986,7 +983,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
|
||||
if (!err) {
|
||||
fuse_dir_changed(dir);
|
||||
fuse_entry_unlinked(entry);
|
||||
} else if (err == -EINTR)
|
||||
} else if (err == -EINTR || err == -ENOENT)
|
||||
fuse_invalidate_entry(entry);
|
||||
return err;
|
||||
}
|
||||
@ -1009,7 +1006,7 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry)
|
||||
if (!err) {
|
||||
fuse_dir_changed(dir);
|
||||
fuse_entry_unlinked(entry);
|
||||
} else if (err == -EINTR)
|
||||
} else if (err == -EINTR || err == -ENOENT)
|
||||
fuse_invalidate_entry(entry);
|
||||
return err;
|
||||
}
|
||||
@ -1050,7 +1047,7 @@ static int fuse_rename_common(struct inode *olddir, struct dentry *oldent,
|
||||
/* newent will end up negative */
|
||||
if (!(flags & RENAME_EXCHANGE) && d_really_is_positive(newent))
|
||||
fuse_entry_unlinked(newent);
|
||||
} else if (err == -EINTR) {
|
||||
} else if (err == -EINTR || err == -ENOENT) {
|
||||
/* If request was interrupted, DEITY only knows if the
|
||||
rename actually took place. If the invalidation
|
||||
fails (e.g. some process has CWD under the renamed
|
||||
@ -1153,6 +1150,87 @@ static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr,
|
||||
stat->blksize = 1 << blkbits;
|
||||
}
|
||||
|
||||
static void fuse_statx_to_attr(struct fuse_statx *sx, struct fuse_attr *attr)
|
||||
{
|
||||
memset(attr, 0, sizeof(*attr));
|
||||
attr->ino = sx->ino;
|
||||
attr->size = sx->size;
|
||||
attr->blocks = sx->blocks;
|
||||
attr->atime = sx->atime.tv_sec;
|
||||
attr->mtime = sx->mtime.tv_sec;
|
||||
attr->ctime = sx->ctime.tv_sec;
|
||||
attr->atimensec = sx->atime.tv_nsec;
|
||||
attr->mtimensec = sx->mtime.tv_nsec;
|
||||
attr->ctimensec = sx->ctime.tv_nsec;
|
||||
attr->mode = sx->mode;
|
||||
attr->nlink = sx->nlink;
|
||||
attr->uid = sx->uid;
|
||||
attr->gid = sx->gid;
|
||||
attr->rdev = new_encode_dev(MKDEV(sx->rdev_major, sx->rdev_minor));
|
||||
attr->blksize = sx->blksize;
|
||||
}
|
||||
|
||||
static int fuse_do_statx(struct inode *inode, struct file *file,
|
||||
struct kstat *stat)
|
||||
{
|
||||
int err;
|
||||
struct fuse_attr attr;
|
||||
struct fuse_statx *sx;
|
||||
struct fuse_statx_in inarg;
|
||||
struct fuse_statx_out outarg;
|
||||
struct fuse_mount *fm = get_fuse_mount(inode);
|
||||
u64 attr_version = fuse_get_attr_version(fm->fc);
|
||||
FUSE_ARGS(args);
|
||||
|
||||
memset(&inarg, 0, sizeof(inarg));
|
||||
memset(&outarg, 0, sizeof(outarg));
|
||||
/* Directories have separate file-handle space */
|
||||
if (file && S_ISREG(inode->i_mode)) {
|
||||
struct fuse_file *ff = file->private_data;
|
||||
|
||||
inarg.getattr_flags |= FUSE_GETATTR_FH;
|
||||
inarg.fh = ff->fh;
|
||||
}
|
||||
/* For now leave sync hints as the default, request all stats. */
|
||||
inarg.sx_flags = 0;
|
||||
inarg.sx_mask = STATX_BASIC_STATS | STATX_BTIME;
|
||||
args.opcode = FUSE_STATX;
|
||||
args.nodeid = get_node_id(inode);
|
||||
args.in_numargs = 1;
|
||||
args.in_args[0].size = sizeof(inarg);
|
||||
args.in_args[0].value = &inarg;
|
||||
args.out_numargs = 1;
|
||||
args.out_args[0].size = sizeof(outarg);
|
||||
args.out_args[0].value = &outarg;
|
||||
err = fuse_simple_request(fm, &args);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
sx = &outarg.stat;
|
||||
if (((sx->mask & STATX_SIZE) && !fuse_valid_size(sx->size)) ||
|
||||
((sx->mask & STATX_TYPE) && (!fuse_valid_type(sx->mode) ||
|
||||
inode_wrong_type(inode, sx->mode)))) {
|
||||
make_bad_inode(inode);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
fuse_statx_to_attr(&outarg.stat, &attr);
|
||||
if ((sx->mask & STATX_BASIC_STATS) == STATX_BASIC_STATS) {
|
||||
fuse_change_attributes(inode, &attr, &outarg.stat,
|
||||
ATTR_TIMEOUT(&outarg), attr_version);
|
||||
}
|
||||
|
||||
if (stat) {
|
||||
stat->result_mask = sx->mask & (STATX_BASIC_STATS | STATX_BTIME);
|
||||
stat->btime.tv_sec = sx->btime.tv_sec;
|
||||
stat->btime.tv_nsec = min_t(u32, sx->btime.tv_nsec, NSEC_PER_SEC - 1);
|
||||
fuse_fillattr(inode, &attr, stat);
|
||||
stat->result_mask |= STATX_TYPE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
|
||||
struct file *file)
|
||||
{
|
||||
@ -1189,8 +1267,8 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
|
||||
fuse_make_bad(inode);
|
||||
err = -EIO;
|
||||
} else {
|
||||
fuse_change_attributes(inode, &outarg.attr,
|
||||
attr_timeout(&outarg),
|
||||
fuse_change_attributes(inode, &outarg.attr, NULL,
|
||||
ATTR_TIMEOUT(&outarg),
|
||||
attr_version);
|
||||
if (stat)
|
||||
fuse_fillattr(inode, &outarg.attr, stat);
|
||||
@ -1204,12 +1282,22 @@ static int fuse_update_get_attr(struct inode *inode, struct file *file,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||
int err = 0;
|
||||
bool sync;
|
||||
u32 inval_mask = READ_ONCE(fi->inval_mask);
|
||||
u32 cache_mask = fuse_get_cache_mask(inode);
|
||||
|
||||
if (flags & AT_STATX_FORCE_SYNC)
|
||||
|
||||
/* FUSE only supports basic stats and possibly btime */
|
||||
request_mask &= STATX_BASIC_STATS | STATX_BTIME;
|
||||
retry:
|
||||
if (fc->no_statx)
|
||||
request_mask &= STATX_BASIC_STATS;
|
||||
|
||||
if (!request_mask)
|
||||
sync = false;
|
||||
else if (flags & AT_STATX_FORCE_SYNC)
|
||||
sync = true;
|
||||
else if (flags & AT_STATX_DONT_SYNC)
|
||||
sync = false;
|
||||
@ -1220,11 +1308,24 @@ static int fuse_update_get_attr(struct inode *inode, struct file *file,
|
||||
|
||||
if (sync) {
|
||||
forget_all_cached_acls(inode);
|
||||
err = fuse_do_getattr(inode, stat, file);
|
||||
/* Try statx if BTIME is requested */
|
||||
if (!fc->no_statx && (request_mask & ~STATX_BASIC_STATS)) {
|
||||
err = fuse_do_statx(inode, file, stat);
|
||||
if (err == -ENOSYS) {
|
||||
fc->no_statx = 1;
|
||||
goto retry;
|
||||
}
|
||||
} else {
|
||||
err = fuse_do_getattr(inode, stat, file);
|
||||
}
|
||||
} else if (stat) {
|
||||
generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat);
|
||||
stat->mode = fi->orig_i_mode;
|
||||
stat->ino = fi->orig_ino;
|
||||
if (test_bit(FUSE_I_BTIME, &fi->state)) {
|
||||
stat->btime = fi->i_btime;
|
||||
stat->result_mask |= STATX_BTIME;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
@ -1861,8 +1962,8 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
|
||||
/* FIXME: clear I_DIRTY_SYNC? */
|
||||
}
|
||||
|
||||
fuse_change_attributes_common(inode, &outarg.attr,
|
||||
attr_timeout(&outarg),
|
||||
fuse_change_attributes_common(inode, &outarg.attr, NULL,
|
||||
ATTR_TIMEOUT(&outarg),
|
||||
fuse_get_cache_mask(inode));
|
||||
oldsize = inode->i_size;
|
||||
/* see the comment in fuse_change_attributes() */
|
||||
|
115
fs/fuse/file.c
115
fs/fuse/file.c
@ -19,7 +19,6 @@
|
||||
#include <linux/uio.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/filelock.h>
|
||||
#include <linux/file.h>
|
||||
|
||||
static int fuse_send_open(struct fuse_mount *fm, u64 nodeid,
|
||||
unsigned int open_flags, int opcode,
|
||||
@ -479,36 +478,48 @@ static void fuse_sync_writes(struct inode *inode)
|
||||
fuse_release_nowrite(inode);
|
||||
}
|
||||
|
||||
struct fuse_flush_args {
|
||||
struct fuse_args args;
|
||||
struct fuse_flush_in inarg;
|
||||
struct work_struct work;
|
||||
struct file *file;
|
||||
};
|
||||
|
||||
static int fuse_do_flush(struct fuse_flush_args *fa)
|
||||
static int fuse_flush(struct file *file, fl_owner_t id)
|
||||
{
|
||||
int err;
|
||||
struct inode *inode = file_inode(fa->file);
|
||||
struct inode *inode = file_inode(file);
|
||||
struct fuse_mount *fm = get_fuse_mount(inode);
|
||||
struct fuse_file *ff = file->private_data;
|
||||
struct fuse_flush_in inarg;
|
||||
FUSE_ARGS(args);
|
||||
int err;
|
||||
|
||||
if (fuse_is_bad(inode))
|
||||
return -EIO;
|
||||
|
||||
if (ff->open_flags & FOPEN_NOFLUSH && !fm->fc->writeback_cache)
|
||||
return 0;
|
||||
|
||||
err = write_inode_now(inode, 1);
|
||||
if (err)
|
||||
goto out;
|
||||
return err;
|
||||
|
||||
inode_lock(inode);
|
||||
fuse_sync_writes(inode);
|
||||
inode_unlock(inode);
|
||||
|
||||
err = filemap_check_errors(fa->file->f_mapping);
|
||||
err = filemap_check_errors(file->f_mapping);
|
||||
if (err)
|
||||
goto out;
|
||||
return err;
|
||||
|
||||
err = 0;
|
||||
if (fm->fc->no_flush)
|
||||
goto inval_attr_out;
|
||||
|
||||
err = fuse_simple_request(fm, &fa->args);
|
||||
memset(&inarg, 0, sizeof(inarg));
|
||||
inarg.fh = ff->fh;
|
||||
inarg.lock_owner = fuse_lock_owner_id(fm->fc, id);
|
||||
args.opcode = FUSE_FLUSH;
|
||||
args.nodeid = get_node_id(inode);
|
||||
args.in_numargs = 1;
|
||||
args.in_args[0].size = sizeof(inarg);
|
||||
args.in_args[0].value = &inarg;
|
||||
args.force = true;
|
||||
|
||||
err = fuse_simple_request(fm, &args);
|
||||
if (err == -ENOSYS) {
|
||||
fm->fc->no_flush = 1;
|
||||
err = 0;
|
||||
@ -521,57 +532,9 @@ inval_attr_out:
|
||||
*/
|
||||
if (!err && fm->fc->writeback_cache)
|
||||
fuse_invalidate_attr_mask(inode, STATX_BLOCKS);
|
||||
|
||||
out:
|
||||
fput(fa->file);
|
||||
kfree(fa);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void fuse_flush_async(struct work_struct *work)
|
||||
{
|
||||
struct fuse_flush_args *fa = container_of(work, typeof(*fa), work);
|
||||
|
||||
fuse_do_flush(fa);
|
||||
}
|
||||
|
||||
static int fuse_flush(struct file *file, fl_owner_t id)
|
||||
{
|
||||
struct fuse_flush_args *fa;
|
||||
struct inode *inode = file_inode(file);
|
||||
struct fuse_mount *fm = get_fuse_mount(inode);
|
||||
struct fuse_file *ff = file->private_data;
|
||||
|
||||
if (fuse_is_bad(inode))
|
||||
return -EIO;
|
||||
|
||||
if (ff->open_flags & FOPEN_NOFLUSH && !fm->fc->writeback_cache)
|
||||
return 0;
|
||||
|
||||
fa = kzalloc(sizeof(*fa), GFP_KERNEL);
|
||||
if (!fa)
|
||||
return -ENOMEM;
|
||||
|
||||
fa->inarg.fh = ff->fh;
|
||||
fa->inarg.lock_owner = fuse_lock_owner_id(fm->fc, id);
|
||||
fa->args.opcode = FUSE_FLUSH;
|
||||
fa->args.nodeid = get_node_id(inode);
|
||||
fa->args.in_numargs = 1;
|
||||
fa->args.in_args[0].size = sizeof(fa->inarg);
|
||||
fa->args.in_args[0].value = &fa->inarg;
|
||||
fa->args.force = true;
|
||||
fa->file = get_file(file);
|
||||
|
||||
/* Don't wait if the task is exiting */
|
||||
if (current->flags & PF_EXITING) {
|
||||
INIT_WORK(&fa->work, fuse_flush_async);
|
||||
schedule_work(&fa->work);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fuse_do_flush(fa);
|
||||
}
|
||||
|
||||
int fuse_fsync_common(struct file *file, loff_t start, loff_t end,
|
||||
int datasync, int opcode)
|
||||
{
|
||||
@ -1465,7 +1428,8 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter,
|
||||
int write = flags & FUSE_DIO_WRITE;
|
||||
int cuse = flags & FUSE_DIO_CUSE;
|
||||
struct file *file = io->iocb->ki_filp;
|
||||
struct inode *inode = file->f_mapping->host;
|
||||
struct address_space *mapping = file->f_mapping;
|
||||
struct inode *inode = mapping->host;
|
||||
struct fuse_file *ff = file->private_data;
|
||||
struct fuse_conn *fc = ff->fm->fc;
|
||||
size_t nmax = write ? fc->max_write : fc->max_read;
|
||||
@ -1477,12 +1441,20 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter,
|
||||
int err = 0;
|
||||
struct fuse_io_args *ia;
|
||||
unsigned int max_pages;
|
||||
bool fopen_direct_io = ff->open_flags & FOPEN_DIRECT_IO;
|
||||
|
||||
max_pages = iov_iter_npages(iter, fc->max_pages);
|
||||
ia = fuse_io_alloc(io, max_pages);
|
||||
if (!ia)
|
||||
return -ENOMEM;
|
||||
|
||||
if (fopen_direct_io && fc->direct_io_relax) {
|
||||
res = filemap_write_and_wait_range(mapping, pos, pos + count - 1);
|
||||
if (res) {
|
||||
fuse_io_free(ia);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
if (!cuse && fuse_range_is_writeback(inode, idx_from, idx_to)) {
|
||||
if (!write)
|
||||
inode_lock(inode);
|
||||
@ -1491,6 +1463,14 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter,
|
||||
inode_unlock(inode);
|
||||
}
|
||||
|
||||
if (fopen_direct_io && write) {
|
||||
res = invalidate_inode_pages2_range(mapping, idx_from, idx_to);
|
||||
if (res) {
|
||||
fuse_io_free(ia);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
io->should_dirty = !write && user_backed_iter(iter);
|
||||
while (count) {
|
||||
ssize_t nres;
|
||||
@ -2478,14 +2458,17 @@ static const struct vm_operations_struct fuse_file_vm_ops = {
|
||||
static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
struct fuse_file *ff = file->private_data;
|
||||
struct fuse_conn *fc = ff->fm->fc;
|
||||
|
||||
/* DAX mmap is superior to direct_io mmap */
|
||||
if (FUSE_IS_DAX(file_inode(file)))
|
||||
return fuse_dax_mmap(file, vma);
|
||||
|
||||
if (ff->open_flags & FOPEN_DIRECT_IO) {
|
||||
/* Can't provide the coherency needed for MAP_SHARED */
|
||||
if (vma->vm_flags & VM_MAYSHARE)
|
||||
/* Can't provide the coherency needed for MAP_SHARED
|
||||
* if FUSE_DIRECT_IO_RELAX isn't set.
|
||||
*/
|
||||
if ((vma->vm_flags & VM_MAYSHARE) && !fc->direct_io_relax)
|
||||
return -ENODEV;
|
||||
|
||||
invalidate_inode_pages2(file->f_mapping);
|
||||
|
@ -88,6 +88,9 @@ struct fuse_inode {
|
||||
preserve the original mode */
|
||||
umode_t orig_i_mode;
|
||||
|
||||
/* Cache birthtime */
|
||||
struct timespec64 i_btime;
|
||||
|
||||
/** 64 bit inode number */
|
||||
u64 orig_ino;
|
||||
|
||||
@ -167,6 +170,8 @@ enum {
|
||||
FUSE_I_SIZE_UNSTABLE,
|
||||
/* Bad inode */
|
||||
FUSE_I_BAD,
|
||||
/* Has btime */
|
||||
FUSE_I_BTIME,
|
||||
};
|
||||
|
||||
struct fuse_conn;
|
||||
@ -792,6 +797,12 @@ struct fuse_conn {
|
||||
/* Is tmpfile not implemented by fs? */
|
||||
unsigned int no_tmpfile:1;
|
||||
|
||||
/* relax restrictions in FOPEN_DIRECT_IO mode */
|
||||
unsigned int direct_io_relax:1;
|
||||
|
||||
/* Is statx not implemented by fs? */
|
||||
unsigned int no_statx:1;
|
||||
|
||||
/** The number of requests waiting for completion */
|
||||
atomic_t num_waiting;
|
||||
|
||||
@ -1058,9 +1069,11 @@ void fuse_init_symlink(struct inode *inode);
|
||||
* Change attributes of an inode
|
||||
*/
|
||||
void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
|
||||
struct fuse_statx *sx,
|
||||
u64 attr_valid, u64 attr_version);
|
||||
|
||||
void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
|
||||
struct fuse_statx *sx,
|
||||
u64 attr_valid, u32 cache_mask);
|
||||
|
||||
u32 fuse_get_cache_mask(struct inode *inode);
|
||||
@ -1111,7 +1124,10 @@ void fuse_invalidate_entry_cache(struct dentry *entry);
|
||||
|
||||
void fuse_invalidate_atime(struct inode *inode);
|
||||
|
||||
u64 entry_attr_timeout(struct fuse_entry_out *o);
|
||||
u64 fuse_time_to_jiffies(u64 sec, u32 nsec);
|
||||
#define ATTR_TIMEOUT(o) \
|
||||
fuse_time_to_jiffies((o)->attr_valid, (o)->attr_valid_nsec)
|
||||
|
||||
void fuse_change_entry_timeout(struct dentry *entry, struct fuse_entry_out *o);
|
||||
|
||||
/**
|
||||
|
@ -77,7 +77,7 @@ static struct inode *fuse_alloc_inode(struct super_block *sb)
|
||||
return NULL;
|
||||
|
||||
fi->i_time = 0;
|
||||
fi->inval_mask = 0;
|
||||
fi->inval_mask = ~0;
|
||||
fi->nodeid = 0;
|
||||
fi->nlookup = 0;
|
||||
fi->attr_version = 0;
|
||||
@ -163,6 +163,7 @@ static ino_t fuse_squash_ino(u64 ino64)
|
||||
}
|
||||
|
||||
void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
|
||||
struct fuse_statx *sx,
|
||||
u64 attr_valid, u32 cache_mask)
|
||||
{
|
||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||
@ -172,7 +173,8 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
|
||||
|
||||
fi->attr_version = atomic64_inc_return(&fc->attr_version);
|
||||
fi->i_time = attr_valid;
|
||||
WRITE_ONCE(fi->inval_mask, 0);
|
||||
/* Clear basic stats from invalid mask */
|
||||
set_mask_bits(&fi->inval_mask, STATX_BASIC_STATS, 0);
|
||||
|
||||
inode->i_ino = fuse_squash_ino(attr->ino);
|
||||
inode->i_mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777);
|
||||
@ -196,6 +198,25 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
|
||||
if (!(cache_mask & STATX_CTIME)) {
|
||||
inode_set_ctime(inode, attr->ctime, attr->ctimensec);
|
||||
}
|
||||
if (sx) {
|
||||
/* Sanitize nsecs */
|
||||
sx->btime.tv_nsec =
|
||||
min_t(u32, sx->btime.tv_nsec, NSEC_PER_SEC - 1);
|
||||
|
||||
/*
|
||||
* Btime has been queried, cache is valid (whether or not btime
|
||||
* is available or not) so clear STATX_BTIME from inval_mask.
|
||||
*
|
||||
* Availability of the btime attribute is indicated in
|
||||
* FUSE_I_BTIME
|
||||
*/
|
||||
set_mask_bits(&fi->inval_mask, STATX_BTIME, 0);
|
||||
if (sx->mask & STATX_BTIME) {
|
||||
set_bit(FUSE_I_BTIME, &fi->state);
|
||||
fi->i_btime.tv_sec = sx->btime.tv_sec;
|
||||
fi->i_btime.tv_nsec = sx->btime.tv_nsec;
|
||||
}
|
||||
}
|
||||
|
||||
if (attr->blksize != 0)
|
||||
inode->i_blkbits = ilog2(attr->blksize);
|
||||
@ -235,6 +256,7 @@ u32 fuse_get_cache_mask(struct inode *inode)
|
||||
}
|
||||
|
||||
void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
|
||||
struct fuse_statx *sx,
|
||||
u64 attr_valid, u64 attr_version)
|
||||
{
|
||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||
@ -269,7 +291,7 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
|
||||
}
|
||||
|
||||
old_mtime = inode->i_mtime;
|
||||
fuse_change_attributes_common(inode, attr, attr_valid, cache_mask);
|
||||
fuse_change_attributes_common(inode, attr, sx, attr_valid, cache_mask);
|
||||
|
||||
oldsize = inode->i_size;
|
||||
/*
|
||||
@ -406,7 +428,7 @@ done:
|
||||
spin_lock(&fi->lock);
|
||||
fi->nlookup++;
|
||||
spin_unlock(&fi->lock);
|
||||
fuse_change_attributes(inode, attr, attr_valid, attr_version);
|
||||
fuse_change_attributes(inode, attr, NULL, attr_valid, attr_version);
|
||||
|
||||
return inode;
|
||||
}
|
||||
@ -1210,6 +1232,8 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
|
||||
fc->init_security = 1;
|
||||
if (flags & FUSE_CREATE_SUPP_GROUP)
|
||||
fc->create_supp_group = 1;
|
||||
if (flags & FUSE_DIRECT_IO_RELAX)
|
||||
fc->direct_io_relax = 1;
|
||||
} else {
|
||||
ra_pages = fc->max_read / PAGE_SIZE;
|
||||
fc->no_lock = 1;
|
||||
@ -1256,7 +1280,7 @@ void fuse_send_init(struct fuse_mount *fm)
|
||||
FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA |
|
||||
FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT | FUSE_INIT_EXT |
|
||||
FUSE_SECURITY_CTX | FUSE_CREATE_SUPP_GROUP |
|
||||
FUSE_HAS_EXPIRE_ONLY;
|
||||
FUSE_HAS_EXPIRE_ONLY | FUSE_DIRECT_IO_RELAX;
|
||||
#ifdef CONFIG_FUSE_DAX
|
||||
if (fm->fc->dax)
|
||||
flags |= FUSE_MAP_ALIGNMENT;
|
||||
|
@ -223,8 +223,8 @@ retry:
|
||||
spin_unlock(&fi->lock);
|
||||
|
||||
forget_all_cached_acls(inode);
|
||||
fuse_change_attributes(inode, &o->attr,
|
||||
entry_attr_timeout(o),
|
||||
fuse_change_attributes(inode, &o->attr, NULL,
|
||||
ATTR_TIMEOUT(o),
|
||||
attr_version);
|
||||
/*
|
||||
* The other branch comes via fuse_iget()
|
||||
@ -232,7 +232,7 @@ retry:
|
||||
*/
|
||||
} else {
|
||||
inode = fuse_iget(dir->i_sb, o->nodeid, o->generation,
|
||||
&o->attr, entry_attr_timeout(o),
|
||||
&o->attr, ATTR_TIMEOUT(o),
|
||||
attr_version);
|
||||
if (!inode)
|
||||
inode = ERR_PTR(-ENOMEM);
|
||||
@ -243,8 +243,16 @@ retry:
|
||||
dput(dentry);
|
||||
dentry = alias;
|
||||
}
|
||||
if (IS_ERR(dentry))
|
||||
if (IS_ERR(dentry)) {
|
||||
if (!IS_ERR(inode)) {
|
||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||
|
||||
spin_lock(&fi->lock);
|
||||
fi->nlookup--;
|
||||
spin_unlock(&fi->lock);
|
||||
}
|
||||
return PTR_ERR(dentry);
|
||||
}
|
||||
}
|
||||
if (fc->readdirplus_auto)
|
||||
set_bit(FUSE_I_INIT_RDPLUS, &get_fuse_inode(inode)->state);
|
||||
|
@ -207,6 +207,10 @@
|
||||
* - add FUSE_EXT_GROUPS
|
||||
* - add FUSE_CREATE_SUPP_GROUP
|
||||
* - add FUSE_HAS_EXPIRE_ONLY
|
||||
*
|
||||
* 7.39
|
||||
* - add FUSE_DIRECT_IO_RELAX
|
||||
* - add FUSE_STATX and related structures
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_FUSE_H
|
||||
@ -242,7 +246,7 @@
|
||||
#define FUSE_KERNEL_VERSION 7
|
||||
|
||||
/** Minor version number of this interface */
|
||||
#define FUSE_KERNEL_MINOR_VERSION 38
|
||||
#define FUSE_KERNEL_MINOR_VERSION 39
|
||||
|
||||
/** The node ID of the root inode */
|
||||
#define FUSE_ROOT_ID 1
|
||||
@ -269,6 +273,40 @@ struct fuse_attr {
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
/*
|
||||
* The following structures are bit-for-bit compatible with the statx(2) ABI in
|
||||
* Linux.
|
||||
*/
|
||||
struct fuse_sx_time {
|
||||
int64_t tv_sec;
|
||||
uint32_t tv_nsec;
|
||||
int32_t __reserved;
|
||||
};
|
||||
|
||||
struct fuse_statx {
|
||||
uint32_t mask;
|
||||
uint32_t blksize;
|
||||
uint64_t attributes;
|
||||
uint32_t nlink;
|
||||
uint32_t uid;
|
||||
uint32_t gid;
|
||||
uint16_t mode;
|
||||
uint16_t __spare0[1];
|
||||
uint64_t ino;
|
||||
uint64_t size;
|
||||
uint64_t blocks;
|
||||
uint64_t attributes_mask;
|
||||
struct fuse_sx_time atime;
|
||||
struct fuse_sx_time btime;
|
||||
struct fuse_sx_time ctime;
|
||||
struct fuse_sx_time mtime;
|
||||
uint32_t rdev_major;
|
||||
uint32_t rdev_minor;
|
||||
uint32_t dev_major;
|
||||
uint32_t dev_minor;
|
||||
uint64_t __spare2[14];
|
||||
};
|
||||
|
||||
struct fuse_kstatfs {
|
||||
uint64_t blocks;
|
||||
uint64_t bfree;
|
||||
@ -371,6 +409,8 @@ struct fuse_file_lock {
|
||||
* FUSE_CREATE_SUPP_GROUP: add supplementary group info to create, mkdir,
|
||||
* symlink and mknod (single group that matches parent)
|
||||
* FUSE_HAS_EXPIRE_ONLY: kernel supports expiry-only entry invalidation
|
||||
* FUSE_DIRECT_IO_RELAX: relax restrictions in FOPEN_DIRECT_IO mode, for now
|
||||
* allow shared mmap
|
||||
*/
|
||||
#define FUSE_ASYNC_READ (1 << 0)
|
||||
#define FUSE_POSIX_LOCKS (1 << 1)
|
||||
@ -409,6 +449,7 @@ struct fuse_file_lock {
|
||||
#define FUSE_HAS_INODE_DAX (1ULL << 33)
|
||||
#define FUSE_CREATE_SUPP_GROUP (1ULL << 34)
|
||||
#define FUSE_HAS_EXPIRE_ONLY (1ULL << 35)
|
||||
#define FUSE_DIRECT_IO_RELAX (1ULL << 36)
|
||||
|
||||
/**
|
||||
* CUSE INIT request/reply flags
|
||||
@ -575,6 +616,7 @@ enum fuse_opcode {
|
||||
FUSE_REMOVEMAPPING = 49,
|
||||
FUSE_SYNCFS = 50,
|
||||
FUSE_TMPFILE = 51,
|
||||
FUSE_STATX = 52,
|
||||
|
||||
/* CUSE specific operations */
|
||||
CUSE_INIT = 4096,
|
||||
@ -639,6 +681,22 @@ struct fuse_attr_out {
|
||||
struct fuse_attr attr;
|
||||
};
|
||||
|
||||
struct fuse_statx_in {
|
||||
uint32_t getattr_flags;
|
||||
uint32_t reserved;
|
||||
uint64_t fh;
|
||||
uint32_t sx_flags;
|
||||
uint32_t sx_mask;
|
||||
};
|
||||
|
||||
struct fuse_statx_out {
|
||||
uint64_t attr_valid; /* Cache timeout for the attributes */
|
||||
uint32_t attr_valid_nsec;
|
||||
uint32_t flags;
|
||||
uint64_t spare[2];
|
||||
struct fuse_statx stat;
|
||||
};
|
||||
|
||||
#define FUSE_COMPAT_MKNOD_IN_SIZE 8
|
||||
|
||||
struct fuse_mknod_in {
|
||||
|
Loading…
Reference in New Issue
Block a user