Commit cdd39b05 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'fuse-update-5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse

Pull fuse updates from Miklos Szeredi:

 - Fix a possible of deadlock in case inode writeback is in progress
   during dentry reclaim

 - Fix a crash in case of page stealing

 - Selectively invalidate cached attributes, possibly improving
   performance

 - Allow filesystems to disable data flushing from ->flush()

 - Misc fixes and cleanups

* tag 'fuse-update-5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse: (23 commits)
  fuse: fix page stealing
  virtiofs: use strscpy for copying the queue name
  fuse: add FOPEN_NOFLUSH
  fuse: only update necessary attributes
  fuse: take cache_mask into account in getattr
  fuse: add cache_mask
  fuse: move reverting attributes to fuse_change_attributes()
  fuse: simplify local variables holding writeback cache state
  fuse: cleanup code conditional on fc->writeback_cache
  fuse: fix attr version comparison in fuse_read_update_size()
  fuse: always invalidate attributes after writes
  fuse: rename fuse_write_update_size()
  fuse: don't bump attr_version in cached write
  fuse: selective attribute invalidation
  fuse: don't increment nlink in link()
  fuse: decrement nlink on overwriting rename
  fuse: simplify __fuse_write_file_get()
  fuse: move fuse_invalidate_attr() into fuse_update_ctime()
  fuse: delete redundant code
  fuse: use kmap_local_page()
  ...
parents a0c7d4a0 712a9510
...@@ -732,11 +732,8 @@ static ssize_t fuse_dax_direct_write(struct kiocb *iocb, struct iov_iter *from) ...@@ -732,11 +732,8 @@ static ssize_t fuse_dax_direct_write(struct kiocb *iocb, struct iov_iter *from)
ssize_t ret; ssize_t ret;
ret = fuse_direct_io(&io, from, &iocb->ki_pos, FUSE_DIO_WRITE); ret = fuse_direct_io(&io, from, &iocb->ki_pos, FUSE_DIO_WRITE);
if (ret < 0)
return ret;
fuse_invalidate_attr(inode); fuse_write_update_attr(inode, iocb->ki_pos, ret);
fuse_write_update_size(inode, iocb->ki_pos);
return ret; return ret;
} }
......
...@@ -756,7 +756,7 @@ static int fuse_copy_do(struct fuse_copy_state *cs, void **val, unsigned *size) ...@@ -756,7 +756,7 @@ static int fuse_copy_do(struct fuse_copy_state *cs, void **val, unsigned *size)
{ {
unsigned ncpy = min(*size, cs->len); unsigned ncpy = min(*size, cs->len);
if (val) { if (val) {
void *pgaddr = kmap_atomic(cs->pg); void *pgaddr = kmap_local_page(cs->pg);
void *buf = pgaddr + cs->offset; void *buf = pgaddr + cs->offset;
if (cs->write) if (cs->write)
...@@ -764,7 +764,7 @@ static int fuse_copy_do(struct fuse_copy_state *cs, void **val, unsigned *size) ...@@ -764,7 +764,7 @@ static int fuse_copy_do(struct fuse_copy_state *cs, void **val, unsigned *size)
else else
memcpy(*val, buf, ncpy); memcpy(*val, buf, ncpy);
kunmap_atomic(pgaddr); kunmap_local(pgaddr);
*val += ncpy; *val += ncpy;
} }
*size -= ncpy; *size -= ncpy;
...@@ -847,6 +847,12 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep) ...@@ -847,6 +847,12 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)
replace_page_cache_page(oldpage, newpage); replace_page_cache_page(oldpage, newpage);
/*
* Release while we have extra ref on stolen page. Otherwise
* anon_pipe_buf_release() might think the page can be reused.
*/
pipe_buf_release(cs->pipe, buf);
get_page(newpage); get_page(newpage);
if (!(buf->flags & PIPE_BUF_FLAG_LRU)) if (!(buf->flags & PIPE_BUF_FLAG_LRU))
...@@ -949,10 +955,10 @@ static int fuse_copy_page(struct fuse_copy_state *cs, struct page **pagep, ...@@ -949,10 +955,10 @@ static int fuse_copy_page(struct fuse_copy_state *cs, struct page **pagep,
} }
} }
if (page) { if (page) {
void *mapaddr = kmap_atomic(page); void *mapaddr = kmap_local_page(page);
void *buf = mapaddr + offset; void *buf = mapaddr + offset;
offset += fuse_copy_do(cs, &buf, &count); offset += fuse_copy_do(cs, &buf, &count);
kunmap_atomic(mapaddr); kunmap_local(mapaddr);
} else } else
offset += fuse_copy_do(cs, NULL, &count); offset += fuse_copy_do(cs, NULL, &count);
} }
...@@ -1591,7 +1597,7 @@ static int fuse_notify_store(struct fuse_conn *fc, unsigned int size, ...@@ -1591,7 +1597,7 @@ static int fuse_notify_store(struct fuse_conn *fc, unsigned int size,
end = outarg.offset + outarg.size; end = outarg.offset + outarg.size;
if (end > file_size) { if (end > file_size) {
file_size = end; file_size = end;
fuse_write_update_size(inode, file_size); fuse_write_update_attr(inode, file_size, outarg.size);
} }
num = outarg.size; num = outarg.size;
...@@ -2031,8 +2037,12 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe, ...@@ -2031,8 +2037,12 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
pipe_lock(pipe); pipe_lock(pipe);
out_free: out_free:
for (idx = 0; idx < nbuf; idx++) for (idx = 0; idx < nbuf; idx++) {
pipe_buf_release(pipe, &bufs[idx]); struct pipe_buffer *buf = &bufs[idx];
if (buf->ops)
pipe_buf_release(pipe, buf);
}
pipe_unlock(pipe); pipe_unlock(pipe);
kvfree(bufs); kvfree(bufs);
......
...@@ -116,7 +116,7 @@ u64 entry_attr_timeout(struct fuse_entry_out *o) ...@@ -116,7 +116,7 @@ u64 entry_attr_timeout(struct fuse_entry_out *o)
return time_to_jiffies(o->attr_valid, o->attr_valid_nsec); return time_to_jiffies(o->attr_valid, o->attr_valid_nsec);
} }
static void fuse_invalidate_attr_mask(struct inode *inode, u32 mask) void fuse_invalidate_attr_mask(struct inode *inode, u32 mask)
{ {
set_mask_bits(&get_fuse_inode(inode)->inval_mask, 0, mask); set_mask_bits(&get_fuse_inode(inode)->inval_mask, 0, mask);
} }
...@@ -738,14 +738,51 @@ static int fuse_symlink(struct user_namespace *mnt_userns, struct inode *dir, ...@@ -738,14 +738,51 @@ static int fuse_symlink(struct user_namespace *mnt_userns, struct inode *dir,
return create_new_entry(fm, &args, dir, entry, S_IFLNK); return create_new_entry(fm, &args, dir, entry, S_IFLNK);
} }
void fuse_update_ctime(struct inode *inode) void fuse_flush_time_update(struct inode *inode)
{
int err = sync_inode_metadata(inode, 1);
mapping_set_error(inode->i_mapping, err);
}
static void fuse_update_ctime_in_cache(struct inode *inode)
{ {
if (!IS_NOCMTIME(inode)) { if (!IS_NOCMTIME(inode)) {
inode->i_ctime = current_time(inode); inode->i_ctime = current_time(inode);
mark_inode_dirty_sync(inode); mark_inode_dirty_sync(inode);
fuse_flush_time_update(inode);
} }
} }
void fuse_update_ctime(struct inode *inode)
{
fuse_invalidate_attr_mask(inode, STATX_CTIME);
fuse_update_ctime_in_cache(inode);
}
static void fuse_entry_unlinked(struct dentry *entry)
{
struct inode *inode = d_inode(entry);
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_inode *fi = get_fuse_inode(inode);
spin_lock(&fi->lock);
fi->attr_version = atomic64_inc_return(&fc->attr_version);
/*
* If i_nlink == 0 then unlink doesn't make sense, yet this can
* happen if userspace filesystem is careless. It would be
* difficult to enforce correct nlink usage so just ignore this
* condition here
*/
if (S_ISDIR(inode->i_mode))
clear_nlink(inode);
else if (inode->i_nlink > 0)
drop_nlink(inode);
spin_unlock(&fi->lock);
fuse_invalidate_entry_cache(entry);
fuse_update_ctime(inode);
}
static int fuse_unlink(struct inode *dir, struct dentry *entry) static int fuse_unlink(struct inode *dir, struct dentry *entry)
{ {
int err; int err;
...@@ -762,24 +799,8 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry) ...@@ -762,24 +799,8 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
args.in_args[0].value = entry->d_name.name; args.in_args[0].value = entry->d_name.name;
err = fuse_simple_request(fm, &args); err = fuse_simple_request(fm, &args);
if (!err) { if (!err) {
struct inode *inode = d_inode(entry);
struct fuse_inode *fi = get_fuse_inode(inode);
spin_lock(&fi->lock);
fi->attr_version = atomic64_inc_return(&fm->fc->attr_version);
/*
* If i_nlink == 0 then unlink doesn't make sense, yet this can
* happen if userspace filesystem is careless. It would be
* difficult to enforce correct nlink usage so just ignore this
* condition here
*/
if (inode->i_nlink > 0)
drop_nlink(inode);
spin_unlock(&fi->lock);
fuse_invalidate_attr(inode);
fuse_dir_changed(dir); fuse_dir_changed(dir);
fuse_invalidate_entry_cache(entry); fuse_entry_unlinked(entry);
fuse_update_ctime(inode);
} else if (err == -EINTR) } else if (err == -EINTR)
fuse_invalidate_entry(entry); fuse_invalidate_entry(entry);
return err; return err;
...@@ -801,9 +822,8 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry) ...@@ -801,9 +822,8 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry)
args.in_args[0].value = entry->d_name.name; args.in_args[0].value = entry->d_name.name;
err = fuse_simple_request(fm, &args); err = fuse_simple_request(fm, &args);
if (!err) { if (!err) {
clear_nlink(d_inode(entry));
fuse_dir_changed(dir); fuse_dir_changed(dir);
fuse_invalidate_entry_cache(entry); fuse_entry_unlinked(entry);
} else if (err == -EINTR) } else if (err == -EINTR)
fuse_invalidate_entry(entry); fuse_invalidate_entry(entry);
return err; return err;
...@@ -833,24 +853,18 @@ static int fuse_rename_common(struct inode *olddir, struct dentry *oldent, ...@@ -833,24 +853,18 @@ static int fuse_rename_common(struct inode *olddir, struct dentry *oldent,
err = fuse_simple_request(fm, &args); err = fuse_simple_request(fm, &args);
if (!err) { if (!err) {
/* ctime changes */ /* ctime changes */
fuse_invalidate_attr(d_inode(oldent));
fuse_update_ctime(d_inode(oldent)); fuse_update_ctime(d_inode(oldent));
if (flags & RENAME_EXCHANGE) { if (flags & RENAME_EXCHANGE)
fuse_invalidate_attr(d_inode(newent));
fuse_update_ctime(d_inode(newent)); fuse_update_ctime(d_inode(newent));
}
fuse_dir_changed(olddir); fuse_dir_changed(olddir);
if (olddir != newdir) if (olddir != newdir)
fuse_dir_changed(newdir); fuse_dir_changed(newdir);
/* 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_invalidate_attr(d_inode(newent)); fuse_entry_unlinked(newent);
fuse_invalidate_entry_cache(newent);
fuse_update_ctime(d_inode(newent));
}
} else if (err == -EINTR) { } else if (err == -EINTR) {
/* 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
...@@ -916,25 +930,11 @@ static int fuse_link(struct dentry *entry, struct inode *newdir, ...@@ -916,25 +930,11 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
args.in_args[1].size = newent->d_name.len + 1; args.in_args[1].size = newent->d_name.len + 1;
args.in_args[1].value = newent->d_name.name; args.in_args[1].value = newent->d_name.name;
err = create_new_entry(fm, &args, newdir, newent, inode->i_mode); err = create_new_entry(fm, &args, newdir, newent, inode->i_mode);
/* Contrary to "normal" filesystems it can happen that link if (!err)
makes two "logical" inodes point to the same "physical" fuse_update_ctime_in_cache(inode);
inode. We invalidate the attributes of the old one, so it else if (err == -EINTR)
will reflect changes in the backing inode (link count,
etc.)
*/
if (!err) {
struct fuse_inode *fi = get_fuse_inode(inode);
spin_lock(&fi->lock);
fi->attr_version = atomic64_inc_return(&fm->fc->attr_version);
if (likely(inode->i_nlink < UINT_MAX))
inc_nlink(inode);
spin_unlock(&fi->lock);
fuse_invalidate_attr(inode);
fuse_update_ctime(inode);
} else if (err == -EINTR) {
fuse_invalidate_attr(inode); fuse_invalidate_attr(inode);
}
return err; return err;
} }
...@@ -944,15 +944,6 @@ static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr, ...@@ -944,15 +944,6 @@ static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr,
unsigned int blkbits; unsigned int blkbits;
struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_conn *fc = get_fuse_conn(inode);
/* see the comment in fuse_change_attributes() */
if (fc->writeback_cache && S_ISREG(inode->i_mode)) {
attr->size = i_size_read(inode);
attr->mtime = inode->i_mtime.tv_sec;
attr->mtimensec = inode->i_mtime.tv_nsec;
attr->ctime = inode->i_ctime.tv_sec;
attr->ctimensec = inode->i_ctime.tv_nsec;
}
stat->dev = inode->i_sb->s_dev; stat->dev = inode->i_sb->s_dev;
stat->ino = attr->ino; stat->ino = attr->ino;
stat->mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777); stat->mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777);
...@@ -1030,12 +1021,14 @@ static int fuse_update_get_attr(struct inode *inode, struct file *file, ...@@ -1030,12 +1021,14 @@ static int fuse_update_get_attr(struct inode *inode, struct file *file,
struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_inode *fi = get_fuse_inode(inode);
int err = 0; int err = 0;
bool sync; bool sync;
u32 inval_mask = READ_ONCE(fi->inval_mask);
u32 cache_mask = fuse_get_cache_mask(inode);
if (flags & AT_STATX_FORCE_SYNC) 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;
else if (request_mask & READ_ONCE(fi->inval_mask)) else if (request_mask & inval_mask & ~cache_mask)
sync = true; sync = true;
else else
sync = time_before64(fi->i_time, get_jiffies_64()); sync = time_before64(fi->i_time, get_jiffies_64());
...@@ -1052,11 +1045,9 @@ static int fuse_update_get_attr(struct inode *inode, struct file *file, ...@@ -1052,11 +1045,9 @@ static int fuse_update_get_attr(struct inode *inode, struct file *file,
return err; return err;
} }
int fuse_update_attributes(struct inode *inode, struct file *file) int fuse_update_attributes(struct inode *inode, struct file *file, u32 mask)
{ {
/* Do *not* need to get atime for internal purposes */ return fuse_update_get_attr(inode, file, NULL, mask, 0);
return fuse_update_get_attr(inode, file, NULL,
STATX_BASIC_STATS & ~STATX_ATIME, 0);
} }
int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
...@@ -1071,7 +1062,7 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, ...@@ -1071,7 +1062,7 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
if (!parent) if (!parent)
return -ENOENT; return -ENOENT;
inode_lock(parent); inode_lock_nested(parent, I_MUTEX_PARENT);
if (!S_ISDIR(parent->i_mode)) if (!S_ISDIR(parent->i_mode))
goto unlock; goto unlock;
...@@ -1561,10 +1552,10 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr, ...@@ -1561,10 +1552,10 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
struct fuse_setattr_in inarg; struct fuse_setattr_in inarg;
struct fuse_attr_out outarg; struct fuse_attr_out outarg;
bool is_truncate = false; bool is_truncate = false;
bool is_wb = fc->writeback_cache; bool is_wb = fc->writeback_cache && S_ISREG(inode->i_mode);
loff_t oldsize; loff_t oldsize;
int err; int err;
bool trust_local_cmtime = is_wb && S_ISREG(inode->i_mode); bool trust_local_cmtime = is_wb;
bool fault_blocked = false; bool fault_blocked = false;
if (!fc->default_permissions) if (!fc->default_permissions)
...@@ -1608,7 +1599,7 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr, ...@@ -1608,7 +1599,7 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
} }
/* Flush dirty data/metadata before non-truncate SETATTR */ /* Flush dirty data/metadata before non-truncate SETATTR */
if (is_wb && S_ISREG(inode->i_mode) && if (is_wb &&
attr->ia_valid & attr->ia_valid &
(ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_MTIME_SET | (ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_MTIME_SET |
ATTR_TIMES_SET)) { ATTR_TIMES_SET)) {
...@@ -1676,10 +1667,11 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr, ...@@ -1676,10 +1667,11 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
} }
fuse_change_attributes_common(inode, &outarg.attr, fuse_change_attributes_common(inode, &outarg.attr,
attr_timeout(&outarg)); attr_timeout(&outarg),
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() */
if (!is_wb || is_truncate || !S_ISREG(inode->i_mode)) if (!is_wb || is_truncate)
i_size_write(inode, outarg.attr.size); i_size_write(inode, outarg.attr.size);
if (is_truncate) { if (is_truncate) {
......
...@@ -211,9 +211,8 @@ void fuse_finish_open(struct inode *inode, struct file *file) ...@@ -211,9 +211,8 @@ void fuse_finish_open(struct inode *inode, struct file *file)
i_size_write(inode, 0); i_size_write(inode, 0);
spin_unlock(&fi->lock); spin_unlock(&fi->lock);
truncate_pagecache(inode, 0); truncate_pagecache(inode, 0);
fuse_invalidate_attr(inode); file_update_time(file);
if (fc->writeback_cache) fuse_invalidate_attr_mask(inode, FUSE_STATX_MODSIZE);
file_update_time(file);
} else if (!(ff->open_flags & FOPEN_KEEP_CACHE)) { } else if (!(ff->open_flags & FOPEN_KEEP_CACHE)) {
invalidate_inode_pages2(inode->i_mapping); invalidate_inode_pages2(inode->i_mapping);
} }
...@@ -339,12 +338,6 @@ static int fuse_open(struct inode *inode, struct file *file) ...@@ -339,12 +338,6 @@ static int fuse_open(struct inode *inode, struct file *file)
static int fuse_release(struct inode *inode, struct file *file) static int fuse_release(struct inode *inode, struct file *file)
{ {
struct fuse_conn *fc = get_fuse_conn(inode);
/* see fuse_vma_close() for !writeback_cache case */
if (fc->writeback_cache)
write_inode_now(inode, 1);
fuse_release_common(file, false); fuse_release_common(file, false);
/* return value is ignored by VFS */ /* return value is ignored by VFS */
...@@ -483,6 +476,9 @@ static int fuse_flush(struct file *file, fl_owner_t id) ...@@ -483,6 +476,9 @@ static int fuse_flush(struct file *file, fl_owner_t id)
if (fuse_is_bad(inode)) if (fuse_is_bad(inode))
return -EIO; 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)
return err; return err;
...@@ -521,7 +517,7 @@ static int fuse_flush(struct file *file, fl_owner_t id) ...@@ -521,7 +517,7 @@ static int fuse_flush(struct file *file, fl_owner_t id)
* enabled, i_blocks from cached attr may not be accurate. * enabled, i_blocks from cached attr may not be accurate.
*/ */
if (!err && fm->fc->writeback_cache) if (!err && fm->fc->writeback_cache)
fuse_invalidate_attr(inode); fuse_invalidate_attr_mask(inode, STATX_BLOCKS);
return err; return err;
} }
...@@ -793,7 +789,7 @@ static void fuse_read_update_size(struct inode *inode, loff_t size, ...@@ -793,7 +789,7 @@ static void fuse_read_update_size(struct inode *inode, loff_t size,
struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_inode *fi = get_fuse_inode(inode);
spin_lock(&fi->lock); spin_lock(&fi->lock);
if (attr_ver == fi->attr_version && size < inode->i_size && if (attr_ver >= fi->attr_version && size < inode->i_size &&
!test_bit(FUSE_I_SIZE_UNSTABLE, &fi->state)) { !test_bit(FUSE_I_SIZE_UNSTABLE, &fi->state)) {
fi->attr_version = atomic64_inc_return(&fc->attr_version); fi->attr_version = atomic64_inc_return(&fc->attr_version);
i_size_write(inode, size); i_size_write(inode, size);
...@@ -1003,7 +999,7 @@ static ssize_t fuse_cache_read_iter(struct kiocb *iocb, struct iov_iter *to) ...@@ -1003,7 +999,7 @@ static ssize_t fuse_cache_read_iter(struct kiocb *iocb, struct iov_iter *to)
if (fc->auto_inval_data || if (fc->auto_inval_data ||
(iocb->ki_pos + iov_iter_count(to) > i_size_read(inode))) { (iocb->ki_pos + iov_iter_count(to) > i_size_read(inode))) {
int err; int err;
err = fuse_update_attributes(inode, iocb->ki_filp); err = fuse_update_attributes(inode, iocb->ki_filp, STATX_SIZE);
if (err) if (err)
return err; return err;
} }
...@@ -1072,7 +1068,7 @@ static ssize_t fuse_send_write(struct fuse_io_args *ia, loff_t pos, ...@@ -1072,7 +1068,7 @@ static ssize_t fuse_send_write(struct fuse_io_args *ia, loff_t pos,
return err ?: ia->write.out.size; return err ?: ia->write.out.size;
} }
bool fuse_write_update_size(struct inode *inode, loff_t pos) bool fuse_write_update_attr(struct inode *inode, loff_t pos, ssize_t written)
{ {
struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_inode *fi = get_fuse_inode(inode);
...@@ -1080,12 +1076,14 @@ bool fuse_write_update_size(struct inode *inode, loff_t pos) ...@@ -1080,12 +1076,14 @@ bool fuse_write_update_size(struct inode *inode, loff_t pos)
spin_lock(&fi->lock); spin_lock(&fi->lock);
fi->attr_version = atomic64_inc_return(&fc->attr_version); fi->attr_version = atomic64_inc_return(&fc->attr_version);
if (pos > inode->i_size) { if (written > 0 && pos > inode->i_size) {
i_size_write(inode, pos); i_size_write(inode, pos);
ret = true; ret = true;
} }
spin_unlock(&fi->lock); spin_unlock(&fi->lock);
fuse_invalidate_attr_mask(inode, FUSE_STATX_MODSIZE);
return ret; return ret;
} }
...@@ -1268,11 +1266,8 @@ static ssize_t fuse_perform_write(struct kiocb *iocb, ...@@ -1268,11 +1266,8 @@ static ssize_t fuse_perform_write(struct kiocb *iocb,
kfree(ap->pages); kfree(ap->pages);
} while (!err && iov_iter_count(ii)); } while (!err && iov_iter_count(ii));
if (res > 0) fuse_write_update_attr(inode, pos, res);
fuse_write_update_size(inode, pos);
clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
fuse_invalidate_attr(inode);
return res > 0 ? res : err; return res > 0 ? res : err;
} }
...@@ -1290,7 +1285,8 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from) ...@@ -1290,7 +1285,8 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
if (fc->writeback_cache) { if (fc->writeback_cache) {
/* Update size (EOF optimization) and mode (SUID clearing) */ /* Update size (EOF optimization) and mode (SUID clearing) */
err = fuse_update_attributes(mapping->host, file); err = fuse_update_attributes(mapping->host, file,
STATX_SIZE | STATX_MODE);
if (err) if (err)
return err; return err;
...@@ -1451,7 +1447,6 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter, ...@@ -1451,7 +1447,6 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter,
if (!ia) if (!ia)
return -ENOMEM; return -ENOMEM;
ia->io = io;
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);
...@@ -1561,11 +1556,9 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from) ...@@ -1561,11 +1556,9 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from)
} else { } else {
res = fuse_direct_io(&io, from, &iocb->ki_pos, res = fuse_direct_io(&io, from, &iocb->ki_pos,
FUSE_DIO_WRITE); FUSE_DIO_WRITE);
fuse_write_update_attr(inode, iocb->ki_pos, res);
} }
} }
fuse_invalidate_attr(inode);
if (res > 0)
fuse_write_update_size(inode, iocb->ki_pos);
inode_unlock(inode); inode_unlock(inode);
return res; return res;
...@@ -1776,7 +1769,7 @@ static void fuse_writepage_end(struct fuse_mount *fm, struct fuse_args *args, ...@@ -1776,7 +1769,7 @@ static void fuse_writepage_end(struct fuse_mount *fm, struct fuse_args *args,
* is enabled, we trust local ctime/mtime. * is enabled, we trust local ctime/mtime.
*/ */
if (!fc->writeback_cache) if (!fc->writeback_cache)
fuse_invalidate_attr(inode); fuse_invalidate_attr_mask(inode, FUSE_STATX_MODIFY);
spin_lock(&fi->lock); spin_lock(&fi->lock);
rb_erase(&wpa->writepages_entry, &fi->writepages); rb_erase(&wpa->writepages_entry, &fi->writepages);
while (wpa->next) { while (wpa->next) {
...@@ -1822,14 +1815,13 @@ static void fuse_writepage_end(struct fuse_mount *fm, struct fuse_args *args, ...@@ -1822,14 +1815,13 @@ static void fuse_writepage_end(struct fuse_mount *fm, struct fuse_args *args,
static struct fuse_file *__fuse_write_file_get(struct fuse_inode *fi) static struct fuse_file *__fuse_write_file_get(struct fuse_inode *fi)
{ {
struct fuse_file *ff = NULL; struct fuse_file *ff;
spin_lock(&fi->lock); spin_lock(&fi->lock);
if (!list_empty(&fi->write_files)) { ff = list_first_entry_or_null(&fi->write_files, struct fuse_file,
ff = list_entry(fi->write_files.next, struct fuse_file, write_entry);
write_entry); if (ff)
fuse_file_get(ff); fuse_file_get(ff);
}
spin_unlock(&fi->lock); spin_unlock(&fi->lock);
return ff; return ff;
...@@ -1848,6 +1840,17 @@ int fuse_write_inode(struct inode *inode, struct writeback_control *wbc) ...@@ -1848,6 +1840,17 @@ int fuse_write_inode(struct inode *inode, struct writeback_control *wbc)
struct fuse_file *ff; struct fuse_file *ff;
int err; int err;
/*
* Inode is always written before the last reference is dropped and
* hence this should not be reached from reclaim.
*
* Writing back the inode from reclaim can deadlock if the request
* processing itself needs an allocation. Allocations triggering
* reclaim while serving a request can't be prevented, because it can
* involve any number of unrelated userspace processes.
*/
WARN_ON(wbc->for_reclaim);
ff = __fuse_write_file_get(fi); ff = __fuse_write_file_get(fi);
err = fuse_flush_times(inode, ff); err = fuse_flush_times(inode, ff);
if (ff) if (ff)
...@@ -2306,15 +2309,18 @@ static int fuse_write_end(struct file *file, struct address_space *mapping, ...@@ -2306,15 +2309,18 @@ static int fuse_write_end(struct file *file, struct address_space *mapping,
if (!copied) if (!copied)
goto unlock; goto unlock;
pos += copied;
if (!PageUptodate(page)) { if (!PageUptodate(page)) {
/* Zero any unwritten bytes at the end of the page */ /* Zero any unwritten bytes at the end of the page */
size_t endoff = (pos + copied) & ~PAGE_MASK; size_t endoff = pos & ~PAGE_MASK;
if (endoff) if (endoff)
zero_user_segment(page, endoff, PAGE_SIZE); zero_user_segment(page, endoff, PAGE_SIZE);
SetPageUptodate(page); SetPageUptodate(page);
} }
fuse_write_update_size(inode, pos + copied); if (pos > inode->i_size)
i_size_write(inode, pos);
set_page_dirty(page); set_page_dirty(page);
unlock: unlock:
...@@ -2340,12 +2346,15 @@ static int fuse_launder_page(struct page *page) ...@@ -2340,12 +2346,15 @@ static int fuse_launder_page(struct page *page)
} }
/* /*
* Write back dirty pages now, because there may not be any suitable * Write back dirty data/metadata now (there may not be any suitable
* open files later * open files later for data)
*/ */
static void fuse_vma_close(struct vm_area_struct *vma) static void fuse_vma_close(struct vm_area_struct *vma)
{ {
filemap_write_and_wait(vma->vm_file->f_mapping); int err;
err = write_inode_now(vma->vm_file->f_mapping->host, 1);
mapping_set_error(vma->vm_file->f_mapping, err);
} }
/* /*
...@@ -2628,7 +2637,7 @@ static loff_t fuse_lseek(struct file *file, loff_t offset, int whence) ...@@ -2628,7 +2637,7 @@ static loff_t fuse_lseek(struct file *file, loff_t offset, int whence)
return vfs_setpos(file, outarg.offset, inode->i_sb->s_maxbytes); return vfs_setpos(file, outarg.offset, inode->i_sb->s_maxbytes);
fallback: fallback:
err = fuse_update_attributes(inode, file); err = fuse_update_attributes(inode, file, STATX_SIZE);
if (!err) if (!err)
return generic_file_llseek(file, offset, whence); return generic_file_llseek(file, offset, whence);
else else
...@@ -2648,7 +2657,7 @@ static loff_t fuse_file_llseek(struct file *file, loff_t offset, int whence) ...@@ -2648,7 +2657,7 @@ static loff_t fuse_file_llseek(struct file *file, loff_t offset, int whence)
break; break;
case SEEK_END: case SEEK_END:
inode_lock(inode); inode_lock(inode);
retval = fuse_update_attributes(inode, file); retval = fuse_update_attributes(inode, file, STATX_SIZE);
if (!retval) if (!retval)
retval = generic_file_llseek(file, offset, whence); retval = generic_file_llseek(file, offset, whence);
inode_unlock(inode); inode_unlock(inode);
...@@ -2869,7 +2878,7 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter) ...@@ -2869,7 +2878,7 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
if (iov_iter_rw(iter) == WRITE) { if (iov_iter_rw(iter) == WRITE) {
ret = fuse_direct_io(io, iter, &pos, FUSE_DIO_WRITE); ret = fuse_direct_io(io, iter, &pos, FUSE_DIO_WRITE);
fuse_invalidate_attr(inode); fuse_invalidate_attr_mask(inode, FUSE_STATX_MODSIZE);
} else { } else {
ret = __fuse_direct_read(io, iter, &pos); ret = __fuse_direct_read(io, iter, &pos);
} }
...@@ -2891,9 +2900,8 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter) ...@@ -2891,9 +2900,8 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
kref_put(&io->refcnt, fuse_io_release); kref_put(&io->refcnt, fuse_io_release);
if (iov_iter_rw(iter) == WRITE) { if (iov_iter_rw(iter) == WRITE) {
if (ret > 0) fuse_write_update_attr(inode, pos, ret);
fuse_write_update_size(inode, pos); if (ret < 0 && offset + count > i_size)
else if (ret < 0 && offset + count > i_size)
fuse_do_truncate(file); fuse_do_truncate(file);
} }
...@@ -2981,16 +2989,14 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, ...@@ -2981,16 +2989,14 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
/* we could have extended the file */ /* we could have extended the file */
if (!(mode & FALLOC_FL_KEEP_SIZE)) { if (!(mode & FALLOC_FL_KEEP_SIZE)) {
bool changed = fuse_write_update_size(inode, offset + length); if (fuse_write_update_attr(inode, offset + length, length))
if (changed && fm->fc->writeback_cache)
file_update_time(file); file_update_time(file);
} }
if (mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE)) if (mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE))
truncate_pagecache_range(inode, offset, offset + length - 1); truncate_pagecache_range(inode, offset, offset + length - 1);
fuse_invalidate_attr(inode); fuse_invalidate_attr_mask(inode, FUSE_STATX_MODSIZE);
out: out:
if (!(mode & FALLOC_FL_KEEP_SIZE)) if (!(mode & FALLOC_FL_KEEP_SIZE))
...@@ -3002,6 +3008,8 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, ...@@ -3002,6 +3008,8 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
if (lock_inode) if (lock_inode)
inode_unlock(inode); inode_unlock(inode);
fuse_flush_time_update(inode);
return err; return err;
} }
...@@ -3096,12 +3104,8 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in, ...@@ -3096,12 +3104,8 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in,
ALIGN_DOWN(pos_out, PAGE_SIZE), ALIGN_DOWN(pos_out, PAGE_SIZE),
ALIGN(pos_out + outarg.size, PAGE_SIZE) - 1); ALIGN(pos_out + outarg.size, PAGE_SIZE) - 1);
if (fc->writeback_cache) { file_update_time(file_out);
fuse_write_update_size(inode_out, pos_out + outarg.size); fuse_write_update_attr(inode_out, pos_out + outarg.size, outarg.size);
file_update_time(file_out);
}
fuse_invalidate_attr(inode_out);
err = outarg.size; err = outarg.size;
out: out:
...@@ -3111,6 +3115,8 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in, ...@@ -3111,6 +3115,8 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in,
inode_unlock(inode_out); inode_unlock(inode_out);
file_accessed(file_in); file_accessed(file_in);
fuse_flush_time_update(inode_out);
return err; return err;
} }
......
...@@ -1031,7 +1031,9 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, ...@@ -1031,7 +1031,9 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
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,
u64 attr_valid); u64 attr_valid, u32 cache_mask);
u32 fuse_get_cache_mask(struct inode *inode);
/** /**
* Initialize the client device * Initialize the client device
...@@ -1065,7 +1067,15 @@ void fuse_wait_aborted(struct fuse_conn *fc); ...@@ -1065,7 +1067,15 @@ void fuse_wait_aborted(struct fuse_conn *fc);
/** /**
* Invalidate inode attributes * Invalidate inode attributes
*/ */
/* Attributes possibly changed on data modification */
#define FUSE_STATX_MODIFY (STATX_MTIME | STATX_CTIME | STATX_BLOCKS)
/* Attributes possibly changed on data and/or size modification */
#define FUSE_STATX_MODSIZE (FUSE_STATX_MODIFY | STATX_SIZE)
void fuse_invalidate_attr(struct inode *inode); void fuse_invalidate_attr(struct inode *inode);
void fuse_invalidate_attr_mask(struct inode *inode, u32 mask);
void fuse_invalidate_entry_cache(struct dentry *entry); void fuse_invalidate_entry_cache(struct dentry *entry);
...@@ -1148,9 +1158,10 @@ int fuse_allow_current_process(struct fuse_conn *fc); ...@@ -1148,9 +1158,10 @@ int fuse_allow_current_process(struct fuse_conn *fc);
u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id); u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id);
void fuse_flush_time_update(struct inode *inode);
void fuse_update_ctime(struct inode *inode); void fuse_update_ctime(struct inode *inode);
int fuse_update_attributes(struct inode *inode, struct file *file); int fuse_update_attributes(struct inode *inode, struct file *file, u32 mask);
void fuse_flush_writepages(struct inode *inode); void fuse_flush_writepages(struct inode *inode);
...@@ -1208,7 +1219,7 @@ long fuse_ioctl_common(struct file *file, unsigned int cmd, ...@@ -1208,7 +1219,7 @@ long fuse_ioctl_common(struct file *file, unsigned int cmd,
__poll_t fuse_file_poll(struct file *file, poll_table *wait); __poll_t fuse_file_poll(struct file *file, poll_table *wait);
int fuse_dev_release(struct inode *inode, struct file *file); int fuse_dev_release(struct inode *inode, struct file *file);
bool fuse_write_update_size(struct inode *inode, loff_t pos); bool fuse_write_update_attr(struct inode *inode, loff_t pos, ssize_t written);
int fuse_flush_times(struct inode *inode, struct fuse_file *ff); int fuse_flush_times(struct inode *inode, struct fuse_file *ff);
int fuse_write_inode(struct inode *inode, struct writeback_control *wbc); int fuse_write_inode(struct inode *inode, struct writeback_control *wbc);
......
...@@ -118,6 +118,9 @@ static void fuse_evict_inode(struct inode *inode) ...@@ -118,6 +118,9 @@ static void fuse_evict_inode(struct inode *inode)
{ {
struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_inode *fi = get_fuse_inode(inode);
/* Will write inode on close/munmap and in all other dirtiers */
WARN_ON(inode->i_state & I_DIRTY_INODE);
truncate_inode_pages_final(&inode->i_data); truncate_inode_pages_final(&inode->i_data);
clear_inode(inode); clear_inode(inode);
if (inode->i_sb->s_flags & SB_ACTIVE) { if (inode->i_sb->s_flags & SB_ACTIVE) {
...@@ -161,7 +164,7 @@ static ino_t fuse_squash_ino(u64 ino64) ...@@ -161,7 +164,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,
u64 attr_valid) u64 attr_valid, u32 cache_mask)
{ {
struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_inode *fi = get_fuse_inode(inode);
...@@ -181,9 +184,11 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, ...@@ -181,9 +184,11 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
inode->i_atime.tv_sec = attr->atime; inode->i_atime.tv_sec = attr->atime;
inode->i_atime.tv_nsec = attr->atimensec; inode->i_atime.tv_nsec = attr->atimensec;
/* mtime from server may be stale due to local buffered write */ /* mtime from server may be stale due to local buffered write */
if (!fc->writeback_cache || !S_ISREG(inode->i_mode)) { if (!(cache_mask & STATX_MTIME)) {
inode->i_mtime.tv_sec = attr->mtime; inode->i_mtime.tv_sec = attr->mtime;
inode->i_mtime.tv_nsec = attr->mtimensec; inode->i_mtime.tv_nsec = attr->mtimensec;
}
if (!(cache_mask & STATX_CTIME)) {
inode->i_ctime.tv_sec = attr->ctime; inode->i_ctime.tv_sec = attr->ctime;
inode->i_ctime.tv_nsec = attr->ctimensec; inode->i_ctime.tv_nsec = attr->ctimensec;
} }
...@@ -215,16 +220,44 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, ...@@ -215,16 +220,44 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
inode->i_flags &= ~S_NOSEC; inode->i_flags &= ~S_NOSEC;
} }
u32 fuse_get_cache_mask(struct inode *inode)
{
struct fuse_conn *fc = get_fuse_conn(inode);
if (!fc->writeback_cache || !S_ISREG(inode->i_mode))
return 0;
return STATX_MTIME | STATX_CTIME | STATX_SIZE;
}
void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
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);
struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_inode *fi = get_fuse_inode(inode);
bool is_wb = fc->writeback_cache; u32 cache_mask;
loff_t oldsize; loff_t oldsize;
struct timespec64 old_mtime; struct timespec64 old_mtime;
spin_lock(&fi->lock); spin_lock(&fi->lock);
/*
* In case of writeback_cache enabled, writes update mtime, ctime and
* may update i_size. In these cases trust the cached value in the
* inode.
*/
cache_mask = fuse_get_cache_mask(inode);
if (cache_mask & STATX_SIZE)
attr->size = i_size_read(inode);
if (cache_mask & STATX_MTIME) {
attr->mtime = inode->i_mtime.tv_sec;
attr->mtimensec = inode->i_mtime.tv_nsec;
}
if (cache_mask & STATX_CTIME) {
attr->ctime = inode->i_ctime.tv_sec;
attr->ctimensec = inode->i_ctime.tv_nsec;
}
if ((attr_version != 0 && fi->attr_version > attr_version) || if ((attr_version != 0 && fi->attr_version > attr_version) ||
test_bit(FUSE_I_SIZE_UNSTABLE, &fi->state)) { test_bit(FUSE_I_SIZE_UNSTABLE, &fi->state)) {
spin_unlock(&fi->lock); spin_unlock(&fi->lock);
...@@ -232,7 +265,7 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, ...@@ -232,7 +265,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); fuse_change_attributes_common(inode, attr, attr_valid, cache_mask);
oldsize = inode->i_size; oldsize = inode->i_size;
/* /*
...@@ -240,11 +273,11 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, ...@@ -240,11 +273,11 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
* extend local i_size without keeping userspace server in sync. So, * extend local i_size without keeping userspace server in sync. So,
* attr->size coming from server can be stale. We cannot trust it. * attr->size coming from server can be stale. We cannot trust it.
*/ */
if (!is_wb || !S_ISREG(inode->i_mode)) if (!(cache_mask & STATX_SIZE))
i_size_write(inode, attr->size); i_size_write(inode, attr->size);
spin_unlock(&fi->lock); spin_unlock(&fi->lock);
if (!is_wb && S_ISREG(inode->i_mode)) { if (!cache_mask && S_ISREG(inode->i_mode)) {
bool inval = false; bool inval = false;
if (oldsize != attr->size) { if (oldsize != attr->size) {
......
...@@ -286,11 +286,11 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, ...@@ -286,11 +286,11 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
in_iovs + out_iovs > FUSE_IOCTL_MAX_IOV) in_iovs + out_iovs > FUSE_IOCTL_MAX_IOV)
goto out; goto out;
vaddr = kmap_atomic(ap.pages[0]); vaddr = kmap_local_page(ap.pages[0]);
err = fuse_copy_ioctl_iovec(fm->fc, iov_page, vaddr, err = fuse_copy_ioctl_iovec(fm->fc, iov_page, vaddr,
transferred, in_iovs + out_iovs, transferred, in_iovs + out_iovs,
(flags & FUSE_IOCTL_COMPAT) != 0); (flags & FUSE_IOCTL_COMPAT) != 0);
kunmap_atomic(vaddr); kunmap_local(vaddr);
if (err) if (err)
goto out; goto out;
......
...@@ -76,11 +76,11 @@ static void fuse_add_dirent_to_cache(struct file *file, ...@@ -76,11 +76,11 @@ static void fuse_add_dirent_to_cache(struct file *file,
WARN_ON(fi->rdc.pos != pos)) WARN_ON(fi->rdc.pos != pos))
goto unlock; goto unlock;
addr = kmap_atomic(page); addr = kmap_local_page(page);
if (!offset) if (!offset)
clear_page(addr); clear_page(addr);
memcpy(addr + offset, dirent, reclen); memcpy(addr + offset, dirent, reclen);
kunmap_atomic(addr); kunmap_local(addr);
fi->rdc.size = (index << PAGE_SHIFT) + offset + reclen; fi->rdc.size = (index << PAGE_SHIFT) + offset + reclen;
fi->rdc.pos = dirent->off; fi->rdc.pos = dirent->off;
unlock: unlock:
...@@ -454,7 +454,7 @@ static int fuse_readdir_cached(struct file *file, struct dir_context *ctx) ...@@ -454,7 +454,7 @@ static int fuse_readdir_cached(struct file *file, struct dir_context *ctx)
* cache; both cases require an up-to-date mtime value. * cache; both cases require an up-to-date mtime value.
*/ */
if (!ctx->pos && fc->auto_inval_data) { if (!ctx->pos && fc->auto_inval_data) {
int err = fuse_update_attributes(inode, file); int err = fuse_update_attributes(inode, file, STATX_MTIME);
if (err) if (err)
return err; return err;
......
...@@ -649,7 +649,7 @@ static void virtio_fs_vq_done(struct virtqueue *vq) ...@@ -649,7 +649,7 @@ static void virtio_fs_vq_done(struct virtqueue *vq)
static void virtio_fs_init_vq(struct virtio_fs_vq *fsvq, char *name, static void virtio_fs_init_vq(struct virtio_fs_vq *fsvq, char *name,
int vq_type) int vq_type)
{ {
strncpy(fsvq->name, name, VQ_NAME_LEN); strscpy(fsvq->name, name, VQ_NAME_LEN);
spin_lock_init(&fsvq->lock); spin_lock_init(&fsvq->lock);
INIT_LIST_HEAD(&fsvq->queued_reqs); INIT_LIST_HEAD(&fsvq->queued_reqs);
INIT_LIST_HEAD(&fsvq->end_reqs); INIT_LIST_HEAD(&fsvq->end_reqs);
......
...@@ -42,10 +42,9 @@ int fuse_setxattr(struct inode *inode, const char *name, const void *value, ...@@ -42,10 +42,9 @@ int fuse_setxattr(struct inode *inode, const char *name, const void *value,
fm->fc->no_setxattr = 1; fm->fc->no_setxattr = 1;
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
} }
if (!err) { if (!err)
fuse_invalidate_attr(inode);
fuse_update_ctime(inode); fuse_update_ctime(inode);
}
return err; return err;
} }
...@@ -173,10 +172,9 @@ int fuse_removexattr(struct inode *inode, const char *name) ...@@ -173,10 +172,9 @@ int fuse_removexattr(struct inode *inode, const char *name)
fm->fc->no_removexattr = 1; fm->fc->no_removexattr = 1;
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
} }
if (!err) { if (!err)
fuse_invalidate_attr(inode);
fuse_update_ctime(inode); fuse_update_ctime(inode);
}
return err; return err;
} }
......
...@@ -184,6 +184,9 @@ ...@@ -184,6 +184,9 @@
* *
* 7.34 * 7.34
* - add FUSE_SYNCFS * - add FUSE_SYNCFS
*
* 7.35
* - add FOPEN_NOFLUSH
*/ */
#ifndef _LINUX_FUSE_H #ifndef _LINUX_FUSE_H
...@@ -219,7 +222,7 @@ ...@@ -219,7 +222,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 34 #define FUSE_KERNEL_MINOR_VERSION 35
/** The node ID of the root inode */ /** The node ID of the root inode */
#define FUSE_ROOT_ID 1 #define FUSE_ROOT_ID 1
...@@ -290,12 +293,14 @@ struct fuse_file_lock { ...@@ -290,12 +293,14 @@ struct fuse_file_lock {
* FOPEN_NONSEEKABLE: the file is not seekable * FOPEN_NONSEEKABLE: the file is not seekable
* FOPEN_CACHE_DIR: allow caching this directory * FOPEN_CACHE_DIR: allow caching this directory
* FOPEN_STREAM: the file is stream-like (no file position at all) * FOPEN_STREAM: the file is stream-like (no file position at all)
* FOPEN_NOFLUSH: don't flush data cache on close (unless FUSE_WRITEBACK_CACHE)
*/ */
#define FOPEN_DIRECT_IO (1 << 0) #define FOPEN_DIRECT_IO (1 << 0)
#define FOPEN_KEEP_CACHE (1 << 1) #define FOPEN_KEEP_CACHE (1 << 1)
#define FOPEN_NONSEEKABLE (1 << 2) #define FOPEN_NONSEEKABLE (1 << 2)
#define FOPEN_CACHE_DIR (1 << 3) #define FOPEN_CACHE_DIR (1 << 3)
#define FOPEN_STREAM (1 << 4) #define FOPEN_STREAM (1 << 4)
#define FOPEN_NOFLUSH (1 << 5)
/** /**
* INIT request/reply flags * INIT request/reply flags
......
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