Commit 9e310ea5 authored by Linus Torvalds's avatar Linus Torvalds

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"
parents 4b3d6e0c f73016b6
...@@ -92,7 +92,7 @@ static void fuse_dentry_settime(struct dentry *dentry, u64 time) ...@@ -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 * 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) { if (sec || nsec) {
struct timespec64 ts = { struct timespec64 ts = {
...@@ -112,17 +112,7 @@ static u64 time_to_jiffies(u64 sec, u32 nsec) ...@@ -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) void fuse_change_entry_timeout(struct dentry *entry, struct fuse_entry_out *o)
{ {
fuse_dentry_settime(entry, fuse_dentry_settime(entry,
time_to_jiffies(o->entry_valid, o->entry_valid_nsec)); fuse_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);
} }
void fuse_invalidate_attr_mask(struct inode *inode, u32 mask) 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) ...@@ -265,8 +255,8 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
goto invalid; goto invalid;
forget_all_cached_acls(inode); forget_all_cached_acls(inode);
fuse_change_attributes(inode, &outarg.attr, fuse_change_attributes(inode, &outarg.attr, NULL,
entry_attr_timeout(&outarg), ATTR_TIMEOUT(&outarg),
attr_version); attr_version);
fuse_change_entry_timeout(entry, &outarg); fuse_change_entry_timeout(entry, &outarg);
} else if (inode) { } else if (inode) {
...@@ -360,10 +350,14 @@ int fuse_valid_type(int m) ...@@ -360,10 +350,14 @@ int fuse_valid_type(int m)
S_ISBLK(m) || S_ISFIFO(m) || S_ISSOCK(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) bool fuse_invalid_attr(struct fuse_attr *attr)
{ {
return !fuse_valid_type(attr->mode) || return !fuse_valid_type(attr->mode) || !fuse_valid_size(attr->size);
attr->size > LLONG_MAX;
} }
int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name, 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 ...@@ -399,7 +393,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name
goto out_put_forget; goto out_put_forget;
*inode = fuse_iget(sb, outarg->nodeid, outarg->generation, *inode = fuse_iget(sb, outarg->nodeid, outarg->generation,
&outarg->attr, entry_attr_timeout(outarg), &outarg->attr, ATTR_TIMEOUT(outarg),
attr_version); attr_version);
err = -ENOMEM; err = -ENOMEM;
if (!*inode) { if (!*inode) {
...@@ -686,7 +680,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, ...@@ -686,7 +680,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
ff->nodeid = outentry.nodeid; ff->nodeid = outentry.nodeid;
ff->open_flags = outopen.open_flags; ff->open_flags = outopen.open_flags;
inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation, 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) { if (!inode) {
flags &= ~(O_CREAT | O_EXCL | O_TRUNC); flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
fuse_sync_release(NULL, ff, flags); fuse_sync_release(NULL, ff, flags);
...@@ -755,7 +749,8 @@ static int fuse_atomic_open(struct inode *dir, struct dentry *entry, ...@@ -755,7 +749,8 @@ static int fuse_atomic_open(struct inode *dir, struct dentry *entry,
if (err == -ENOSYS) { if (err == -ENOSYS) {
fc->no_create = 1; fc->no_create = 1;
goto mknod; goto mknod;
} } else if (err == -EEXIST)
fuse_invalidate_entry(entry);
out_dput: out_dput:
dput(res); dput(res);
return err; return err;
...@@ -813,7 +808,7 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args, ...@@ -813,7 +808,7 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
goto out_put_forget_req; goto out_put_forget_req;
inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, 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) { if (!inode) {
fuse_queue_forget(fm->fc, forget, outarg.nodeid, 1); fuse_queue_forget(fm->fc, forget, outarg.nodeid, 1);
return -ENOMEM; return -ENOMEM;
...@@ -835,6 +830,8 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args, ...@@ -835,6 +830,8 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
return 0; return 0;
out_put_forget_req: out_put_forget_req:
if (err == -EEXIST)
fuse_invalidate_entry(entry);
kfree(forget); kfree(forget);
return err; return err;
} }
...@@ -986,7 +983,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry) ...@@ -986,7 +983,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
if (!err) { if (!err) {
fuse_dir_changed(dir); fuse_dir_changed(dir);
fuse_entry_unlinked(entry); fuse_entry_unlinked(entry);
} else if (err == -EINTR) } else if (err == -EINTR || err == -ENOENT)
fuse_invalidate_entry(entry); fuse_invalidate_entry(entry);
return err; return err;
} }
...@@ -1009,7 +1006,7 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry) ...@@ -1009,7 +1006,7 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry)
if (!err) { if (!err) {
fuse_dir_changed(dir); fuse_dir_changed(dir);
fuse_entry_unlinked(entry); fuse_entry_unlinked(entry);
} else if (err == -EINTR) } else if (err == -EINTR || err == -ENOENT)
fuse_invalidate_entry(entry); fuse_invalidate_entry(entry);
return err; return err;
} }
...@@ -1050,7 +1047,7 @@ static int fuse_rename_common(struct inode *olddir, struct dentry *oldent, ...@@ -1050,7 +1047,7 @@ static int fuse_rename_common(struct inode *olddir, struct dentry *oldent,
/* newent will end up negative */ /* newent will end up negative */
if (!(flags & RENAME_EXCHANGE) && d_really_is_positive(newent)) if (!(flags & RENAME_EXCHANGE) && d_really_is_positive(newent))
fuse_entry_unlinked(newent); fuse_entry_unlinked(newent);
} else if (err == -EINTR) { } else if (err == -EINTR || err == -ENOENT) {
/* If request was interrupted, DEITY only knows if the /* If request was interrupted, DEITY only knows if the
rename actually took place. If the invalidation rename actually took place. If the invalidation
fails (e.g. some process has CWD under the renamed 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, ...@@ -1153,6 +1150,87 @@ static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr,
stat->blksize = 1 << blkbits; 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, static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
struct file *file) struct file *file)
{ {
...@@ -1189,8 +1267,8 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat, ...@@ -1189,8 +1267,8 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
fuse_make_bad(inode); fuse_make_bad(inode);
err = -EIO; err = -EIO;
} else { } else {
fuse_change_attributes(inode, &outarg.attr, fuse_change_attributes(inode, &outarg.attr, NULL,
attr_timeout(&outarg), ATTR_TIMEOUT(&outarg),
attr_version); attr_version);
if (stat) if (stat)
fuse_fillattr(inode, &outarg.attr, stat); fuse_fillattr(inode, &outarg.attr, stat);
...@@ -1204,12 +1282,22 @@ static int fuse_update_get_attr(struct inode *inode, struct file *file, ...@@ -1204,12 +1282,22 @@ static int fuse_update_get_attr(struct inode *inode, struct file *file,
unsigned int flags) unsigned int flags)
{ {
struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_inode *fi = get_fuse_inode(inode);
struct fuse_conn *fc = get_fuse_conn(inode);
int err = 0; int err = 0;
bool sync; bool sync;
u32 inval_mask = READ_ONCE(fi->inval_mask); u32 inval_mask = READ_ONCE(fi->inval_mask);
u32 cache_mask = fuse_get_cache_mask(inode); 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; sync = true;
else if (flags & AT_STATX_DONT_SYNC) else if (flags & AT_STATX_DONT_SYNC)
sync = false; sync = false;
...@@ -1220,11 +1308,24 @@ static int fuse_update_get_attr(struct inode *inode, struct file *file, ...@@ -1220,11 +1308,24 @@ static int fuse_update_get_attr(struct inode *inode, struct file *file,
if (sync) { if (sync) {
forget_all_cached_acls(inode); forget_all_cached_acls(inode);
/* 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); err = fuse_do_getattr(inode, stat, file);
}
} else if (stat) { } else if (stat) {
generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat); generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat);
stat->mode = fi->orig_i_mode; stat->mode = fi->orig_i_mode;
stat->ino = fi->orig_ino; 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; return err;
...@@ -1861,8 +1962,8 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr, ...@@ -1861,8 +1962,8 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
/* FIXME: clear I_DIRTY_SYNC? */ /* FIXME: clear I_DIRTY_SYNC? */
} }
fuse_change_attributes_common(inode, &outarg.attr, fuse_change_attributes_common(inode, &outarg.attr, NULL,
attr_timeout(&outarg), ATTR_TIMEOUT(&outarg),
fuse_get_cache_mask(inode)); fuse_get_cache_mask(inode));
oldsize = inode->i_size; oldsize = inode->i_size;
/* see the comment in fuse_change_attributes() */ /* see the comment in fuse_change_attributes() */
......
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
#include <linux/uio.h> #include <linux/uio.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/filelock.h> #include <linux/filelock.h>
#include <linux/file.h>
static int fuse_send_open(struct fuse_mount *fm, u64 nodeid, static int fuse_send_open(struct fuse_mount *fm, u64 nodeid,
unsigned int open_flags, int opcode, unsigned int open_flags, int opcode,
...@@ -479,36 +478,48 @@ static void fuse_sync_writes(struct inode *inode) ...@@ -479,36 +478,48 @@ static void fuse_sync_writes(struct inode *inode)
fuse_release_nowrite(inode); fuse_release_nowrite(inode);
} }
struct fuse_flush_args { static int fuse_flush(struct file *file, fl_owner_t id)
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)
{ {
int err; struct inode *inode = file_inode(file);
struct inode *inode = file_inode(fa->file);
struct fuse_mount *fm = get_fuse_mount(inode); 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); err = write_inode_now(inode, 1);
if (err) if (err)
goto out; return err;
inode_lock(inode); inode_lock(inode);
fuse_sync_writes(inode); fuse_sync_writes(inode);
inode_unlock(inode); inode_unlock(inode);
err = filemap_check_errors(fa->file->f_mapping); err = filemap_check_errors(file->f_mapping);
if (err) if (err)
goto out; return err;
err = 0; err = 0;
if (fm->fc->no_flush) if (fm->fc->no_flush)
goto inval_attr_out; 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) { if (err == -ENOSYS) {
fm->fc->no_flush = 1; fm->fc->no_flush = 1;
err = 0; err = 0;
...@@ -521,57 +532,9 @@ static int fuse_do_flush(struct fuse_flush_args *fa) ...@@ -521,57 +532,9 @@ static int fuse_do_flush(struct fuse_flush_args *fa)
*/ */
if (!err && fm->fc->writeback_cache) if (!err && fm->fc->writeback_cache)
fuse_invalidate_attr_mask(inode, STATX_BLOCKS); fuse_invalidate_attr_mask(inode, STATX_BLOCKS);
out:
fput(fa->file);
kfree(fa);
return err; 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 fuse_fsync_common(struct file *file, loff_t start, loff_t end,
int datasync, int opcode) int datasync, int opcode)
{ {
...@@ -1465,7 +1428,8 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter, ...@@ -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 write = flags & FUSE_DIO_WRITE;
int cuse = flags & FUSE_DIO_CUSE; int cuse = flags & FUSE_DIO_CUSE;
struct file *file = io->iocb->ki_filp; 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_file *ff = file->private_data;
struct fuse_conn *fc = ff->fm->fc; struct fuse_conn *fc = ff->fm->fc;
size_t nmax = write ? fc->max_write : fc->max_read; 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, ...@@ -1477,12 +1441,20 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter,
int err = 0; int err = 0;
struct fuse_io_args *ia; struct fuse_io_args *ia;
unsigned int max_pages; unsigned int max_pages;
bool fopen_direct_io = ff->open_flags & FOPEN_DIRECT_IO;
max_pages = iov_iter_npages(iter, fc->max_pages); max_pages = iov_iter_npages(iter, fc->max_pages);
ia = fuse_io_alloc(io, max_pages); ia = fuse_io_alloc(io, max_pages);
if (!ia) if (!ia)
return -ENOMEM; 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 (!cuse && fuse_range_is_writeback(inode, idx_from, idx_to)) {
if (!write) if (!write)
inode_lock(inode); inode_lock(inode);
...@@ -1491,6 +1463,14 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter, ...@@ -1491,6 +1463,14 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter,
inode_unlock(inode); 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); io->should_dirty = !write && user_backed_iter(iter);
while (count) { while (count) {
ssize_t nres; ssize_t nres;
...@@ -2478,14 +2458,17 @@ static const struct vm_operations_struct fuse_file_vm_ops = { ...@@ -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) static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma)
{ {
struct fuse_file *ff = file->private_data; struct fuse_file *ff = file->private_data;
struct fuse_conn *fc = ff->fm->fc;
/* DAX mmap is superior to direct_io mmap */ /* DAX mmap is superior to direct_io mmap */
if (FUSE_IS_DAX(file_inode(file))) if (FUSE_IS_DAX(file_inode(file)))
return fuse_dax_mmap(file, vma); return fuse_dax_mmap(file, vma);
if (ff->open_flags & FOPEN_DIRECT_IO) { if (ff->open_flags & FOPEN_DIRECT_IO) {
/* Can't provide the coherency needed for MAP_SHARED */ /* Can't provide the coherency needed for MAP_SHARED
if (vma->vm_flags & VM_MAYSHARE) * if FUSE_DIRECT_IO_RELAX isn't set.
*/
if ((vma->vm_flags & VM_MAYSHARE) && !fc->direct_io_relax)
return -ENODEV; return -ENODEV;
invalidate_inode_pages2(file->f_mapping); invalidate_inode_pages2(file->f_mapping);
......
...@@ -88,6 +88,9 @@ struct fuse_inode { ...@@ -88,6 +88,9 @@ struct fuse_inode {
preserve the original mode */ preserve the original mode */
umode_t orig_i_mode; umode_t orig_i_mode;
/* Cache birthtime */
struct timespec64 i_btime;
/** 64 bit inode number */ /** 64 bit inode number */
u64 orig_ino; u64 orig_ino;
...@@ -167,6 +170,8 @@ enum { ...@@ -167,6 +170,8 @@ enum {
FUSE_I_SIZE_UNSTABLE, FUSE_I_SIZE_UNSTABLE,
/* Bad inode */ /* Bad inode */
FUSE_I_BAD, FUSE_I_BAD,
/* Has btime */
FUSE_I_BTIME,
}; };
struct fuse_conn; struct fuse_conn;
...@@ -792,6 +797,12 @@ struct fuse_conn { ...@@ -792,6 +797,12 @@ struct fuse_conn {
/* Is tmpfile not implemented by fs? */ /* Is tmpfile not implemented by fs? */
unsigned int no_tmpfile:1; 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 */ /** The number of requests waiting for completion */
atomic_t num_waiting; atomic_t num_waiting;
...@@ -1058,9 +1069,11 @@ void fuse_init_symlink(struct inode *inode); ...@@ -1058,9 +1069,11 @@ void fuse_init_symlink(struct inode *inode);
* Change attributes of an inode * Change attributes of an inode
*/ */
void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
struct fuse_statx *sx,
u64 attr_valid, u64 attr_version); u64 attr_valid, u64 attr_version);
void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
struct fuse_statx *sx,
u64 attr_valid, u32 cache_mask); u64 attr_valid, u32 cache_mask);
u32 fuse_get_cache_mask(struct inode *inode); u32 fuse_get_cache_mask(struct inode *inode);
...@@ -1111,7 +1124,10 @@ void fuse_invalidate_entry_cache(struct dentry *entry); ...@@ -1111,7 +1124,10 @@ void fuse_invalidate_entry_cache(struct dentry *entry);
void fuse_invalidate_atime(struct inode *inode); 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); 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) ...@@ -77,7 +77,7 @@ static struct inode *fuse_alloc_inode(struct super_block *sb)
return NULL; return NULL;
fi->i_time = 0; fi->i_time = 0;
fi->inval_mask = 0; fi->inval_mask = ~0;
fi->nodeid = 0; fi->nodeid = 0;
fi->nlookup = 0; fi->nlookup = 0;
fi->attr_version = 0; fi->attr_version = 0;
...@@ -163,6 +163,7 @@ static ino_t fuse_squash_ino(u64 ino64) ...@@ -163,6 +163,7 @@ static ino_t fuse_squash_ino(u64 ino64)
} }
void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
struct fuse_statx *sx,
u64 attr_valid, u32 cache_mask) u64 attr_valid, u32 cache_mask)
{ {
struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_conn *fc = get_fuse_conn(inode);
...@@ -172,7 +173,8 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, ...@@ -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->attr_version = atomic64_inc_return(&fc->attr_version);
fi->i_time = attr_valid; 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_ino = fuse_squash_ino(attr->ino);
inode->i_mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777); 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, ...@@ -196,6 +198,25 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
if (!(cache_mask & STATX_CTIME)) { if (!(cache_mask & STATX_CTIME)) {
inode_set_ctime(inode, attr->ctime, attr->ctimensec); 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) if (attr->blksize != 0)
inode->i_blkbits = ilog2(attr->blksize); inode->i_blkbits = ilog2(attr->blksize);
...@@ -235,6 +256,7 @@ u32 fuse_get_cache_mask(struct inode *inode) ...@@ -235,6 +256,7 @@ u32 fuse_get_cache_mask(struct inode *inode)
} }
void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
struct fuse_statx *sx,
u64 attr_valid, u64 attr_version) u64 attr_valid, u64 attr_version)
{ {
struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_conn *fc = get_fuse_conn(inode);
...@@ -269,7 +291,7 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, ...@@ -269,7 +291,7 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
} }
old_mtime = inode->i_mtime; 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; oldsize = inode->i_size;
/* /*
...@@ -406,7 +428,7 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid, ...@@ -406,7 +428,7 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid,
spin_lock(&fi->lock); spin_lock(&fi->lock);
fi->nlookup++; fi->nlookup++;
spin_unlock(&fi->lock); 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; return inode;
} }
...@@ -1210,6 +1232,8 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args, ...@@ -1210,6 +1232,8 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
fc->init_security = 1; fc->init_security = 1;
if (flags & FUSE_CREATE_SUPP_GROUP) if (flags & FUSE_CREATE_SUPP_GROUP)
fc->create_supp_group = 1; fc->create_supp_group = 1;
if (flags & FUSE_DIRECT_IO_RELAX)
fc->direct_io_relax = 1;
} else { } else {
ra_pages = fc->max_read / PAGE_SIZE; ra_pages = fc->max_read / PAGE_SIZE;
fc->no_lock = 1; fc->no_lock = 1;
...@@ -1256,7 +1280,7 @@ void fuse_send_init(struct fuse_mount *fm) ...@@ -1256,7 +1280,7 @@ void fuse_send_init(struct fuse_mount *fm)
FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA | FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA |
FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT | FUSE_INIT_EXT | FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT | FUSE_INIT_EXT |
FUSE_SECURITY_CTX | FUSE_CREATE_SUPP_GROUP | FUSE_SECURITY_CTX | FUSE_CREATE_SUPP_GROUP |
FUSE_HAS_EXPIRE_ONLY; FUSE_HAS_EXPIRE_ONLY | FUSE_DIRECT_IO_RELAX;
#ifdef CONFIG_FUSE_DAX #ifdef CONFIG_FUSE_DAX
if (fm->fc->dax) if (fm->fc->dax)
flags |= FUSE_MAP_ALIGNMENT; flags |= FUSE_MAP_ALIGNMENT;
......
...@@ -223,8 +223,8 @@ static int fuse_direntplus_link(struct file *file, ...@@ -223,8 +223,8 @@ static int fuse_direntplus_link(struct file *file,
spin_unlock(&fi->lock); spin_unlock(&fi->lock);
forget_all_cached_acls(inode); forget_all_cached_acls(inode);
fuse_change_attributes(inode, &o->attr, fuse_change_attributes(inode, &o->attr, NULL,
entry_attr_timeout(o), ATTR_TIMEOUT(o),
attr_version); attr_version);
/* /*
* The other branch comes via fuse_iget() * The other branch comes via fuse_iget()
...@@ -232,7 +232,7 @@ static int fuse_direntplus_link(struct file *file, ...@@ -232,7 +232,7 @@ static int fuse_direntplus_link(struct file *file,
*/ */
} else { } else {
inode = fuse_iget(dir->i_sb, o->nodeid, o->generation, inode = fuse_iget(dir->i_sb, o->nodeid, o->generation,
&o->attr, entry_attr_timeout(o), &o->attr, ATTR_TIMEOUT(o),
attr_version); attr_version);
if (!inode) if (!inode)
inode = ERR_PTR(-ENOMEM); inode = ERR_PTR(-ENOMEM);
...@@ -243,9 +243,17 @@ static int fuse_direntplus_link(struct file *file, ...@@ -243,9 +243,17 @@ static int fuse_direntplus_link(struct file *file,
dput(dentry); dput(dentry);
dentry = alias; 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); return PTR_ERR(dentry);
} }
}
if (fc->readdirplus_auto) if (fc->readdirplus_auto)
set_bit(FUSE_I_INIT_RDPLUS, &get_fuse_inode(inode)->state); set_bit(FUSE_I_INIT_RDPLUS, &get_fuse_inode(inode)->state);
fuse_change_entry_timeout(dentry, o); fuse_change_entry_timeout(dentry, o);
......
...@@ -207,6 +207,10 @@ ...@@ -207,6 +207,10 @@
* - add FUSE_EXT_GROUPS * - add FUSE_EXT_GROUPS
* - add FUSE_CREATE_SUPP_GROUP * - add FUSE_CREATE_SUPP_GROUP
* - add FUSE_HAS_EXPIRE_ONLY * - add FUSE_HAS_EXPIRE_ONLY
*
* 7.39
* - add FUSE_DIRECT_IO_RELAX
* - add FUSE_STATX and related structures
*/ */
#ifndef _LINUX_FUSE_H #ifndef _LINUX_FUSE_H
...@@ -242,7 +246,7 @@ ...@@ -242,7 +246,7 @@
#define FUSE_KERNEL_VERSION 7 #define FUSE_KERNEL_VERSION 7
/** Minor version number of this interface */ /** 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 */ /** The node ID of the root inode */
#define FUSE_ROOT_ID 1 #define FUSE_ROOT_ID 1
...@@ -269,6 +273,40 @@ struct fuse_attr { ...@@ -269,6 +273,40 @@ struct fuse_attr {
uint32_t flags; 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 { struct fuse_kstatfs {
uint64_t blocks; uint64_t blocks;
uint64_t bfree; uint64_t bfree;
...@@ -371,6 +409,8 @@ struct fuse_file_lock { ...@@ -371,6 +409,8 @@ struct fuse_file_lock {
* FUSE_CREATE_SUPP_GROUP: add supplementary group info to create, mkdir, * FUSE_CREATE_SUPP_GROUP: add supplementary group info to create, mkdir,
* symlink and mknod (single group that matches parent) * symlink and mknod (single group that matches parent)
* FUSE_HAS_EXPIRE_ONLY: kernel supports expiry-only entry invalidation * 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_ASYNC_READ (1 << 0)
#define FUSE_POSIX_LOCKS (1 << 1) #define FUSE_POSIX_LOCKS (1 << 1)
...@@ -409,6 +449,7 @@ struct fuse_file_lock { ...@@ -409,6 +449,7 @@ struct fuse_file_lock {
#define FUSE_HAS_INODE_DAX (1ULL << 33) #define FUSE_HAS_INODE_DAX (1ULL << 33)
#define FUSE_CREATE_SUPP_GROUP (1ULL << 34) #define FUSE_CREATE_SUPP_GROUP (1ULL << 34)
#define FUSE_HAS_EXPIRE_ONLY (1ULL << 35) #define FUSE_HAS_EXPIRE_ONLY (1ULL << 35)
#define FUSE_DIRECT_IO_RELAX (1ULL << 36)
/** /**
* CUSE INIT request/reply flags * CUSE INIT request/reply flags
...@@ -575,6 +616,7 @@ enum fuse_opcode { ...@@ -575,6 +616,7 @@ enum fuse_opcode {
FUSE_REMOVEMAPPING = 49, FUSE_REMOVEMAPPING = 49,
FUSE_SYNCFS = 50, FUSE_SYNCFS = 50,
FUSE_TMPFILE = 51, FUSE_TMPFILE = 51,
FUSE_STATX = 52,
/* CUSE specific operations */ /* CUSE specific operations */
CUSE_INIT = 4096, CUSE_INIT = 4096,
...@@ -639,6 +681,22 @@ struct fuse_attr_out { ...@@ -639,6 +681,22 @@ struct fuse_attr_out {
struct fuse_attr attr; 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 #define FUSE_COMPAT_MKNOD_IN_SIZE 8
struct fuse_mknod_in { struct fuse_mknod_in {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment