diff options
Diffstat (limited to 'fs/fuse/inode.c')
| -rw-r--r-- | fs/fuse/inode.c | 69 | 
1 files changed, 51 insertions, 18 deletions
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index a8ce6dab60a..03246cd9d47 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -123,7 +123,7 @@ static void fuse_destroy_inode(struct inode *inode)  static void fuse_evict_inode(struct inode *inode)  { -	truncate_inode_pages(&inode->i_data, 0); +	truncate_inode_pages_final(&inode->i_data);  	clear_inode(inode);  	if (inode->i_sb->s_flags & MS_ACTIVE) {  		struct fuse_conn *fc = get_fuse_conn(inode); @@ -135,6 +135,7 @@ static void fuse_evict_inode(struct inode *inode)  static int fuse_remount_fs(struct super_block *sb, int *flags, char *data)  { +	sync_filesystem(sb);  	if (*flags & MS_MANDLOCK)  		return -EINVAL; @@ -170,10 +171,13 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,  	inode->i_blocks  = attr->blocks;  	inode->i_atime.tv_sec   = attr->atime;  	inode->i_atime.tv_nsec  = attr->atimensec; -	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; +	/* mtime from server may be stale due to local buffered write */ +	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); @@ -197,6 +201,7 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,  {  	struct fuse_conn *fc = get_fuse_conn(inode);  	struct fuse_inode *fi = get_fuse_inode(inode); +	bool is_wb = fc->writeback_cache;  	loff_t oldsize;  	struct timespec old_mtime; @@ -211,10 +216,16 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,  	fuse_change_attributes_common(inode, attr, attr_valid);  	oldsize = inode->i_size; -	i_size_write(inode, attr->size); +	/* +	 * In case of writeback_cache enabled, the cached writes beyond EOF +	 * extend local i_size without keeping userspace server in sync. So, +	 * attr->size coming from server can be stale. We cannot trust it. +	 */ +	if (!is_wb || !S_ISREG(inode->i_mode)) +		i_size_write(inode, attr->size);  	spin_unlock(&fc->lock); -	if (S_ISREG(inode->i_mode)) { +	if (!is_wb && S_ISREG(inode->i_mode)) {  		bool inval = false;  		if (oldsize != attr->size) { @@ -243,6 +254,10 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)  {  	inode->i_mode = attr->mode & S_IFMT;  	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); @@ -289,7 +304,9 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid,  		return NULL;  	if ((inode->i_state & I_NEW)) { -		inode->i_flags |= S_NOATIME|S_NOCMTIME; +		inode->i_flags |= S_NOATIME; +		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;  		fuse_init_inode(inode, attr); @@ -461,6 +478,17 @@ static const match_table_t tokens = {  	{OPT_ERR,			NULL}  }; +static int fuse_match_uint(substring_t *s, unsigned int *res) +{ +	int err = -ENOMEM; +	char *buf = match_strdup(s); +	if (buf) { +		err = kstrtouint(buf, 10, res); +		kfree(buf); +	} +	return err; +} +  static int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev)  {  	char *p; @@ -471,6 +499,7 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev)  	while ((p = strsep(&opt, ",")) != NULL) {  		int token;  		int value; +		unsigned uv;  		substring_t args[MAX_OPT_ARGS];  		if (!*p)  			continue; @@ -494,18 +523,18 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev)  			break;  		case OPT_USER_ID: -			if (match_int(&args[0], &value)) +			if (fuse_match_uint(&args[0], &uv))  				return 0; -			d->user_id = make_kuid(current_user_ns(), value); +			d->user_id = make_kuid(current_user_ns(), uv);  			if (!uid_valid(d->user_id))  				return 0;  			d->user_id_present = 1;  			break;  		case OPT_GROUP_ID: -			if (match_int(&args[0], &value)) +			if (fuse_match_uint(&args[0], &uv))  				return 0; -			d->group_id = make_kgid(current_user_ns(), value); +			d->group_id = make_kgid(current_user_ns(), uv);  			if (!gid_valid(d->group_id))  				return 0;  			d->group_id_present = 1; @@ -565,7 +594,6 @@ void fuse_conn_init(struct fuse_conn *fc)  {  	memset(fc, 0, sizeof(*fc));  	spin_lock_init(&fc->lock); -	mutex_init(&fc->inst_mutex);  	init_rwsem(&fc->killsb);  	atomic_set(&fc->count, 1);  	init_waitqueue_head(&fc->waitq); @@ -596,7 +624,6 @@ void fuse_conn_put(struct fuse_conn *fc)  	if (atomic_dec_and_test(&fc->count)) {  		if (fc->destroy_req)  			fuse_request_free(fc->destroy_req); -		mutex_destroy(&fc->inst_mutex);  		fc->release(fc);  	}  } @@ -775,6 +802,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, @@ -875,6 +903,10 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)  			}  			if (arg->flags & FUSE_ASYNC_DIO)  				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 {  			ra_pages = fc->max_read / PAGE_CACHE_SIZE;  			fc->no_lock = 1; @@ -902,7 +934,8 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)  		FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES | FUSE_DONT_MASK |  		FUSE_SPLICE_WRITE | FUSE_SPLICE_MOVE | FUSE_SPLICE_READ |  		FUSE_FLOCK_LOCKS | FUSE_IOCTL_DIR | FUSE_AUTO_INVAL_DATA | -		FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO; +		FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO | +		FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT;  	req->in.h.opcode = FUSE_INIT;  	req->in.numargs = 1;  	req->in.args[0].size = sizeof(*arg); @@ -920,7 +953,7 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)  static void fuse_free_conn(struct fuse_conn *fc)  { -	kfree(fc); +	kfree_rcu(fc, rcu);  }  static int fuse_bdi_init(struct fuse_conn *fc, struct super_block *sb) @@ -980,9 +1013,9 @@ 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)) +	if (!parse_fuse_opt(data, &d, is_bdev))  		goto err;  	if (is_bdev) {  | 
