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

Commit dbfad214 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull fuse updates from Miklos Szeredi.

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse:
  fuse: use flexible array in fuse.h
  fuse: allow nanosecond granularity
  fuse: O_DIRECT support for files
  fuse: fix nlink after unlink
parents 743e89eb c628ee67
Loading
Loading
Loading
Loading
+15 −10
Original line number Diff line number Diff line
@@ -387,9 +387,6 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
	if (fc->no_create)
		return -ENOSYS;

	if (flags & O_DIRECT)
		return -EINVAL;

	forget = fuse_alloc_forget();
	if (!forget)
		return -ENOMEM;
@@ -644,13 +641,12 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
	fuse_put_request(fc, req);
	if (!err) {
		struct inode *inode = entry->d_inode;
		struct fuse_inode *fi = get_fuse_inode(inode);

		/*
		 * Set nlink to zero so the inode can be cleared, if the inode
		 * does have more links this will be discovered at the next
		 * lookup/getattr.
		 */
		clear_nlink(inode);
		spin_lock(&fc->lock);
		fi->attr_version = ++fc->attr_version;
		drop_nlink(inode);
		spin_unlock(&fc->lock);
		fuse_invalidate_attr(inode);
		fuse_invalidate_attr(dir);
		fuse_invalidate_entry_cache(entry);
@@ -762,8 +758,17 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
	   will reflect changes in the backing inode (link count,
	   etc.)
	*/
	if (!err || err == -EINTR)
	if (!err) {
		struct fuse_inode *fi = get_fuse_inode(inode);

		spin_lock(&fc->lock);
		fi->attr_version = ++fc->attr_version;
		inc_nlink(inode);
		spin_unlock(&fc->lock);
		fuse_invalidate_attr(inode);
	} else if (err == -EINTR) {
		fuse_invalidate_attr(inode);
	}
	return err;
}

+112 −17
Original line number Diff line number Diff line
@@ -194,10 +194,6 @@ int fuse_open_common(struct inode *inode, struct file *file, bool isdir)
	struct fuse_conn *fc = get_fuse_conn(inode);
	int err;

	/* VFS checks this, but only _after_ ->open() */
	if (file->f_flags & O_DIRECT)
		return -EINVAL;

	err = generic_file_open(inode, file);
	if (err)
		return err;
@@ -932,17 +928,23 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
	struct file *file = iocb->ki_filp;
	struct address_space *mapping = file->f_mapping;
	size_t count = 0;
	size_t ocount = 0;
	ssize_t written = 0;
	ssize_t written_buffered = 0;
	struct inode *inode = mapping->host;
	ssize_t err;
	struct iov_iter i;
	loff_t endbyte = 0;

	WARN_ON(iocb->ki_pos != pos);

	err = generic_segment_checks(iov, &nr_segs, &count, VERIFY_READ);
	ocount = 0;
	err = generic_segment_checks(iov, &nr_segs, &ocount, VERIFY_READ);
	if (err)
		return err;

	count = ocount;

	mutex_lock(&inode->i_mutex);
	vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);

@@ -962,11 +964,41 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,

	file_update_time(file);

	if (file->f_flags & O_DIRECT) {
		written = generic_file_direct_write(iocb, iov, &nr_segs,
						    pos, &iocb->ki_pos,
						    count, ocount);
		if (written < 0 || written == count)
			goto out;

		pos += written;
		count -= written;

		iov_iter_init(&i, iov, nr_segs, count, written);
		written_buffered = fuse_perform_write(file, mapping, &i, pos);
		if (written_buffered < 0) {
			err = written_buffered;
			goto out;
		}
		endbyte = pos + written_buffered - 1;

		err = filemap_write_and_wait_range(file->f_mapping, pos,
						   endbyte);
		if (err)
			goto out;

		invalidate_mapping_pages(file->f_mapping,
					 pos >> PAGE_CACHE_SHIFT,
					 endbyte >> PAGE_CACHE_SHIFT);

		written += written_buffered;
		iocb->ki_pos = pos + written_buffered;
	} else {
		iov_iter_init(&i, iov, nr_segs, count, 0);
		written = fuse_perform_write(file, mapping, &i, pos);
		if (written >= 0)
			iocb->ki_pos = pos + written;

	}
out:
	current->backing_dev_info = NULL;
	mutex_unlock(&inode->i_mutex);
@@ -1101,30 +1133,41 @@ static ssize_t fuse_direct_read(struct file *file, char __user *buf,
	return res;
}

static ssize_t fuse_direct_write(struct file *file, const char __user *buf,
static ssize_t __fuse_direct_write(struct file *file, const char __user *buf,
				   size_t count, loff_t *ppos)
{
	struct inode *inode = file->f_path.dentry->d_inode;
	ssize_t res;

	if (is_bad_inode(inode))
		return -EIO;

	/* Don't allow parallel writes to the same file */
	mutex_lock(&inode->i_mutex);
	res = generic_write_checks(file, ppos, &count, 0);
	if (!res) {
		res = fuse_direct_io(file, buf, count, ppos, 1);
		if (res > 0)
			fuse_write_update_size(inode, *ppos);
	}
	mutex_unlock(&inode->i_mutex);

	fuse_invalidate_attr(inode);

	return res;
}

static ssize_t fuse_direct_write(struct file *file, const char __user *buf,
				 size_t count, loff_t *ppos)
{
	struct inode *inode = file->f_path.dentry->d_inode;
	ssize_t res;

	if (is_bad_inode(inode))
		return -EIO;

	/* Don't allow parallel writes to the same file */
	mutex_lock(&inode->i_mutex);
	res = __fuse_direct_write(file, buf, count, ppos);
	mutex_unlock(&inode->i_mutex);

	return res;
}

static void fuse_writepage_free(struct fuse_conn *fc, struct fuse_req *req)
{
	__free_page(req->pages[0]);
@@ -2077,6 +2120,57 @@ int fuse_notify_poll_wakeup(struct fuse_conn *fc,
	return 0;
}

static ssize_t fuse_loop_dio(struct file *filp, const struct iovec *iov,
			     unsigned long nr_segs, loff_t *ppos, int rw)
{
	const struct iovec *vector = iov;
	ssize_t ret = 0;

	while (nr_segs > 0) {
		void __user *base;
		size_t len;
		ssize_t nr;

		base = vector->iov_base;
		len = vector->iov_len;
		vector++;
		nr_segs--;

		if (rw == WRITE)
			nr = __fuse_direct_write(filp, base, len, ppos);
		else
			nr = fuse_direct_read(filp, base, len, ppos);

		if (nr < 0) {
			if (!ret)
				ret = nr;
			break;
		}
		ret += nr;
		if (nr != len)
			break;
	}

	return ret;
}


static ssize_t
fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
			loff_t offset, unsigned long nr_segs)
{
	ssize_t ret = 0;
	struct file *file = NULL;
	loff_t pos = 0;

	file = iocb->ki_filp;
	pos = offset;

	ret = fuse_loop_dio(file, iov, nr_segs, &pos, rw);

	return ret;
}

static const struct file_operations fuse_file_operations = {
	.llseek		= fuse_file_llseek,
	.read		= do_sync_read,
@@ -2120,6 +2214,7 @@ static const struct address_space_operations fuse_file_aops = {
	.readpages	= fuse_readpages,
	.set_page_dirty	= __set_page_dirty_nobuffers,
	.bmap		= fuse_bmap,
	.direct_IO	= fuse_direct_IO,
};

void fuse_init_file_inode(struct inode *inode)
+1 −0
Original line number Diff line number Diff line
@@ -947,6 +947,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
	sb->s_magic = FUSE_SUPER_MAGIC;
	sb->s_op = &fuse_super_operations;
	sb->s_maxbytes = MAX_LFS_FILESIZE;
	sb->s_time_gran = 1;
	sb->s_export_op = &fuse_export_operations;

	file = fget(d.fd);
+1 −1
Original line number Diff line number Diff line
@@ -593,7 +593,7 @@ struct fuse_dirent {
	__u64	off;
	__u32	namelen;
	__u32	type;
	char name[0];
	char name[];
};

#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)