Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 256cf4c4 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull fuse fixes from Miklos Szeredi:
 "This adds ctime update in the new cached writeback mode and also
  fixes/simplifies the mtime update handling.  Support for rename flags
  (aka renameat2) is also added to the userspace API"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse:
  fuse: add renameat2 support
  fuse: clear MS_I_VERSION
  fuse: clear FUSE_I_CTIME_DIRTY flag on setattr
  fuse: trust kernel i_ctime only
  fuse: remove .update_time
  fuse: allow ctime flushing to userspace
  fuse: fuse: add time_gran to INIT_OUT
  fuse: add .write_inode
  fuse: clean up fsync
  fuse: fuse: fallocate: use file_update_time()
  fuse: update mtime on open(O_TRUNC) in atomic_o_trunc mode
  fuse: update mtime on truncate(2)
  fuse: do not use uninitialized i_mode
  fuse: fix mtime update error in fsync
  fuse: check fallocate mode
  fuse: add __exit to fuse_ctl_cleanup
parents 9bd29c56 1560c974
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -348,7 +348,7 @@ int __init fuse_ctl_init(void)
	return register_filesystem(&fuse_ctl_fs_type);
}

void fuse_ctl_cleanup(void)
void __exit fuse_ctl_cleanup(void)
{
	unregister_filesystem(&fuse_ctl_fs_type);
}
+100 −46
Original line number Diff line number Diff line
@@ -679,6 +679,14 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry,
	return create_new_entry(fc, req, dir, entry, S_IFLNK);
}

static inline void fuse_update_ctime(struct inode *inode)
{
	if (!IS_NOCMTIME(inode)) {
		inode->i_ctime = current_fs_time(inode->i_sb);
		mark_inode_dirty_sync(inode);
	}
}

static int fuse_unlink(struct inode *dir, struct dentry *entry)
{
	int err;
@@ -713,6 +721,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
		fuse_invalidate_attr(inode);
		fuse_invalidate_attr(dir);
		fuse_invalidate_entry_cache(entry);
		fuse_update_ctime(inode);
	} else if (err == -EINTR)
		fuse_invalidate_entry(entry);
	return err;
@@ -743,23 +752,26 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry)
	return err;
}

static int fuse_rename(struct inode *olddir, struct dentry *oldent,
		       struct inode *newdir, struct dentry *newent)
static int fuse_rename_common(struct inode *olddir, struct dentry *oldent,
			      struct inode *newdir, struct dentry *newent,
			      unsigned int flags, int opcode, size_t argsize)
{
	int err;
	struct fuse_rename_in inarg;
	struct fuse_rename2_in inarg;
	struct fuse_conn *fc = get_fuse_conn(olddir);
	struct fuse_req *req = fuse_get_req_nopages(fc);
	struct fuse_req *req;

	req = fuse_get_req_nopages(fc);
	if (IS_ERR(req))
		return PTR_ERR(req);

	memset(&inarg, 0, sizeof(inarg));
	memset(&inarg, 0, argsize);
	inarg.newdir = get_node_id(newdir);
	req->in.h.opcode = FUSE_RENAME;
	inarg.flags = flags;
	req->in.h.opcode = opcode;
	req->in.h.nodeid = get_node_id(olddir);
	req->in.numargs = 3;
	req->in.args[0].size = sizeof(inarg);
	req->in.args[0].size = argsize;
	req->in.args[0].value = &inarg;
	req->in.args[1].size = oldent->d_name.len + 1;
	req->in.args[1].value = oldent->d_name.name;
@@ -771,15 +783,22 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent,
	if (!err) {
		/* ctime changes */
		fuse_invalidate_attr(oldent->d_inode);
		fuse_update_ctime(oldent->d_inode);

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

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

		/* newent will end up negative */
		if (newent->d_inode) {
		if (!(flags & RENAME_EXCHANGE) && newent->d_inode) {
			fuse_invalidate_attr(newent->d_inode);
			fuse_invalidate_entry_cache(newent);
			fuse_update_ctime(newent->d_inode);
		}
	} else if (err == -EINTR) {
		/* If request was interrupted, DEITY only knows if the
@@ -795,6 +814,36 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent,
	return err;
}

static int fuse_rename(struct inode *olddir, struct dentry *oldent,
		       struct inode *newdir, struct dentry *newent)
{
	return fuse_rename_common(olddir, oldent, newdir, newent, 0,
				  FUSE_RENAME, sizeof(struct fuse_rename_in));
}

static int fuse_rename2(struct inode *olddir, struct dentry *oldent,
			struct inode *newdir, struct dentry *newent,
			unsigned int flags)
{
	struct fuse_conn *fc = get_fuse_conn(olddir);
	int err;

	if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE))
		return -EINVAL;

	if (fc->no_rename2 || fc->minor < 23)
		return -EINVAL;

	err = fuse_rename_common(olddir, oldent, newdir, newent, flags,
				 FUSE_RENAME2, sizeof(struct fuse_rename2_in));
	if (err == -ENOSYS) {
		fc->no_rename2 = 1;
		err = -EINVAL;
	}
	return err;

}

static int fuse_link(struct dentry *entry, struct inode *newdir,
		     struct dentry *newent)
{
@@ -829,6 +878,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
		inc_nlink(inode);
		spin_unlock(&fc->lock);
		fuse_invalidate_attr(inode);
		fuse_update_ctime(inode);
	} else if (err == -EINTR) {
		fuse_invalidate_attr(inode);
	}
@@ -846,6 +896,8 @@ static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr,
		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;
@@ -1504,7 +1556,7 @@ static bool update_mtime(unsigned ivalid, bool trust_local_mtime)
}

static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg,
			   bool trust_local_mtime)
			   bool trust_local_cmtime)
{
	unsigned ivalid = iattr->ia_valid;

@@ -1523,13 +1575,18 @@ static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg,
		if (!(ivalid & ATTR_ATIME_SET))
			arg->valid |= FATTR_ATIME_NOW;
	}
	if ((ivalid & ATTR_MTIME) && update_mtime(ivalid, trust_local_mtime)) {
	if ((ivalid & ATTR_MTIME) && update_mtime(ivalid, trust_local_cmtime)) {
		arg->valid |= FATTR_MTIME;
		arg->mtime = iattr->ia_mtime.tv_sec;
		arg->mtimensec = iattr->ia_mtime.tv_nsec;
		if (!(ivalid & ATTR_MTIME_SET) && !trust_local_mtime)
		if (!(ivalid & ATTR_MTIME_SET) && !trust_local_cmtime)
			arg->valid |= FATTR_MTIME_NOW;
	}
	if ((ivalid & ATTR_CTIME) && trust_local_cmtime) {
		arg->valid |= FATTR_CTIME;
		arg->ctime = iattr->ia_ctime.tv_sec;
		arg->ctimensec = iattr->ia_ctime.tv_nsec;
	}
}

/*
@@ -1597,39 +1654,38 @@ static void fuse_setattr_fill(struct fuse_conn *fc, struct fuse_req *req,
/*
 * Flush inode->i_mtime to the server
 */
int fuse_flush_mtime(struct file *file, bool nofail)
int fuse_flush_times(struct inode *inode, struct fuse_file *ff)
{
	struct inode *inode = file->f_mapping->host;
	struct fuse_inode *fi = get_fuse_inode(inode);
	struct fuse_conn *fc = get_fuse_conn(inode);
	struct fuse_req *req = NULL;
	struct fuse_req *req;
	struct fuse_setattr_in inarg;
	struct fuse_attr_out outarg;
	int err;

	if (nofail) {
		req = fuse_get_req_nofail_nopages(fc, file);
	} else {
	req = fuse_get_req_nopages(fc);
	if (IS_ERR(req))
		return PTR_ERR(req);
	}

	memset(&inarg, 0, sizeof(inarg));
	memset(&outarg, 0, sizeof(outarg));

	inarg.valid |= FATTR_MTIME;
	inarg.valid = FATTR_MTIME;
	inarg.mtime = inode->i_mtime.tv_sec;
	inarg.mtimensec = inode->i_mtime.tv_nsec;

	if (fc->minor >= 23) {
		inarg.valid |= FATTR_CTIME;
		inarg.ctime = inode->i_ctime.tv_sec;
		inarg.ctimensec = inode->i_ctime.tv_nsec;
	}
	if (ff) {
		inarg.valid |= FATTR_FH;
		inarg.fh = ff->fh;
	}
	fuse_setattr_fill(fc, req, inode, &inarg, &outarg);
	fuse_request_send(fc, req);
	err = req->out.h.error;
	fuse_put_request(fc, req);

	if (!err)
		clear_bit(FUSE_I_MTIME_DIRTY, &fi->state);

	return err;
}

@@ -1653,7 +1709,7 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
	bool is_wb = fc->writeback_cache;
	loff_t oldsize;
	int err;
	bool trust_local_mtime = is_wb && S_ISREG(inode->i_mode);
	bool trust_local_cmtime = is_wb && S_ISREG(inode->i_mode);

	if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS))
		attr->ia_valid |= ATTR_FORCE;
@@ -1678,11 +1734,13 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
	if (is_truncate) {
		fuse_set_nowrite(inode);
		set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);
		if (trust_local_cmtime && attr->ia_size != inode->i_size)
			attr->ia_valid |= ATTR_MTIME | ATTR_CTIME;
	}

	memset(&inarg, 0, sizeof(inarg));
	memset(&outarg, 0, sizeof(outarg));
	iattr_to_fattr(attr, &inarg, trust_local_mtime);
	iattr_to_fattr(attr, &inarg, trust_local_cmtime);
	if (file) {
		struct fuse_file *ff = file->private_data;
		inarg.valid |= FATTR_FH;
@@ -1711,9 +1769,12 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,

	spin_lock(&fc->lock);
	/* the kernel maintains i_mtime locally */
	if (trust_local_mtime && (attr->ia_valid & ATTR_MTIME)) {
	if (trust_local_cmtime) {
		if (attr->ia_valid & ATTR_MTIME)
			inode->i_mtime = attr->ia_mtime;
		clear_bit(FUSE_I_MTIME_DIRTY, &fi->state);
		if (attr->ia_valid & ATTR_CTIME)
			inode->i_ctime = attr->ia_ctime;
		/* FIXME: clear I_DIRTY_SYNC? */
	}

	fuse_change_attributes_common(inode, &outarg.attr,
@@ -1810,8 +1871,10 @@ static int fuse_setxattr(struct dentry *entry, const char *name,
		fc->no_setxattr = 1;
		err = -EOPNOTSUPP;
	}
	if (!err)
	if (!err) {
		fuse_invalidate_attr(inode);
		fuse_update_ctime(inode);
	}
	return err;
}

@@ -1941,20 +2004,11 @@ static int fuse_removexattr(struct dentry *entry, const char *name)
		fc->no_removexattr = 1;
		err = -EOPNOTSUPP;
	}
	if (!err)
	if (!err) {
		fuse_invalidate_attr(inode);
	return err;
		fuse_update_ctime(inode);
	}

static int fuse_update_time(struct inode *inode, struct timespec *now,
			    int flags)
{
	if (flags & S_MTIME) {
		inode->i_mtime = *now;
		set_bit(FUSE_I_MTIME_DIRTY, &get_fuse_inode(inode)->state);
		BUG_ON(!S_ISREG(inode->i_mode));
	}
	return 0;
	return err;
}

static const struct inode_operations fuse_dir_inode_operations = {
@@ -1964,6 +2018,7 @@ static const struct inode_operations fuse_dir_inode_operations = {
	.unlink		= fuse_unlink,
	.rmdir		= fuse_rmdir,
	.rename		= fuse_rename,
	.rename2	= fuse_rename2,
	.link		= fuse_link,
	.setattr	= fuse_setattr,
	.create		= fuse_create,
@@ -1996,7 +2051,6 @@ static const struct inode_operations fuse_common_inode_operations = {
	.getxattr	= fuse_getxattr,
	.listxattr	= fuse_listxattr,
	.removexattr	= fuse_removexattr,
	.update_time	= fuse_update_time,
};

static const struct inode_operations fuse_symlink_inode_operations = {
+53 −31
Original line number Diff line number Diff line
@@ -223,6 +223,8 @@ void fuse_finish_open(struct inode *inode, struct file *file)
		i_size_write(inode, 0);
		spin_unlock(&fc->lock);
		fuse_invalidate_attr(inode);
		if (fc->writeback_cache)
			file_update_time(file);
	}
	if ((file->f_mode & FMODE_WRITE) && fc->writeback_cache)
		fuse_link_write_file(file);
@@ -232,18 +234,26 @@ int fuse_open_common(struct inode *inode, struct file *file, bool isdir)
{
	struct fuse_conn *fc = get_fuse_conn(inode);
	int err;
	bool lock_inode = (file->f_flags & O_TRUNC) &&
			  fc->atomic_o_trunc &&
			  fc->writeback_cache;

	err = generic_file_open(inode, file);
	if (err)
		return err;

	if (lock_inode)
		mutex_lock(&inode->i_mutex);

	err = fuse_do_open(fc, get_node_id(inode), file, isdir);
	if (err)
		return err;

	if (!err)
		fuse_finish_open(inode, file);

	return 0;
	if (lock_inode)
		mutex_unlock(&inode->i_mutex);

	return err;
}

static void fuse_prepare_release(struct fuse_file *ff, int flags, int opcode)
@@ -314,10 +324,7 @@ static int fuse_release(struct inode *inode, struct file *file)

	/* see fuse_vma_close() for !writeback_cache case */
	if (fc->writeback_cache)
		filemap_write_and_wait(file->f_mapping);

	if (test_bit(FUSE_I_MTIME_DIRTY, &get_fuse_inode(inode)->state))
		fuse_flush_mtime(file, true);
		write_inode_now(inode, 1);

	fuse_release_common(file, FUSE_RELEASE);

@@ -439,7 +446,7 @@ static int fuse_flush(struct file *file, fl_owner_t id)
	if (fc->no_flush)
		return 0;

	err = filemap_write_and_wait(file->f_mapping);
	err = write_inode_now(inode, 1);
	if (err)
		return err;

@@ -480,13 +487,6 @@ int fuse_fsync_common(struct file *file, loff_t start, loff_t end,
	if (is_bad_inode(inode))
		return -EIO;

	err = filemap_write_and_wait_range(inode->i_mapping, start, end);
	if (err)
		return err;

	if ((!isdir && fc->no_fsync) || (isdir && fc->no_fsyncdir))
		return 0;

	mutex_lock(&inode->i_mutex);

	/*
@@ -494,17 +494,17 @@ int fuse_fsync_common(struct file *file, loff_t start, loff_t end,
	 * wait for all outstanding writes, before sending the FSYNC
	 * request.
	 */
	err = write_inode_now(inode, 0);
	err = filemap_write_and_wait_range(inode->i_mapping, start, end);
	if (err)
		goto out;

	fuse_sync_writes(inode);

	if (test_bit(FUSE_I_MTIME_DIRTY, &get_fuse_inode(inode)->state)) {
		int err = fuse_flush_mtime(file, false);
	err = sync_inode_metadata(inode, 1);
	if (err)
		goto out;
	}

	if ((!isdir && fc->no_fsync) || (isdir && fc->no_fsyncdir))
		goto out;

	req = fuse_get_req_nopages(fc);
	if (IS_ERR(req)) {
@@ -1659,13 +1659,13 @@ static void fuse_writepage_end(struct fuse_conn *fc, struct fuse_req *req)
	fuse_writepage_free(fc, req);
}

static struct fuse_file *fuse_write_file_get(struct fuse_conn *fc,
static struct fuse_file *__fuse_write_file_get(struct fuse_conn *fc,
					       struct fuse_inode *fi)
{
	struct fuse_file *ff = NULL;

	spin_lock(&fc->lock);
	if (!WARN_ON(list_empty(&fi->write_files))) {
	if (!list_empty(&fi->write_files)) {
		ff = list_entry(fi->write_files.next, struct fuse_file,
				write_entry);
		fuse_file_get(ff);
@@ -1675,6 +1675,29 @@ static struct fuse_file *fuse_write_file_get(struct fuse_conn *fc,
	return ff;
}

static struct fuse_file *fuse_write_file_get(struct fuse_conn *fc,
					     struct fuse_inode *fi)
{
	struct fuse_file *ff = __fuse_write_file_get(fc, fi);
	WARN_ON(!ff);
	return ff;
}

int fuse_write_inode(struct inode *inode, struct writeback_control *wbc)
{
	struct fuse_conn *fc = get_fuse_conn(inode);
	struct fuse_inode *fi = get_fuse_inode(inode);
	struct fuse_file *ff;
	int err;

	ff = __fuse_write_file_get(fc, fi);
	err = fuse_flush_times(inode, ff);
	if (ff)
		fuse_file_put(ff, 0);

	return err;
}

static int fuse_writepage_locked(struct page *page)
{
	struct address_space *mapping = page->mapping;
@@ -2972,6 +2995,9 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
	bool lock_inode = !(mode & FALLOC_FL_KEEP_SIZE) ||
			   (mode & FALLOC_FL_PUNCH_HOLE);

	if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
		return -EOPNOTSUPP;

	if (fc->no_fallocate)
		return -EOPNOTSUPP;

@@ -3017,12 +3043,8 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
	if (!(mode & FALLOC_FL_KEEP_SIZE)) {
		bool changed = fuse_write_update_size(inode, offset + length);

		if (changed && fc->writeback_cache) {
			struct fuse_inode *fi = get_fuse_inode(inode);

			inode->i_mtime = current_fs_time(inode->i_sb);
			set_bit(FUSE_I_MTIME_DIRTY, &fi->state);
		}
		if (changed && fc->writeback_cache)
			file_update_time(file);
	}

	if (mode & FALLOC_FL_PUNCH_HOLE)
+6 −4
Original line number Diff line number Diff line
@@ -119,8 +119,6 @@ enum {
	FUSE_I_INIT_RDPLUS,
	/** An operation changing file size is in progress  */
	FUSE_I_SIZE_UNSTABLE,
	/** i_mtime has been updated locally; a flush to userspace needed */
	FUSE_I_MTIME_DIRTY,
};

struct fuse_conn;
@@ -544,6 +542,9 @@ struct fuse_conn {
	/** Is fallocate not implemented by fs? */
	unsigned no_fallocate:1;

	/** Is rename with flags implemented by fs? */
	unsigned no_rename2:1;

	/** Use enhanced/automatic page cache invalidation. */
	unsigned auto_inval_data:1;

@@ -725,7 +726,7 @@ int fuse_dev_init(void);
void fuse_dev_cleanup(void);

int fuse_ctl_init(void);
void fuse_ctl_cleanup(void);
void __exit fuse_ctl_cleanup(void);

/**
 * Allocate a request
@@ -891,7 +892,8 @@ int fuse_dev_release(struct inode *inode, struct file *file);

bool fuse_write_update_size(struct inode *inode, loff_t pos);

int fuse_flush_mtime(struct file *file, bool nofail);
int fuse_flush_times(struct inode *inode, struct fuse_file *ff);
int fuse_write_inode(struct inode *inode, struct writeback_control *wbc);

int fuse_do_setattr(struct inode *inode, struct iattr *attr,
		    struct file *file);
+12 −4
Original line number Diff line number Diff line
@@ -175,9 +175,9 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
	if (!fc->writeback_cache || !S_ISREG(inode->i_mode)) {
		inode->i_mtime.tv_sec   = attr->mtime;
		inode->i_mtime.tv_nsec  = attr->mtimensec;
	}
		inode->i_ctime.tv_sec   = attr->ctime;
		inode->i_ctime.tv_nsec  = attr->ctimensec;
	}

	if (attr->blksize != 0)
		inode->i_blkbits = ilog2(attr->blksize);
@@ -256,6 +256,8 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
	inode->i_size = attr->size;
	inode->i_mtime.tv_sec  = attr->mtime;
	inode->i_mtime.tv_nsec = attr->mtimensec;
	inode->i_ctime.tv_sec  = attr->ctime;
	inode->i_ctime.tv_nsec = attr->ctimensec;
	if (S_ISREG(inode->i_mode)) {
		fuse_init_common(inode);
		fuse_init_file_inode(inode);
@@ -303,7 +305,7 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid,

	if ((inode->i_state & I_NEW)) {
		inode->i_flags |= S_NOATIME;
		if (!fc->writeback_cache || !S_ISREG(inode->i_mode))
		if (!fc->writeback_cache || !S_ISREG(attr->mode))
			inode->i_flags |= S_NOCMTIME;
		inode->i_generation = generation;
		inode->i_data.backing_dev_info = &fc->bdi;
@@ -788,6 +790,7 @@ static const struct super_operations fuse_super_operations = {
	.alloc_inode    = fuse_alloc_inode,
	.destroy_inode  = fuse_destroy_inode,
	.evict_inode	= fuse_evict_inode,
	.write_inode	= fuse_write_inode,
	.drop_inode	= generic_delete_inode,
	.remount_fs	= fuse_remount_fs,
	.put_super	= fuse_put_super,
@@ -890,6 +893,11 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
				fc->async_dio = 1;
			if (arg->flags & FUSE_WRITEBACK_CACHE)
				fc->writeback_cache = 1;
			if (arg->time_gran && arg->time_gran <= 1000000000)
				fc->sb->s_time_gran = arg->time_gran;
			else
				fc->sb->s_time_gran = 1000000000;

		} else {
			ra_pages = fc->max_read / PAGE_CACHE_SIZE;
			fc->no_lock = 1;
@@ -996,7 +1004,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
	if (sb->s_flags & MS_MANDLOCK)
		goto err;

	sb->s_flags &= ~MS_NOSEC;
	sb->s_flags &= ~(MS_NOSEC | MS_I_VERSION);

	if (!parse_fuse_opt((char *) data, &d, is_bdev))
		goto err;
Loading