Commit cdd39b05 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
Loading
Loading
Loading
Loading
+1 −4
Original line number Diff line number Diff line
@@ -732,11 +732,8 @@ static ssize_t fuse_dax_direct_write(struct kiocb *iocb, struct iov_iter *from)
	ssize_t ret;

	ret = fuse_direct_io(&io, from, &iocb->ki_pos, FUSE_DIO_WRITE);
	if (ret < 0)
		return ret;

	fuse_invalidate_attr(inode);
	fuse_write_update_size(inode, iocb->ki_pos);
	fuse_write_update_attr(inode, iocb->ki_pos, ret);
	return ret;
}

+17 −7
Original line number Diff line number Diff line
@@ -756,7 +756,7 @@ static int fuse_copy_do(struct fuse_copy_state *cs, void **val, unsigned *size)
{
	unsigned ncpy = min(*size, cs->len);
	if (val) {
		void *pgaddr = kmap_atomic(cs->pg);
		void *pgaddr = kmap_local_page(cs->pg);
		void *buf = pgaddr + cs->offset;

		if (cs->write)
@@ -764,7 +764,7 @@ static int fuse_copy_do(struct fuse_copy_state *cs, void **val, unsigned *size)
		else
			memcpy(*val, buf, ncpy);

		kunmap_atomic(pgaddr);
		kunmap_local(pgaddr);
		*val += ncpy;
	}
	*size -= ncpy;
@@ -847,6 +847,12 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)

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

	if (!(buf->flags & PIPE_BUF_FLAG_LRU))
@@ -949,10 +955,10 @@ static int fuse_copy_page(struct fuse_copy_state *cs, struct page **pagep,
			}
		}
		if (page) {
			void *mapaddr = kmap_atomic(page);
			void *mapaddr = kmap_local_page(page);
			void *buf = mapaddr + offset;
			offset += fuse_copy_do(cs, &buf, &count);
			kunmap_atomic(mapaddr);
			kunmap_local(mapaddr);
		} else
			offset += fuse_copy_do(cs, NULL, &count);
	}
@@ -1591,7 +1597,7 @@ static int fuse_notify_store(struct fuse_conn *fc, unsigned int size,
	end = outarg.offset + outarg.size;
	if (end > file_size) {
		file_size = end;
		fuse_write_update_size(inode, file_size);
		fuse_write_update_attr(inode, file_size, outarg.size);
	}

	num = outarg.size;
@@ -2031,8 +2037,12 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,

	pipe_lock(pipe);
out_free:
	for (idx = 0; idx < nbuf; idx++)
		pipe_buf_release(pipe, &bufs[idx]);
	for (idx = 0; idx < nbuf; idx++) {
		struct pipe_buffer *buf = &bufs[idx];

		if (buf->ops)
			pipe_buf_release(pipe, buf);
	}
	pipe_unlock(pipe);

	kvfree(bufs);
+60 −68
Original line number Diff line number Diff line
@@ -116,7 +116,7 @@ u64 entry_attr_timeout(struct fuse_entry_out *o)
	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);
}
@@ -738,12 +738,49 @@ static int fuse_symlink(struct user_namespace *mnt_userns, struct inode *dir,
	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)) {
		inode->i_ctime = current_time(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)
@@ -762,24 +799,8 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
	args.in_args[0].value = entry->d_name.name;
	err = fuse_simple_request(fm, &args);
	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_invalidate_entry_cache(entry);
		fuse_update_ctime(inode);
		fuse_entry_unlinked(entry);
	} else if (err == -EINTR)
		fuse_invalidate_entry(entry);
	return err;
@@ -801,9 +822,8 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry)
	args.in_args[0].value = entry->d_name.name;
	err = fuse_simple_request(fm, &args);
	if (!err) {
		clear_nlink(d_inode(entry));
		fuse_dir_changed(dir);
		fuse_invalidate_entry_cache(entry);
		fuse_entry_unlinked(entry);
	} else if (err == -EINTR)
		fuse_invalidate_entry(entry);
	return err;
@@ -833,24 +853,18 @@ static int fuse_rename_common(struct inode *olddir, struct dentry *oldent,
	err = fuse_simple_request(fm, &args);
	if (!err) {
		/* ctime changes */
		fuse_invalidate_attr(d_inode(oldent));
		fuse_update_ctime(d_inode(oldent));

		if (flags & RENAME_EXCHANGE) {
			fuse_invalidate_attr(d_inode(newent));
		if (flags & RENAME_EXCHANGE)
			fuse_update_ctime(d_inode(newent));
		}

		fuse_dir_changed(olddir);
		if (olddir != newdir)
			fuse_dir_changed(newdir);

		/* newent will end up negative */
		if (!(flags & RENAME_EXCHANGE) && d_really_is_positive(newent)) {
			fuse_invalidate_attr(d_inode(newent));
			fuse_invalidate_entry_cache(newent);
			fuse_update_ctime(d_inode(newent));
		}
		if (!(flags & RENAME_EXCHANGE) && d_really_is_positive(newent))
			fuse_entry_unlinked(newent);
	} else if (err == -EINTR) {
		/* If request was interrupted, DEITY only knows if the
		   rename actually took place.  If the invalidation
@@ -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].value = newent->d_name.name;
	err = create_new_entry(fm, &args, newdir, newent, inode->i_mode);
	/* Contrary to "normal" filesystems it can happen that link
	   makes two "logical" inodes point to the same "physical"
	   inode.  We invalidate the attributes of the old one, so it
	   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) {
	if (!err)
		fuse_update_ctime_in_cache(inode);
	else if (err == -EINTR)
		fuse_invalidate_attr(inode);
	}

	return err;
}

@@ -944,15 +944,6 @@ static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr,
	unsigned int blkbits;
	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->ino = attr->ino;
	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,
	struct fuse_inode *fi = get_fuse_inode(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)
		sync = true;
	else if (flags & AT_STATX_DONT_SYNC)
		sync = false;
	else if (request_mask & READ_ONCE(fi->inval_mask))
	else if (request_mask & inval_mask & ~cache_mask)
		sync = true;
	else
		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,
	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,
				    STATX_BASIC_STATS & ~STATX_ATIME, 0);
	return fuse_update_get_attr(inode, file, NULL, mask, 0);
}

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)
		return -ENOENT;

	inode_lock(parent);
	inode_lock_nested(parent, I_MUTEX_PARENT);
	if (!S_ISDIR(parent->i_mode))
		goto unlock;

@@ -1561,10 +1552,10 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
	struct fuse_setattr_in inarg;
	struct fuse_attr_out outarg;
	bool is_truncate = false;
	bool is_wb = fc->writeback_cache;
	bool is_wb = fc->writeback_cache && S_ISREG(inode->i_mode);
	loff_t oldsize;
	int err;
	bool trust_local_cmtime = is_wb && S_ISREG(inode->i_mode);
	bool trust_local_cmtime = is_wb;
	bool fault_blocked = false;

	if (!fc->default_permissions)
@@ -1608,7 +1599,7 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
	}

	/* Flush dirty data/metadata before non-truncate SETATTR */
	if (is_wb && S_ISREG(inode->i_mode) &&
	if (is_wb &&
	    attr->ia_valid &
			(ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_MTIME_SET |
			 ATTR_TIMES_SET)) {
@@ -1676,10 +1667,11 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
	}

	fuse_change_attributes_common(inode, &outarg.attr,
				      attr_timeout(&outarg));
				      attr_timeout(&outarg),
				      fuse_get_cache_mask(inode));
	oldsize = inode->i_size;
	/* 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);

	if (is_truncate) {
+56 −50
Original line number Diff line number Diff line
@@ -211,9 +211,8 @@ void fuse_finish_open(struct inode *inode, struct file *file)
		i_size_write(inode, 0);
		spin_unlock(&fi->lock);
		truncate_pagecache(inode, 0);
		fuse_invalidate_attr(inode);
		if (fc->writeback_cache)
		file_update_time(file);
		fuse_invalidate_attr_mask(inode, FUSE_STATX_MODSIZE);
	} else if (!(ff->open_flags & FOPEN_KEEP_CACHE)) {
		invalidate_inode_pages2(inode->i_mapping);
	}
@@ -339,12 +338,6 @@ static int fuse_open(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);

	/* return value is ignored by VFS */
@@ -483,6 +476,9 @@ static int fuse_flush(struct file *file, fl_owner_t id)
	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)
		return err;
@@ -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.
	 */
	if (!err && fm->fc->writeback_cache)
		fuse_invalidate_attr(inode);
		fuse_invalidate_attr_mask(inode, STATX_BLOCKS);
	return err;
}

@@ -793,7 +789,7 @@ static void fuse_read_update_size(struct inode *inode, loff_t size,
	struct fuse_inode *fi = get_fuse_inode(inode);

	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)) {
		fi->attr_version = atomic64_inc_return(&fc->attr_version);
		i_size_write(inode, size);
@@ -1003,7 +999,7 @@ static ssize_t fuse_cache_read_iter(struct kiocb *iocb, struct iov_iter *to)
	if (fc->auto_inval_data ||
	    (iocb->ki_pos + iov_iter_count(to) > i_size_read(inode))) {
		int err;
		err = fuse_update_attributes(inode, iocb->ki_filp);
		err = fuse_update_attributes(inode, iocb->ki_filp, STATX_SIZE);
		if (err)
			return err;
	}
@@ -1072,7 +1068,7 @@ static ssize_t fuse_send_write(struct fuse_io_args *ia, loff_t pos,
	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_inode *fi = get_fuse_inode(inode);
@@ -1080,12 +1076,14 @@ bool fuse_write_update_size(struct inode *inode, loff_t pos)

	spin_lock(&fi->lock);
	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);
		ret = true;
	}
	spin_unlock(&fi->lock);

	fuse_invalidate_attr_mask(inode, FUSE_STATX_MODSIZE);

	return ret;
}

@@ -1268,11 +1266,8 @@ static ssize_t fuse_perform_write(struct kiocb *iocb,
		kfree(ap->pages);
	} while (!err && iov_iter_count(ii));

	if (res > 0)
		fuse_write_update_size(inode, pos);

	fuse_write_update_attr(inode, pos, res);
	clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
	fuse_invalidate_attr(inode);

	return res > 0 ? res : err;
}
@@ -1290,7 +1285,8 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)

	if (fc->writeback_cache) {
		/* 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)
			return err;

@@ -1451,7 +1447,6 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter,
	if (!ia)
		return -ENOMEM;

	ia->io = io;
	if (!cuse && fuse_range_is_writeback(inode, idx_from, idx_to)) {
		if (!write)
			inode_lock(inode);
@@ -1561,11 +1556,9 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from)
		} else {
			res = fuse_direct_io(&io, from, &iocb->ki_pos,
					     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);

	return res;
@@ -1776,7 +1769,7 @@ static void fuse_writepage_end(struct fuse_mount *fm, struct fuse_args *args,
	 * is enabled, we trust local ctime/mtime.
	 */
	if (!fc->writeback_cache)
		fuse_invalidate_attr(inode);
		fuse_invalidate_attr_mask(inode, FUSE_STATX_MODIFY);
	spin_lock(&fi->lock);
	rb_erase(&wpa->writepages_entry, &fi->writepages);
	while (wpa->next) {
@@ -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)
{
	struct fuse_file *ff = NULL;
	struct fuse_file *ff;

	spin_lock(&fi->lock);
	if (!list_empty(&fi->write_files)) {
		ff = list_entry(fi->write_files.next, struct fuse_file,
	ff = list_first_entry_or_null(&fi->write_files, struct fuse_file,
				      write_entry);
	if (ff)
		fuse_file_get(ff);
	}
	spin_unlock(&fi->lock);

	return ff;
@@ -1848,6 +1840,17 @@ int fuse_write_inode(struct inode *inode, struct writeback_control *wbc)
	struct fuse_file *ff;
	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);
	err = fuse_flush_times(inode, ff);
	if (ff)
@@ -2306,15 +2309,18 @@ static int fuse_write_end(struct file *file, struct address_space *mapping,
	if (!copied)
		goto unlock;

	pos += copied;
	if (!PageUptodate(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)
			zero_user_segment(page, endoff, PAGE_SIZE);
		SetPageUptodate(page);
	}

	fuse_write_update_size(inode, pos + copied);
	if (pos > inode->i_size)
		i_size_write(inode, pos);

	set_page_dirty(page);

unlock:
@@ -2340,12 +2346,15 @@ static int fuse_launder_page(struct page *page)
}

/*
 * Write back dirty pages now, because there may not be any suitable
 * open files later
 * Write back dirty data/metadata now (there may not be any suitable
 * open files later for data)
 */
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)
	return vfs_setpos(file, outarg.offset, inode->i_sb->s_maxbytes);

fallback:
	err = fuse_update_attributes(inode, file);
	err = fuse_update_attributes(inode, file, STATX_SIZE);
	if (!err)
		return generic_file_llseek(file, offset, whence);
	else
@@ -2648,7 +2657,7 @@ static loff_t fuse_file_llseek(struct file *file, loff_t offset, int whence)
		break;
	case SEEK_END:
		inode_lock(inode);
		retval = fuse_update_attributes(inode, file);
		retval = fuse_update_attributes(inode, file, STATX_SIZE);
		if (!retval)
			retval = generic_file_llseek(file, offset, whence);
		inode_unlock(inode);
@@ -2869,7 +2878,7 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter)

	if (iov_iter_rw(iter) == WRITE) {
		ret = fuse_direct_io(io, iter, &pos, FUSE_DIO_WRITE);
		fuse_invalidate_attr(inode);
		fuse_invalidate_attr_mask(inode, FUSE_STATX_MODSIZE);
	} else {
		ret = __fuse_direct_read(io, iter, &pos);
	}
@@ -2891,9 +2900,8 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
	kref_put(&io->refcnt, fuse_io_release);

	if (iov_iter_rw(iter) == WRITE) {
		if (ret > 0)
			fuse_write_update_size(inode, pos);
		else if (ret < 0 && offset + count > i_size)
		fuse_write_update_attr(inode, pos, ret);
		if (ret < 0 && offset + count > i_size)
			fuse_do_truncate(file);
	}

@@ -2981,16 +2989,14 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,

	/* we could have extended the file */
	if (!(mode & FALLOC_FL_KEEP_SIZE)) {
		bool changed = fuse_write_update_size(inode, offset + length);

		if (changed && fm->fc->writeback_cache)
		if (fuse_write_update_attr(inode, offset + length, length))
			file_update_time(file);
	}

	if (mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE))
		truncate_pagecache_range(inode, offset, offset + length - 1);

	fuse_invalidate_attr(inode);
	fuse_invalidate_attr_mask(inode, FUSE_STATX_MODSIZE);

out:
	if (!(mode & FALLOC_FL_KEEP_SIZE))
@@ -3002,6 +3008,8 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
	if (lock_inode)
		inode_unlock(inode);

	fuse_flush_time_update(inode);

	return err;
}

@@ -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(pos_out + outarg.size, PAGE_SIZE) - 1);

	if (fc->writeback_cache) {
		fuse_write_update_size(inode_out, pos_out + outarg.size);
	file_update_time(file_out);
	}

	fuse_invalidate_attr(inode_out);
	fuse_write_update_attr(inode_out, pos_out + outarg.size, outarg.size);

	err = outarg.size;
out:
@@ -3111,6 +3115,8 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in,
	inode_unlock(inode_out);
	file_accessed(file_in);

	fuse_flush_time_update(inode_out);

	return err;
}

+14 −3
Original line number Diff line number Diff line
@@ -1031,7 +1031,9 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
			    u64 attr_valid, u64 attr_version);

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
@@ -1065,7 +1067,15 @@ void fuse_wait_aborted(struct fuse_conn *fc);
/**
 * 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_mask(struct inode *inode, u32 mask);

void fuse_invalidate_entry_cache(struct dentry *entry);

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

void fuse_flush_time_update(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);

@@ -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);
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_write_inode(struct inode *inode, struct writeback_control *wbc);
Loading